关于粗略解决scrapy断点续爬的问题

** 任何程序在运行的过程中都有可能会崩溃,爬虫也不例外。**

当我们需要爬取的数据量很大很大的时候,爬取的过程中难免会出现各种各样的问题导致程序崩溃断掉,这个时候我们就需要记录爬虫的状态,当爬虫挂掉的时候可以恢复原来的状态继续跑。

scrapy简单易用,效率极高,自带多线程机制。但是也正因为它的多线程机制导致在用scrapy写爬虫的时候处理断点续爬很恼火。当你用for循环遍历一个网站的所有页面的时候,例如:

for i in range(totalpages):
    url = '***page=' + str(i)
    yield Request(url=url,callback=self.next)

如果这个网站有10000页,启动scrapy后程序第一个发送的请求可能就是第10000页,然后第二个请求可能就是第1页。当程序跑到一半断掉的时候天知道有哪些页是爬过的。不过针对这个问题也不是没有解决办法。

总之我们要想保证爬取数据的完整就要牺牲程序的效率。

  • 有的人把所有爬取过的url列表保存到一个文件当中,然后再次启动的时候每次爬取要和文件当中的url列表对比,如果相同则不再爬取。
  • 有的人在scrapy再次启动爬取的时候和数据库里面的数据做对比,如果相同则不存取。
  • 还有一种办法呢就是利用Request中的优先级(priority)

前两种方法就不详细说了,通俗易懂。主要说一下这个优先级。
修改上面的代码:

for i in range(totalpages):
    url = '***page=' + str(i)
    priority = totalpages-i
    yield Request(url=url,priority=priority,callback=self.next)

当Request中加入了priority属性的时候,每次scrapy从请求队列中取请求的时候就会判断优先级,先取出优先级高的去访问。由于scrapy默认启动十个线程。这时优先级为100 的就会在优先级为90之前被取出请求队列,这样呢我们就能大体上保证爬取网页的顺序性

保证了顺序性之后呢,我们就要记录已经爬取的页数。由于发送请求,下载页面,存取数据这几个动作是顺序执行的,也就是说程序发送了这个请求不代表此时已经爬取到这一页了,只有当收到response的时候我们才能确定我们已经获取到了数据,这时我们才能记录爬取位置。完整的代码应该是这样的:

f = open('filename','r+') #程序开始我们要读取上次程序断掉时候的爬取位置
page = int(f.read())-20   #这里减20下面详细解释一下 
f.close()

***
***

def first(self, response):
    for i in range(self.page, totalpages): # 这里循环要从上次断掉的page开始
        url = '***page=' + str(i)
        priority = totalpages-i            # 优先级要从高到低
        yield Request(url=url,
                      meta={               # 这里把page传到下一个函数
                          'page':i 
                      }
                      priority=priority,   # 这里设置优先级
                      callback=self.next)  # 回调下一个函数

def next(self, response):
    f = open('filename', 'wb')             # 此时请求成功把当前page保存
    f.write(response.meta["page"])
    f.close()
***
***

这里说一下为什么要减20: 虽然请求是按优先级高低顺序发送的,但是也避免不了小范围的乱序。一方面是因为scrapy多线程,另一方面也和爬取网站的服务器响应速度有关,这个值也要视情况而定。所以减20是尽最大可能保证数据的完整性。


关于粗略解决scrapy断点续爬的问题_第1张图片
图片发自App

你可能感兴趣的:(关于粗略解决scrapy断点续爬的问题)