当前位置:嗨网首页>书籍在线阅读

21-使用爬虫实现双向爬取

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

3.4.1 使用爬虫实现双向爬取

你可能已经注意到了前面例子中的 `yield` 语句。 `yield` 与 `return` 在某种意义上来说有些相似,都是将返回值提供给调用者。不过,和 `return` 不同的是, `yield` 不会退出函数,而是继续执行 `for` 循环。从功能上来说,前面的例子与下面的代码大体相当: `yield` 是Python“魔法”的一部分,它可以使日常的高效编程工作更加轻松。

我们将之前的爬虫拷贝到一个新文件中,并命名为 manual.py

$ ls
properties scrapy.cfg
$ cp properties/spiders/basic.py properties/spiders/manual.py
next_requests = []
for url in...
  next_requests.append(Request(...))
for url in...
  next_requests.append(Request(...))
return next_requests

properties/spiders/manual.py 文件中,通过添加 from scrapy.http import Request 语句引入 Request 模块,将爬虫的 name 参数改为 'manual' ,修改 start_urls 以使用第一个索引页,并将 parse() 方法重命名为 parse_item() 。好了!现在开始编写一个新的 parse() 方法,来实现水平和垂直两种抓取方式。

def parse(self, response):
  # Get the next index URLs and yield Requests
  next_selector = response.xpath('//­*[contains(@class,'
                  '"next")]//@href')
  for url in next_selector.extract():
    yield Request(urlparse.urljoin(response.url, url))
  # Get item URLs and yield Requests
  item_selector = response.xpath('//­*[@itemprop="url"]/@href')
  for url in item_selector.extract():
    yield Request(urlparse.urljoin(response.url, url),
           callback=self.parse_item)

我们现在已经准备好运行该爬虫了。不过如果让该爬虫以当前的方式运行的话,则会抓取网站完整的5万个页面。为了避免运行时间过长,可以通过命令行参数: -s CLOSESPIDER_ITEMCOUNT=90 ,告知爬虫在爬取指定数量(如90个)的Item后停止运行(更多细节参见第7章)。现在,我们可以运行了。

$ scrapy crawl manual -s CLOSESPIDER_ITEMCOUNT=90
INFO: Scrapy 1.0.3 started (bot: properties)
...
DEBUG: Crawled (200) <...index_00000.html> (referer: None)
DEBUG: Crawled (200) <...property_000029.html> (referer: ...index_00000.
html)
DEBUG: Scraped from <200 ...property_000029.html>
 {'address': [u'Clapham, London'],
 'date': [datetime.datetime(2015, 10, 4, 21, 25, 22, 801098)],
 'description': [u'situated camden facilities corner'],
 'image_urls': [u'http://web:9312/images/i10.jpg'],
 'price': [223.88],
 'project': ['properties'],
 'server': ['scrapyserver1'],
 'spider': ['manual'],
 'title': [u'Portered Mile'],
 'url': ['http://.../property_000029.html']}
DEBUG: Crawled (200) <...property_000028.html> (referer: ...index_00000.
html)
...
DEBUG: Crawled (200) <...index_00001.html> (referer: ...)
DEBUG: Crawled (200) <...property_000059.html> (referer: ...)
...
INFO: Dumping Scrapy stats: ...
  'downloader/request_count': 94, ...
  'item_scraped_count': 90,

如果仔细查看前面的输出,就会发现我们同时获得了水平抓取和垂直抓取的结果。第一个 index_00000.html 读取后,派生出了许多请求。当它们执行时,调试信息通过 referer URL指出是谁发起的请求。比如,可以看到, property_000029.htmlproperty_000028.html……index_00001.html 都有相同的 referer(index_00000.html) 。而 property_000059.html 及其他请求则是以 index_00001.htmlreferer 的,并且该过程还在持续。

从该示例中还可以观察到,Scrapy在处理请求时使用的是 后入先出(LIFO) 策略(即深度优先爬取)。用户提交的最后一个请求会被首先处理。在大多数情况下,这种默认的方式非常方便。比如,我们想要在移动到下一个索引页之前处理每一个房源页时。否则,我们将会填充一个包含待爬取房源页URL的巨大队列,无谓地消耗内存。另外,在许多情况中,你可能需要辅助的请求来完成单个请求,我们将会在后面的章节中遇到这种情况。你需要这些辅助的请求能够尽快完成,以腾出资源,并且让被抓取的Item能够稳定流动。

我们可以通过设置 Request() 的优先级参数修改默认顺序,大于0表示高于默认的优先级,小于0表示低于默认的优先级。通常来说,Scrapy的调度器会首先执行高优先级的请求,不过不要花费太多时间来考虑具体的哪个请求应该被首先执行。很可能在你的应用中,不会使用超过1个或2个请求优先级。此外还需要注意的是,URL还会被执行去重操作,这在大部分时候也是我们想要的功能。不过如果我们需要多次执行同一个URL的请求,可以设置 dont_filter_Request() 参数为 true