scrapy源码分析(十二)---------下载中间件RobotsTxtMiddleware
来源:互联网 发布:淘客微博群发软件 编辑:程序博客网 时间:2024/05/16 06:49
上一节分析了下载器的源码,知道了一个request经过middleware到handler下载返回response,response再经过middleware,最后由scraper处理的流程。
其中正是middleware的存在使我们对下载和解析的控制有很大的灵活性,我们可以自定义中间件来个性化我们的需求。这一节就分析一下middleware在整个下载流程中所发挥的关键作用。
我们从默认的下载中间件入手来分析,先看下默认的下载中间件:
DOWNLOADER_MIDDLEWARES_BASE = { # Engine side 'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100, 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300, 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350, 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 400, 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 500, 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 550, 'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560, 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580, 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590, 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600, 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700, 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750, 'scrapy.downloadermiddlewares.chunked.ChunkedTransferMiddleware': 830, 'scrapy.downloadermiddlewares.stats.DownloaderStats': 850, 'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900, # Downloader side}
1.RobotsTxtMiddleware:
scray/downloadermiddlewares/robotstxt.py:
前面分析中间件管理器时知道,管理器构造中间件时会优先调用中间件的from_crawler方法,因此构造RobotsTxtMiddleware时只是调用其构造函数。class RobotsTxtMiddleware(object): DOWNLOAD_PRIORITY = 1000 def __init__(self, crawler): if not crawler.settings.getbool('ROBOTSTXT_OBEY'): raise NotConfigured self.crawler = crawler self._useragent = crawler.settings.get('USER_AGENT') self._parsers = {} @classmethod def from_crawler(cls, crawler): return cls(crawler)
配置文件中如果未定义是否遵守机器人协议‘ROBOTSTXT_OBEY',则抛出未配置异常。这会使Robots中间件构造失败,因此也不会处理网站的机器人文件。
然后还会从配置中获取'USER_AGENT'配置,
最后定义了一个_parsers字典,用来存储已经分析过Robts.txt文件的网站信息。
然后分析其’process_request'方法,这个方法前面讲过会在下载之前由下载中间件管理器调用。
def process_request(self, request, spider): if request.meta.get('dont_obey_robotstxt'): return d = maybeDeferred(self.robot_parser, request, spider) d.addCallback(self.process_request_2, request, spider) return d首先判断request是否在meta中设置了'dont_obey_robotstx',如果设置了则什么也不做。def process_request_2(self, rp, request, spider): if rp is not None and not rp.can_fetch(self._useragent, request.url): logger.debug("Forbidden by robots.txt: %(request)s", {'request': request}, extra={'spider': spider}) raise IgnoreRequest()
然后调用robot_parser方法下载和分析robot.txt文件,并给返回的Deferred对象安装了一个'process_request_2'方法。
robot_parser会去下载robot.txt文件,并会返回一个rp对象。scrapy使用的是第三方库six.moves.urllib.robotparser来分析robot.txt的,返回的rp就是这个对象。这个对象提供了can_fetch方法用来判断是否允许爬取指定的URL。因此,给robt_parser返回的Deferred对象安装的process_request_2函数用rp来判断是否允许爬取request.url
再来看下'robot_parser'方法,函数比较长,代码分析通过注释讲解
def robot_parser(self, request, spider): url = urlparse_cached(request) netloc = url.netloc /*获取request对应的网站*/ if netloc not in self._parsers: /*还没有分析过则继续*/ self._parsers[netloc] = Deferred() robotsurl = "%s://%s/robots.txt" % (url.scheme, url.netloc) robotsreq = Request( robotsurl, priority=self.DOWNLOAD_PRIORITY, /*robots的request的优化级为1000*/ meta={'dont_obey_robotstxt': True} /*对于robot.txt文件不遵守robot协议*/ ) dfd = self.crawler.engine.download(robotsreq, spider) /*调用engine的download方法下载robot.txt,注意调用这个方法不会走入网页的解析和scraper流程。*/ dfd.addCallback(self._parse_robots, netloc) /*下载完成后调用'_parse_robots方法*/ dfd.addErrback(self._logerror, robotsreq, spider) dfd.addErrback(self._robots_error, netloc) if isinstance(self._parsers[netloc], Deferred): /*如果分析结果是一个Deferred对象,说明之前的robot还未处理完成,则再构造一个deferred对象d返回,当分析结果的Defered对象完成时,激活返回的d.这样就会调用'process_request_2'方法了*/ d = Deferred() def cb(result): d.callback(result) return result self._parsers[netloc].addCallback(cb) return d else: /*不是Deferred对象则已经分析完成了,直接返回对应的RobotFileParser对象*/ return self._parsers[netloc]
接下来看看下载成功后调用的'_parse_robots'方法:
def _parse_robots(self, response, netloc): rp = robotparser.RobotFileParser(response.url) /*调用第三方库生成RobotFileParser对象。*/ body = '' if hasattr(response, 'text'): /*处理网页内容编码*/ body = response.text else: # last effort try try: body = response.body.decode('utf-8') except UnicodeDecodeError: # If we found garbage, disregard it:, # but keep the lookup cached (in self._parsers) # Running rp.parse() will set rp state from # 'disallow all' to 'allow any'. pass rp.parse(body.splitlines()) /*分析网页内容*/ rp_dfd = self._parsers[netloc] /*获取Deferred对象*/ self._parsers[netloc] = rp /*将字典中存储的替换为生成的RobotFileParser对象*/ rp_dfd.callback(rp) /*用rp对象激活deferred,这样就会调用为deferred添加的'process_request_2'方法了*/
这样就分析完了RobotsTxtMiddleware中间件的源码,它用来分析被爬取的网站的robot.txt文件,用来决定是否要对request继续爬取。当然也可以通过给Request指定
’dont_obey_robotstxt‘meta属性来不遵守robot协议。
- scrapy源码分析(十二)---------下载中间件RobotsTxtMiddleware
- scrapy源码分析(十一)----------下载器Downloader
- scrapy中的下载器中间件
- zg手册 之 scrapy 开发(3)-- 下载中间件
- laravel中间件源码分析
- scrapy源码分析(六)---------------CrawlProcess
- scrapy源码分析(七)------------ Crawler
- scrapy源码分析(八)--------ExecutionEngine
- scrapy源码分析(九)-----------Scheduler
- scrapy源码分析(十)------------Scraper
- Scrapy源码分析(四):请求Request
- scrapy-redis源码分析
- Scrapy-settings源码分析
- Scrapy-redis源码分析
- appium 源码分析(十二)-pressKeyCode,LongPressKeyCode
- Shark源码分析(十二):线性SVM
- (二十二)Animator 源码分析
- scrapy源码分析(五)--------------execute函数分析
- 管道命令和xargs的区别
- leetcode-68. Text Justification
- Android 开源日志库 Logger 使用教程
- fork()进阶点
- 阻塞非阻塞与同步异步的区别
- scrapy源码分析(十二)---------下载中间件RobotsTxtMiddleware
- 动态创建script实现跨域资源访问(转)
- 队列的基本操作
- 寝室卫生问题
- 静态链表GO语言实现
- JavaScript中的焦点事件 — 第13.4.2 节
- Kindle Paperwhite 2 USB升级
- Java使用form表单上传文件10大需要注意事项
- 1619-7 张良 十二月十三号总结 [连续第七十四天]