温馨提示:本文要求对 scrapy 有一定基础认识
在原 scrapy 中,爬取的页面是文本,也就是单纯的文字。而对于动态网站而言,需要执行一些 javascript 脚本,才能加载出真正的页面,比如网易云音乐,而想要爬取这些网站通常需要借助一些可以执行 javascript 脚本的中间件来完成,本文使用的是 Chrome ,换成其他也无压力
scrapy + selenium + headless
selenium 似乎是被用来做自动化测试的 Python 库
headless 是 Chrome 的没有 UI 的浏览器,用来提高爬取速度去掉 UI,不需要额外安装只需安装较高版本的 Chrome 就可以使用 headlessle, 但是需要额外安装驱动,即 chromedriver ,没有安装 chromedriver 会报错,如何安装 chromedriver 请阅读其他博客。
爬取动态网站时,需要开启中间件,官方文档是这样激活中间件的,在 settings.py 中找到
# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
# DOWNLOADER_MIDDLEWARES = {
# #'yourprojectname.middlewares.yourprojectnameDownloaderMiddleware': 543,
#}
然后去掉注释
# Enable or disable downloader middlewares
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
'yourprojectname.middlewares.yourprojectnameDownloaderMiddleware': 543,
}
最开始我是在中间件中实例化 chrome 导致,爬虫开启一次就实例化一次,如果 browser 用完就关闭,使用时实例化,爬取的速度很慢,每一个 request 请求都会调用 process_request
方法,导致操作系统频繁打开关闭 chrome,而不这么做,久而久之内存泄漏导致电脑卡死。为了解决完美解决这一点,实例化 chrome headless 时,放在 spider 中,具体做法如下:
进入 middlewares.py 定义自己的下载中间件
.
.
.
class BrowserDownloaderMiddleware(object):
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
pass
def process_request(self, request, spider):
# 如果爬虫的名字为music则请求在这里处理
if spider.name == 'spider_name':
browser = spider.browser
browser.get(request.url) # 打开这个页面
body = browser.page_source.encode('utf8')
return HtmlResponse(url=request.url, body=body) # 将下载好的页面返回出去
else:
return None
def process_response(self, request, response, spider):
return response
.
.
.
进入 /spiders/yourspider.py 定义自己的下载中间件
官方文档中给出:close
方法,在爬虫关闭时掉用,而 __init__
方法在类实例化时自动调用,不知道为何 __del__
在 scrapy 中没有调用。
.
.
.
def __init__(self, *args, **kwargs):
super(YourSpider, self).__init__(*args, **kwargs)
options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--disable-gpu")
self.browser = webdriver.Chrome(chrome_options=options)
pass
def close(self, reason):
self.browser.close()
pass
.
.
.
这样一来,通过 spider 对象将 browser 携带给中间件,完美解决了内存泄漏问题,同时爬取的速度也很快。