手写Scrapy系列(四) – Python量化投资

手写Scrapy系列(四)

计划表:

  • 基本的Request/Response (完成)
  • 实现异步下载 (完成)
  • 加入队列Queue,为实现调度器做准备 (完成)
  • 加入引擎管理 (当前进度)
  • 加入调度器管理
  • 加入下载器管理
  • 加入下载器中间件管理
  • 加入爬虫进程管理
  • 加入信号机制管理

从这节开始,就涉及到Scrapy核心模块,引擎模块
在Scrapy框架中,引擎是整个框架的核心,负责管理调度器、下载器、中间件、管道等等,接下来慢慢添加他。

class Request:
    def __init__(self, url, callback=None):
        self.url = url
        self.callback = callback
class Response:
    def __init__(self, byte_content, request):
        self.content = byte_content
        self.request = request
        self.url = request.url
        self.text = str(byte_content, encoding='utf-8')
class MySpider:
    start_urls = ['http://www.czasg.xyz', 'http://www.czasg.xyz', 'http://www.czasg.xyz', 'http://www.czasg.xyz',
                  'http://www.czasg.xyz', 'http://www.czasg.xyz', 'http://www.czasg.xyz', 'http://www.czasg.xyz']
    def start_requests(self):
        yield from (Request(url, self.parse) for url in self.start_urls)
    def parse(self, response):
        print(response.url)

我们首先确定,加入引擎模块,需要可以直接嵌入既有代码,所以主体部分仍然和上一节一样。

我们来看下引擎模块的设计:

class Engine:
    def __init__(self):...
    @defer.inlineCallbacks
    def start(self):...
    def _handle_downloader_output(self, byte_content, request):...
    def _next_request(self):...
    @defer.inlineCallbacks
    def open_spider(self, spider):...

  • open_spider:用于初始化Queue,将Request对象推到队列中。
    @defer.inlineCallbacks
    def open_spider(self, spider):
        for request in spider.start_requests():
            q.put(request)
        reactor.callLater(0, self._next_request)
        yield
  • _next_request:从Queue中获取Request并处理Request对象,而且应该包含下载,及下载后的结果传递。
    def _next_request(self):
        if len(self.crawling) < self.max_pool_size:
            while True:
                try:
                    request = q.get(block=False)
                    self.crawling.append(request)
                    dfd = getPage(request.url.encode())
                    dfd.addCallback(self._handle_downloader_output, request)
                    dfd.addCallback(lambda _: reactor.callLater(0, self._next_request))
                except _queue.Empty:
                    break
        if q.qsize() == 0 and not self.crawling:
            self._closewait.callback(None)
  • _handle_downloader_output:处理获取到的Response,也就是上几节说到的,构造Response对象,然后调用Request的回调函数处理
    def _handle_downloader_output(self, byte_content, request):
        response = Response(byte_content, request)
        request.callback(response)
        self.crawling.remove(request)
  • start:开始函数。该函数需要实例化一个Deferred对象,并返回给主函数,以边后期能够结束爬虫进程。
    @defer.inlineCallbacks
    def start(self):
        self._closewait = defer.Deferred()
        yield self._closewait

贴一个完整的引擎模块:

class Engine:
    def __init__(self):
        self.max_pool_size = 4
        self.crawling = []
    @defer.inlineCallbacks
    def start(self):
        self._closewait = defer.Deferred()
        yield self._closewait
    def _handle_downloader_output(self, byte_content, request):
        response = Response(byte_content, request)
        request.callback(response)
        self.crawling.remove(request)
    def _next_request(self):
        if len(self.crawling) < self.max_pool_size:
            while True:
                try:
                    request = q.get(block=False)
                    self.crawling.append(request)
                    dfd = getPage(request.url.encode())
                    dfd.addCallback(self._handle_downloader_output, request)
                    dfd.addCallback(lambda _: reactor.callLater(0, self._next_request))
                except _queue.Empty:
                    break
        if q.qsize() == 0 and not self.crawling:
            self._closewait.callback(None)
    @defer.inlineCallbacks
    def open_spider(self, spider):
        for request in spider.start_requests():
            q.put(request)
        reactor.callLater(0, self._next_request)
        yield

接下就是如何启动该爬虫了:

if __name__ == '__main__':
    @defer.inlineCallbacks
    def crawl():
        spider = MySpider()
        engine = Engine()
        yield engine.open_spider(spider)
        yield engine.start()
    d = crawl()
    d.addBoth(lambda _: reactor.stop())
    reactor.run()
  1. yield engine.open_spider(spider): 从爬虫中获取Request对象,推到队列,完成初始化
  2. yield engine.start(): 获取一个Deferred句柄,并绑定匿名函数lambda _: reactor.stop(),当没有爬虫任务的时候,能够关闭爬虫。

gitbub地址:https://github.com/CzaOrz/ioco/blob/t426/open_source_project/scrapy_simulate_tutorial/4.py

https://www.jianshu.com/p/dc3c0c137b76

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论