首先需要对目标网站进行分析,具体的分析这里不详细介绍。目标网站;豆瓣某个电影评论页面 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解析。
未经允许不得转载:作者:鳄鱼君,
转载或复制请以 超链接形式 并注明出处 鳄鱼君。