deltafetch,让爬虫有记忆
”我化作人鱼,只有七秒钟的记忆“。
很多时候,爬虫程序跑着跑着,因为网络故障或者程序异常就宕掉了。无奈之下只能重启重新爬取。为了避免这种每次重头再来的情况,我们都会利用mysql、redis、文本等方式,来记录一下爬取过的url。
这也提高了程序整体的复杂度。而scrapy提供了一个模块来解决了这个痛点,仅仅两行配置就解决了这个问题。
在Scrapy系列的第一篇,我就写了一个爬虫常见断点续爬问题。
假如有1000个页面需要爬取,爬到第999个页面,进度条马上满格的时候,程序咯噔一下挂了,就差一个,但是还是没爬完啊,咋整?我选择重新启动程序,那么你说我怎么样才能直接从第999个开始爬取呢?
这里先讲讲我写的第一个爬虫:爬取10+个地市的poi信息。
17年实习,第一次开发爬虫,也不知道有高德poi接口啥的,于是就找了个网站来爬取poi信息。当时那个网站估计还在起步阶段,服务器带宽应该不高,访问速度是真的慢,而且动不动维护停站,所以我的程序也得跟着停止。如果每次启动都重新爬取,估计几年也爬不完,于是我想了个办法。
我先将所有地市下所有区县数据的条数(网站上有)先手动录入到数据库表中,每次重新启动爬虫程序的时候,先统计结果数据表中各个区县已经爬取的条数,与总条数进行对比。如果小于的话,说明还没有爬取完,然后通过某区县已爬取条数 / 网站每页展示条数计算出我已经爬取到此区县的页数,再通过余数定位到我爬到了此页面的第几个。通过这种方法,最后无丢失爬取了163w条数据。
换种思路,将爬取的url放到表中,重启程序开始爬取url的时候,先去判断url是否存在于数据表中,如果存在就不进行爬取,这样也能实现断点续爬。也是沿用了原始的url的去重的思路。
而今天,要讲的scrapy-deltafetch,完全不用考虑上面的这些问题!
上面的两种思路有两个共同点:
这样就加大了开发的工作量。所以我们这里就引入了scrapy-deltafecth模块,两行配置就可以完美解决上面的两个问题。
deltch在Scrapy中是作为一个Spider中间件存在的。原理就是内置一个内嵌式KV数据库BerkeleyDB,在执行yield item的时候,将response.request加密作为key存储到内嵌式数据库中。
这样,每次爬取的时候,都会去内嵌数据库中判断这个url是否已存在,存在就不再爬取。
这时候就有人要说了,这不还是用到了数据库吗?
内嵌式数据库和数据库是有区别的:
简言之,就是内嵌式数据库不会像MySQL这种数据库一样,有属于自己的后台服务,自己的执行引擎。
deltafetch在Windows和Linux环境下的安装方式不一样的,Linux下的安装比较复杂。
deltafech模块依赖于bsddb3模块,bsddb3又需要依赖BerkeleyDB。
Win下的安装比较简单,不需要单独安装BerkeleyDB。直接使用pip安装bsddb3,如果安装失败,就去下载whl安装版单独安装。然后再安装scrapy-deltafetch即可。
具体方法就不做阐述了,主要讲讲Llinux下的安装。
Linux下的安装比较麻烦一点,需要自己去oracle官网下载Berkeley DB,然后进行编译安装。
亲测,不要下载Berkeley DD的v18版本,我使用的是6.2.23版本。
# ,18版本不行,18.1.40会安装失败,18低版本在安装bsddb3时会报错
cd build_unix
../dist/configure --prefix=/usr/local/berkeleyDb
make & make install
export BERKELEYDB_DIR=/usr/local/berkeleyDb
export YES_I_HAVE_THE_RIGHT_TO_USE_THIS_BERKELEY_DB_VERSION=yes
pip3 install bsddb3
pip3 install scrapy-deltafetch
在settings.py中,添加deltafetch中间件并激活。
SPIDER_MIDDLEWARES = {
'scrapy_deltafetch.DeltaFetch': 100
}
# 开启
DELTAFETCH_ENABLED = True
这里要注意的点是,deltafetch是一个spider中间件,根据架构图,spider只有yield item到pipeline的时候才会触发,所以只有yield item才能让deltafetch生效。
当我们第一次启动爬虫的时候,它会从头开始爬取,如果再次重启,那么爬取过的url则不会再重新爬取。那么我们需要在启动时添加参数,告诉爬虫我要重新爬取。
scrapy crawl name -a deltafetch_reset=1
如何判断deltafetch生效了呢
在程序的根目录下的隐藏目录.scrapy中,找到deltafetch目录,里面会根据crawler_name生成db文件,这个就是berlekeyDB的数据库文件,里面记录着已经爬取过的url信息。
之前提到的的deltafetch_reset=1参数,就是清空对应的db文件。
先不加参数启动爬虫,启动了两次程序,相同的url都进行了数据爬取。
配置了deltefetch之后。第二次启动时,就会提示忽略已经爬取的url,不再进行爬取。
如果想要重新爬取之前爬取过的url,启动前添加deltafetch_reset=1参数即可。
DeltaFetch类为核心代码。
def process_spider_output(self, response, result, spider):
for r in result:
if isinstance(r, Request):
key = self._get_key(r)
if key in self.db:
logger.info("Ignoring already visited: %s" % r)
if self.stats:
self.stats.inc_value('deltafetch/skipped', spider=spider)
continue
elif isinstance(r, (BaseItem, dict)):
key = self._get_key(response.request)
self.db[key] = str(time.time())
if self.stats:
self.stats.inc_value('deltafetch/stored', spider=spider)
yield r
个人觉得deltafetch安装是比较容易遇到问题的,还是要多查阅一些资料。下一篇就是scrapy-splash,一个网页渲染的插件,可以替换selenium。