Scrapy+Selenium爬取豆瓣电影评论

鳄鱼君

发表文章数:615

热门标签

, , , ,

Vieu四代商业主题

高扩展、安全、稳定、响应式布局多功能模板。

¥69 现在购买
首页 » Python » Scrapy+Selenium爬取豆瓣电影评论

首先需要对目标网站进行分析,具体的分析这里不详细介绍。目标网站;豆瓣某个电影评论页面 https://movie.douban.com/subject/1292052/reviews,这个电影是肖申克的救赎。网页没有使用什么特殊的加载方式,所有的评论数据都在当前的源码中。翻页:

https://movie.douban.com/subject/1292052/reviews?start=0
https://movie.douban.com/subject/1292052/reviews?start=20
https://movie.douban.com/subject/1292052/reviews?start=40

以此类推,修改start的值就可以实现翻页,数字为电影的ID,如果想要抓取不同的电影评论,只需要替换掉电影ID即可。

1.settings.py配置文件

import os

BOT_NAME = 'douban'
SPIDER_MODULES = ['douban.spiders']
NEWSPIDER_MODULE = 'douban.spiders'
ROBOTSTXT_OBEY = False

DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
  'User-Agent': 'User-AgentMozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0'
}
# 注册自定义中间件SeleniumMiddleware
DOWNLOADER_MIDDLEWARES = {
   'douban.middlewares.SeleniumMiddleware': 543,
}
LOG_LEVEL='ERROR'
# 注册管道
ITEM_PIPELINES = {
   'douban.pipelines.DoubanPipeline': 300,
}
# 设置Selenium超时时间
SELENIUM_TIMUOUT = 30
# 设置为selenim抓取
USE_SELENIUM = True

# 设置配置文件conf.ini路径信息
BASE_DIR = os.path.dirname(os.path.realpath(__file__))
CONF = os.path.join(BASE_DIR, 'conf.ini')
# 数据库连接信息
MYSQL_CONNECTION = 'mysql+pymysql://root:123@localhost/spider?charsetutf8mb4'

配置settings.py主要用于修改功能配置文件,conf.ini文件在settings.py同级目录下

数据库连接编码使用utf8mb4是为了保证数据能完全录入数据库,因为电影评论里可能出现某些特殊字符

2.items.py文件
将需要存储的字段定义在items.py中:

import scrapy

class DoubanItem(scrapy.Item):
    # 电影ID
    movieId = scrapy.Field()
    # 电影评论内容
    comment = scrapy.Field()

3.pipelines.py文件
在管道里面定义数据存储的代码。这里使用SQLAlchemy框架,将数据存储到MySQL数据库中。

from sqlalchemy import *
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# 导入setting配置信息
from scrapy.utils.project import get_project_settings
settings = get_project_settings()

# 定义映射类
Base = declarative_base()
class scrapy_db(Base):
    __tablename__ = 'douban_db'
    id = Column(Integer(), primary_key=True)
    movieId = Column(Integer)
    comment = Column(String(2000))

class DoubanPipeline:
    def __init__(self):
        # 初始化连接数据库
        connection = settings['MYSQL_CONNECTION']
        engine = create_engine(connection, echo=False, pool_size=2000)
        DBSession = sessionmaker(bind=engine)
        self.SQLsession = DBSession()
        # 创建数据库
        Base.metadata.create_all(engine)

    def process_item(self, item, spider):
        # 入库处理
        self.SQLsession.execute(scrapy_db.__table__.insert(), {'comment': item['comment'], 'movieId': item['movieId']})
        self.SQLsession.commit()
        return item

4.middlewares.py文件
删掉原来scrapy默认的中间件,定义SeleniumMiddleware中间件,将selenium处理后的数据作为response返回:

# 自定义中间件SeleniumMiddleware
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from scrapy.http import HtmlResponse


class SeleniumMiddleware(object):
    def __init__(self, timeout=None):
        self.timeout = timeout
        self.options = Options()
        self.options.binary_location = r'D:\Chrome\chrome.exe'
        self.options.add_argument('--headless')
        self.driver = webdriver.Chrome(options=self.options)
        self.wait = WebDriverWait(self.driver, self.timeout)

    def process_item(self, request, spider):
        try:
            self.driver.get(request.url)
            return HtmlResponse(url=request.url,
                                body=self.driver.page_source,
                                request=request,
                                encoding='utf-8',
                                status=200)
        except Exception as e:
            return HtmlResponse(url=request.url, status=502, request=request)

    def __del__(self):
        self.driver.close()

    @classmethod
    def from_crawler(cls, crawler):
        # 读取settings.py的SELENIUM_TIMEOUT
        return cls(timeout=crawler.settings.get('SELENIUM_TIMEOUT'))

5.spider.py文件
电影评论页的URL地址带有电影ID,只要切换不同的ID就能爬取不同电影的评论。我们将电影ID写入配置文件conf.ini,再由spider程序读取并实现不同电影的评论爬取

import scrapy
import configparser
from scrapy.selector import Selector
from douban.items import DoubanItem


class MovieSpider(scrapy.Spider):
    name = 'movie'
    allowed_domains = ['douban.com']
    start_urls = 'https://movie.douban.com/subject/%s/reviews?start=%s'

    # 重写start_requests方法
    def start_requests(self):
        # 读取配置文件,获取电影ID
        conf = configparser.ConfigParser()
        urls_list = []
        # 获取conf.ini的路径
        conf.read(self.settings.get('CONF'))
        tem = conf['config']
        if 'movieId' in tem.keys():
            urls_list = conf['config']['movieId'].split(',')
        for i in urls_list:
            # 爬取电影评论
            for page in range(100):
                url = self.start_urls % (str(i), str(page*20))
                yield scrapy.Request(url=url, meta={'movieId': str(i)}, callback=self.parse)
                
    def parse(self, response):
        # 将响应内容内容生成Selector对象,用于数据清洗
        se = Selector(response)
        item = DoubanItem()
        comment = se.xpath('//div[contains(@class,"main review-item")]')
        for i in comment:
            item['movieId'] = response.meta['movieId']
            content = i.xpath('./div/div/div//text()').extract()
            item['comment'] = ''.join([q.strip() for q in content if len(q.strip()) > 0]).replace('\n', '')
            yield item

conf.ini配置文件主要存放电影ID,不同电影ID之间需要用逗号隔开

[config] 
movieId=1292052,1292052

由于豆瓣的影评使用了JS,代码只能抓取部分影评信息,如果想要抓取全部信息就需要使用Splash解析。

未经允许不得转载:作者:鳄鱼君, 转载或复制请以 超链接形式 并注明出处 鳄鱼君
原文地址:《Scrapy+Selenium爬取豆瓣电影评论》 发布于2020-07-10

分享到:
赞(0) 赏杯咖啡

评论 1

8 + 1 =
  1. #1

    看样子挺有意思的,有时间我也去试试!

    Teacher Du1个月前 (07-11)回复

文章对你有帮助可赏作者一杯咖啡

支付宝扫一扫打赏

微信扫一扫打赏

Vieu4.6主题
专业打造轻量级个人企业风格博客主题!专注于前端开发,全站响应式布局自适应模板。
切换注册

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

Q Q 登 录
微 博 登 录
切换登录

注册