Scrapy 组件分析
image.png
Spiders ->网页分析器
Item Pipline -> 数据管道
Scheduler -> 调度器
Downloader -> 下载器
Scraoy Engine -> 核心引擎
Scrapy 执行过程分析
image.png
Spider(我们编码的一个网站的爬虫) yield 一个 Request 出来, 并发送给 Engine(产生 request, 处理 response)
Engine 拿到 Request 以后发送给 Scheduler(调度器)
Scheduler 生成一个 Requests 交给 Engine
Engine 拿到 Scheduler 的 request 后(注意是 Scheduler 发来的而不是 Spider 发来的), 经过设置好的 DownloaderMiddleware, 一步一步将 Request 送达 Downloader
Downloader 下载网页后, 再经过设置好的 DownloaderMiddleware, 一步一步将 HttpResponse 返回给 Engine
Downloader 拿到 Response 以后发送给 Spiders 进行处理分析(比如正则表达式, CSS 选择器的配合使用提取网页字段)
Spider 处理完的结果分为两类, 一类是 Item, 一类是 Request, 这两类都会发给 Engine,Engine 拿到后判断如果是 Items 则会走 8, 如果是 Requests 则重复走 2
Engine 将 Spiders 发送过来的 item 发送给 Item Piplines, 将结果一步一步的 Piplines 将数据持久化到不同存储体里, 比如 JSON,Mysql,ES 等
源码分析
Scrapy 核心的代码都在 scrapy 类库的 scrapy/core 文件夹下
image.png
(downloader 支持多种类型下载)
spider,pipline,middleware 是自己编写的
image.png
Engine 源码简析
- ...
- ...
- # 此处为执行过程中 1-2 步, Engine 拿到 request 后发送给 Scheduler
- def schedule(self, request, spider):
- self.signals.send_catch_log(signal=signals.request_scheduled,
- request=request, spider=spider)
- # 调用 scheduler 的 enqueue_request 方法将 request 放到 Scheduler 中
- if not self.slot.scheduler.enqueue_request(request):
- self.signals.send_catch_log(signal=signals.request_dropped,
- request=request, spider=spider)
- ...
- ...
- # 此处为执行过程中第三步, 从 Engine 中拿 request 给 Scheduler
- def _next_request_from_scheduler(self, spider):
- slot = self.slot
- request = slot.scheduler.next_request()
- # 爬虫首次启动的时候先执行这个_next_request_from_scheduler 方法,
- # 但是 scheduler 里此时没有 request, 所以就会去从 Spider 中读取 start_urls
- if not request:
- return
- d = self._download(request, spider)
- d.addBoth(self._handle_downloader_output, request, spider)
- d.addErrback(lambda f: logger.info('Error while handling downloader output',
- exc_info=failure_to_exc_info(f),
- extra={'spider': spider}))
- d.addBoth(lambda _: slot.remove_request(request))
- d.addErrback(lambda f: logger.info('Error while removing request from slot',
- exc_info=failure_to_exc_info(f),
- extra={'spider': spider}))
- d.addBoth(lambda _: slot.nextcall.schedule())
- d.addErrback(lambda f: logger.info('Error while scheduling new request',
- exc_info=failure_to_exc_info(f),
- extra={'spider': spider}))
- return d
- ...
- ...
Request 类 (由 Spider 产生) 构造函数参数分析
- class Request(object_ref):
- # url: 请求参数
- # callback: 请求回调函数
- # method: http 请求类型
- # headers: 请求头
- # body: 请求体
- # cookies: 浏览器 cookie, 自动登录后, scrapy 会自动把 cookie 加入 request 中
- # 该操作的实现是由 scrapy.downloadermiddlewares.cookies.CookiesMiddleware 的 scrapy 内置 Middleware 完成的
- # meta: 元信息,(可以在 Request 中传递)
- # encoding: 网页编码格式, 默认 UTF-8
- # priority: 设置在 scheduler 的调度优先级
- # dont_filter: 是否不过滤同时发出的相同 request 请求
- # errback: 失败的回调函数
- #
- def __init__(self, url, callback=None, method='GET', headers=None, body=None,
- cookies=None, meta=None, encoding='utf-8', priority=0,
- dont_filter=False, errback=None, flags=None):
Response 类 (由 Downloader 产生) 构造函数参数分析
- class Response(object_ref):
- # url 网页的 url
- # status 返回状态码, 默认是 200, 代表成功
- # headers 服务器返回的响应头
- # body 返回的内容体
- # request 之前 yield 的 Request, 对应的请求
- def __init__(self, url, status=200, headers=None, body=b'', flags=None, request=None):
- self.headers = Headers(headers or {})
- self.status = int(status)
- self._set_body(body)
- self._set_url(url)
- self.request = request
- self.flags = [] if flags is None else list(flags)
其子类有 htmlResponse,TextResponse,XmlResponse
- from scrapy.http.response.text import TextResponse
- class HtmlResponse(TextResponse):
- pass
- class TextResponse(Response):
- ...
- ...
- # Response 内部已经引入了 selector 拱 xpath,css 方法调用
- @property
- def selector(self):
- from scrapy.selector import Selector
- if self._cached_selector is None:
- self._cached_selector = Selector(self)
- return self._cached_selector
- # xpath 选择器
- def xpath(self, query, **kwargs):
- return self.selector.xpath(query, **kwargs)
- # css 选择器
- def css(self, query):
- return self.selector.css(query)
- ...
- ...
来源: http://www.jianshu.com/p/c7c464d8bdd1