Spider基本上能做很多事情了,但是如果你想爬取全站的话,可能需要一个更强大的武器。CrawlSpider基于Spider,但是可以说是为全站爬取而生。CrawlSpiders是Spider的派生类,Spider类的设计原则是只爬取start_url列表中的网页,而CrawlSpider类定义了一些规则(rule)来提供跟进link的方便的机制,从爬取的网页中获取link并继续爬取的工作更适合。

一、CrawlSpiders源码解析

"""
This modules implements the CrawlSpider which is the recommended spider to use
for scraping typical web sites that requires crawling pages. See documentation in docs/topics/spiders.rst
""" import copy
import six from scrapy.http import Request, HtmlResponse
from scrapy.utils.spider import iterate_spider_output
from scrapy.spiders import Spider def identity(x):
return x class Rule(object): def __init__(self, link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=identity):
self.link_extractor = link_extractor
self.callback = callback
self.cb_kwargs = cb_kwargs or {}
self.process_links = process_links
self.process_request = process_request
if follow is None:
self.follow = False if callback else True
else:
self.follow = follow class CrawlSpider(Spider): rules = () def __init__(self, *a, **kw):
super(CrawlSpider, self).__init__(*a, **kw)
self._compile_rules() # 首先调用parser()来处理start_urls中返回的response对象
# parser()则将这些response对象传递给_parse_response()函数来处理,并设置回调函数为parse_start_url()
# 设置了跟进标志位True
# parser将返回item和跟进了的Request对象
def parse(self, response):
return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True) # 处理start_url中返回的response,需要重写
def parse_start_url(self, response):
# print(response)
return [] def process_results(self, response, results):
return results # 从response中抽取符合任一用户定义‘规则的连接’,并构造成response对象返回
def _build_request(self, rule, link):
r = Request(url=link.url, callback=self._response_downloaded)
r.meta.update(rule=rule, link_text=link.text)
return r def _requests_to_follow(self, response):
# 注释掉的源码
# if not isinstance(response, HtmlResponse):
# print(isinstance(response, HtmlResponse))
# print(response, type(response))
# print('------')
# return
seen = set()
# 抽取之内的所有连接,只要通过任意一个‘规则’,即表示合法
for n, rule in enumerate(self._rules):
links = [lnk for lnk in rule.link_extractor.extract_links(response)
if lnk not in seen]
# 使用用户指定的process_links处理每个连接
# print('links', links)
if links and rule.process_links:
links = rule.process_links(links)
# 将链接加入seen集合,为每个连接生成Response对象,并设置回调函数为_response_downloaded()
for link in links:
seen.add(link)
# 构造Response对象,并将Rule规则中定义的回调函数作为这个Resquest对象的回调函数
r = self._build_request(n, link)
# 对每个Request调用process_request()函数,该函数默认为indentify,即不做任何处理,直接返回该Request
yield rule.process_request(r) # 处理通过rule提取出的连接,并返回item以及request
def _response_downloaded(self, response):
# print(response)
rule = self._rules[response.meta['rule']]
return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow) # 解析response对象,会用callback解析处理他,并返回request或Item对象
def _parse_response(self, response, callback, cb_kwargs, follow=True):
# 首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是parser_start_url函数)
# 如果设置了回调函数(parser_start_url)_),那么首先用parser_start_url()处理response对象
# 然后再交给process_resules处理,返回cb_res的一个列表
if callback:
# 如果是parse调用的,则会解析成Request对象
# 如果是rule callback,则会解析成Item
cb_res = callback(response, **cb_kwargs) or ()
cb_res = self.process_results(response, cb_res)
for requests_or_item in iterate_spider_output(cb_res):
yield requests_or_item # 如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象
if follow and self._follow_links:
# 返回每个Request对象
for request_or_item in self._requests_to_follow(response):
yield request_or_item def _compile_rules(self):
def get_method(method):
if callable(method):
return method
elif isinstance(method, six.string_types):
return getattr(self, method, None) self._rules = [copy.copy(r) for r in self.rules]
for rule in self._rules:
rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request) @classmethod
def from_crawler(cls, crawler, *args, **kwargs):
spider = super(CrawlSpider, cls).from_crawler(crawler, *args, **kwargs)
spider._follow_links = crawler.settings.getbool(
'CRAWLSPIDER_FOLLOW_LINKS', True)
return spider def set_crawler(self, crawler):
super(CrawlSpider, self).set_crawler(crawler)
self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)
class CrawlSpider(Spider):
rules = ()
def __init__(self, *a, **kw):
super(CrawlSpider, self).__init__(*a, **kw)
self._compile_rules() # 首先调用parse()来处理start_urls中返回的response对象
# parse()则将这些response对象传递给了_parse_response()函数处理,并设置回调函数为parse_start_url()
# 设置了跟进标志位True
# parse将返回item和跟进了的Request对象
def parse(self, response):
return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True) # 处理start_url中返回的response,需要重写
def parse_start_url(self, response):
return [] def process_results(self, response, results):
return results # 从response中抽取符合任一用户定义'规则'的链接,并构造成Resquest对象返回
def _requests_to_follow(self, response):
if not isinstance(response, HtmlResponse):
return
seen = set()
# 抽取之内的所有链接,只要通过任意一个'规则',即表示合法
for n, rule in enumerate(self._rules):
links = [l for l in rule.link_extractor.extract_links(response) if l not in seen]
# 使用用户指定的process_links处理每个连接
if links and rule.process_links:
links = rule.process_links(links)
# 将链接加入seen集合,为每个链接生成Request对象,并设置回调函数为_repsonse_downloaded()
for link in links:
seen.add(link)
# 构造Request对象,并将Rule规则中定义的回调函数作为这个Request对象的回调函数
r = Request(url=link.url, callback=self._response_downloaded)
r.meta.update(rule=n, link_text=link.text)
# 对每个Request调用process_request()函数。该函数默认为indentify,即不做任何处理,直接返回该Request.
yield rule.process_request(r) # 处理通过rule提取出的连接,并返回item以及request
def _response_downloaded(self, response):
rule = self._rules[response.meta['rule']]
return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow) # 解析response对象,会用callback解析处理他,并返回request或Item对象
def _parse_response(self, response, callback, cb_kwargs, follow=True):
# 首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是 parse_start_url函数)
# 如果设置了回调函数(parse_start_url()),那么首先用parse_start_url()处理response对象,
# 然后再交给process_results处理。返回cb_res的一个列表
if callback:
#如果是parse调用的,则会解析成Request对象
#如果是rule callback,则会解析成Item
cb_res = callback(response, **cb_kwargs) or ()
cb_res = self.process_results(response, cb_res)
for requests_or_item in iterate_spider_output(cb_res):
yield requests_or_item # 如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象
if follow and self._follow_links:
#返回每个Request对象
for request_or_item in self._requests_to_follow(response):
yield request_or_item def _compile_rules(self):
def get_method(method):
if callable(method):
return method
elif isinstance(method, basestring):
return getattr(self, method, None) self._rules = [copy.copy(r) for r in self.rules]
for rule in self._rules:
rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request) def set_crawler(self, crawler):
super(CrawlSpider, self).set_crawler(crawler)
self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)
二、 CrawlSpider爬虫文件字段的介绍

1、 CrawlSpider继承于Spider类,除了继承过来的属性外(name、allow_domains),还提供了新的属性和方法:class scrapy.linkextractors.LinkExtractorLink Extractors 的目的很简单: 提取链接。每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个 scrapy.link.Link 对象。

Link Extractors要实例化一次,并且 extract_links 方法会根据不同的 response 调用多次提取链接。

class scrapy.linkextractors.LinkExtractor(
allow = (),
deny = (),
allow_domains = (),
deny_domains = (),
deny_extensions = None,
restrict_xpaths = (),
tags = ('a','area'),
attrs = ('href'),
canonicalize = True,
unique = True,
process_value = None
)

主要参数:

① allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
② deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。
③ allow_domains:会被提取的链接的domains。
④ deny_domains:一定不会被提取链接的domains。
⑤ restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。

2、 在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了特定操作。如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。

class scrapy.spiders.Rule(
link_extractor,
callback = None,
cb_kwargs = None,
follow = None,
process_links = None,
process_request = None
)

① link_extractor:是一个Link Extractor对象,用于定义需要提取的链接。

② callback: 从link_extractor中每获取到链接时,参数所指定的值作为回调函数,该回调函数接受一个response作为其第一个参数。

注意:当编写爬虫规则时,避免使用parse作为回调函数。由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败。

③ follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为None,follow 默认设置为True ,否则默认为False。

④ process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。

⑤ process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会调用该函数。 (用来过滤request)

3、Scrapy提供了log功能,可以通过 logging 模块使用。可以修改配置文件settings.py,任意位置添加下面两行,效果会清爽很多。

LOG_FILE = "TencentSpider.log"
LOG_LEVEL = "INFO"

Scrapy提供5层logging级别:

① CRITICAL - 严重错误(critical)
② ERROR - 一般错误(regular errors)
③ WARNING - 警告信息(warning messages)
④ INFO - 一般信息(informational messages)
⑤ DEBUG - 调试信息(debugging messages)

通过在setting.py中进行以下设置可以被用来配置logging:

① LOG_ENABLED 默认: True,启用logging
② LOG_ENCODING 默认: 'utf-8',logging使用的编码
③ LOG_FILE 默认: None,在当前目录里创建logging输出文件的文件名
④ LOG_LEVEL 默认: 'DEBUG',log的最低级别
⑤ LOG_STDOUT 默认: False 如果为 True,进程所有的标准输出(及错误)将会被重定向到log中。例如,执行 print "hello" ,其将会在Scrapy log中显示。

crawlspider的源码学习的更多相关文章

  1. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  2. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  3. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  4. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  5. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  7. 我的angularjs源码学习之旅2——依赖注入

    依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...

  8. ddms(基于 Express 的表单管理系统)源码学习

    ddms是基于express的一个表单管理系统,今天抽时间看了下它的代码,其实算不上源码学习,只是对它其中一些小的开发技巧做一些记录,希望以后在项目开发中能够实践下. 数据层封装 模块只对外暴露mod ...

  9. leveldb源码学习系列

    楼主从2014年7月份开始学习<>,由于书籍比较抽象,为了加深思考,同时开始了Google leveldb的源码学习,主要是想学习leveldb的设计思想和Google的C++编程规范.目 ...

随机推荐

  1. rtmp与hls流媒体服务器搭建:ubuntu下Nginx搭建初探与rtmp-module的添加

    关键词:Nignx(http服务器):rtmp,hls(流媒体服务) 前言:感谢开源,感谢战斗民族.现在在做流媒体服务的一些工作,流媒体服务器搭建的网上教程多入牛毛,但是细细查看,发现很多同志贴上来的 ...

  2. [JSOI 2015] 最大公约数

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=4488 [算法] 不妨首先枚举左端点 注意到对于任意一个正整数n , 其质因子个数是l ...

  3. [转]两种高性能I/O设计模式(Reactor/Proactor)的比较

    [原文地址:http://www.cppblog.com/pansunyou/archive/2011/01/26/io_design_patterns.html] 综述 这篇文章探讨并比较两种用于T ...

  4. WCF中WcfSvcHost.exe如何禁止自动启动

    今天同事问在一个WCF server的解决方案里调试时如何禁止Server自动启动. 经过调查发现, VS的工具WcfSvcHost会在调试时自动扫描工程里的WCF server, 然后启动起来. 如 ...

  5. 代码实现跟控制器跳转到storyBoard

  6. vijos1779国王游戏

    描述 恰逢H国国庆,国王邀请n位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这n位大臣排成一排,国王站在队伍的最前面.排好队后, ...

  7. POJ3256:Cow Picnic

    Cow Picnic Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 5432   Accepted: 2243 Descri ...

  8. JavaScript-Tool:jquery.cookie.js

    ylbtech-JavaScript-Tool:jquery.cookie.js 1.返回顶部 1.jquery.cookie.js /*! * jQuery Cookie Plugin v1.4.0 ...

  9. Android audioManager

    Android audioManager AudioManager provides access to volume and ringer mode control. 获取对象 Use Contex ...

  10. HttpApplication 对象的创建过程及HttpModule过滤器的内部实现过程

    最近通过Reflector学习了一下asp.net内部的原理,做做笔记,方便以后查阅. 先看下HttpApplication 对象的创建过程 从IHttpHandler applicationInst ...