zoukankan      html  css  js  c++  java
  • Scrapy源码研究前戏

    一、Twisted的使用

    在 Twisted 中,有一种特殊的对象用于实现事件循环。这个对象叫做 reactor。可以把反应器
    (reactor)想象为 Twisted 程序的中枢神经。除了分发事件循环之外,反应器还做很多重要的
    工作:定时任务、线程、建立网络连接、监听连接。为了让反应器可以正常工作,需要启动
    事件循环。
    1、一个简单的使用
    #########################
    # 1.利用getPage创建socket
    # 2.将socket添加到事件循环中
    # 3.开始事件循环(自动结束)
    #########################
    def response(content):
        print(content)
    
    # 该装饰器装饰的内容,只要yield是一个阻塞的对象都会转交给reactor接手
    @defer.inlineCallbacks
    def task():
        url = "http://www.baidu.com"
        d = getPage(url.encode('utf-8'))
        d.addCallback(response)
        yield d
        url = "http://www.baidu.com"
        d = getPage(url.encode('utf-8'))
        d.addCallback(response)
        yield d
    
    def done(*args,**kwargs):
        reactor.stop()
    
    li = []
    for i in range(10):
        d = task()
        li.append(d)
    # DeferredList也属于defer的对象,也会转交给reactor接手
    dd = defer.DeferredList(li)
    # 给它增加了一个回调函数
    dd.addBoth(done)
    
    reactor.run()

    二、自定义爬虫包

    from twisted.internet import reactor  # 事件循环(终止条件,所有的socket都已经移除)
    from twisted.web.client import getPage  # socket对象(如果下载完成,自动从时间循环中移除...)
    from twisted.internet import defer  # defer.Deferred 特殊的socket对象 (不会发请求,手动移除)
    
    
    # 自定义一个Request 类
    class Request(object):
        def __init__(self, url, callback):
            """
            初始化接受url和callback回调函数
            :param url: 请求的url
            :param callback: 获取内容后的callback
            """
            self.url = url
            self.callback = callback
    
    
    # 响应对象
    class HttpResponse(object):
        def __init__(self, content, request):
            """
            初始化相应内容
            :param content: 下载 下来的响应的content
            :param request: response对应的request
            """
            # 响应的内容
            self.content = content
            # 响应的请求
            self.request = request
            # response对应的request
            self.url = request.url
            # 将内容转换为文本
            self.text = str(content, encoding='utf-8')
    
    
    class ChoutiSpider(object):
        """
        初始化顶一个小蜘蛛
        """
        name = 'chouti'
    
        # 蜘蛛一开始的执行方法
        def start_requests(self):
            start_url = ['http://www.baidu.com', 'http://www.bing.com', ]
            for url in start_url:
                yield Request(url, self.parse)
    
        # 收到response后的解析函数
        def parse(self, response):
            print(response)  # response是下载的页面
            yield Request('http://www.cnblogs.com', callback=self.parse)
    
    
    
    import queue
    # 这里是调度器
    Q = queue.Queue()
    
    
    # 定义了一个引擎类
    class Engine(object):
        def __init__(self):
            # 引擎关闭
            self._close = None
            # 最大的请求数
            self.max = 5
            # 正在爬的请求
            self.crawlling = []
    
        # 拿着相应的回调函数
        def get_response_callback(self, content, request):
            """
    
            :param content: 响应的content
            :param request: 响应对应的request
            :return:
            """
            # 一旦执行回调函数,就可以从调度中拿走这个请求
            self.crawlling.remove(request)
            # 将内容封装成 一个 HttpResponse对象
            rep = HttpResponse(content, request)
            # 调用请求时的回调函数,将封装的HttpResponse传递进去
            result = request.callback(rep)
            import types
            # 查看回调函数是否继续返回迭代器对象
            if isinstance(result, types.GeneratorType):
                # 将回调函数 新的请求放到调度器
                for req in result:
                    Q.put(req)
    
        # 从调度器取请求,执行,下载,并控制最大并发数
        def _next_request(self):
            """
            去取request对象,并发送请求
            最大并发数限制
            :return:
            """
            print(self.crawlling, Q.qsize())
            # 如果调度器的长度为0,而且处于正在爬取的数目也为 0 ,那么就说明该关闭了
            if Q.qsize() == 0 and len(self.crawlling) == 0:
                # 直接调用 defer.Deferred().callback(None)就会关闭defer
                self._close.callback(None)
                return
    
            # 如果正在爬取的数目超过了最大的并发限制,直接返回
            if len(self.crawlling) >= self.max:
                return
            # 如果没有达到并发限制,就执行以下内容
            while len(self.crawlling) < self.max:
                try:
                    # 从 调度器 取一个请求 任务
                    req = Q.get(block=False)
                    # 把拿到的请求放到 正在爬取的列表中
                    self.crawlling.append(req)
                    # 获取相应的页面
                    d = getPage(req.url.encode('utf-8'))
                    # 页面下载完成,get_response_callback,调用用户spider中定义的parse方法,并且将新请求添加到调度器
                    d.addCallback(self.get_response_callback, req)
                    # 未达到最大并发数,可以再去调度器中获取Request
                    # 继续给d添加回调函数,这个回调函数可以是匿名函数
                    d.addCallback(lambda _: reactor.callLater(0, self._next_request))
                except Exception as e:
                    print(e)
                    return
    
        @defer.inlineCallbacks
        def crawl(self, spider):
            # 将start_requests包含的生成器,初始Request对象添加到调度器
            start_requests = iter(spider.start_requests())
            while True:
                try:
                    # 拿到每个request,放到调度器中
                    request = next(start_requests)
                    Q.put( request)
                except StopIteration as e:
                    break
    
            # 去调度器中取request,并发送请求
            # self._next_request()
    
            reactor.callLater(0, self._next_request)
            # 初始化self._close
            self._close = defer.Deferred()
            yield self._close
    
    # 初始化一个抽屉爬虫
    spider = ChoutiSpider()
    
    _active = set()
    # 实例化一个引擎对象
    engine = Engine()
    
    # 引擎对象 调用 crawl方法,运行指定的spider
    d = engine.crawl(spider)
    
    # 将crawl方法放到set中
    _active.add(d)
    # 实例化一个DeferredList,将_active 内容放进去,返回一个defer.Deferred()对象,若defer.Deferred()被关闭,dd就为空
    dd = defer.DeferredList(_active)
    
    # 一旦dd里面为空,就调用reactor.stop()方法
    dd.addBoth(lambda a: reactor.stop())
    
    # 让它run起来
    reactor.run()
  • 相关阅读:
    人工智能背后的故事
    idea 开发插件。
    安卓工作室 Android studio 或 Intellij IDEA 美化 修改 汉化 酷炫 装逼 Android studio or Intellij IDEA beautify modify Chinesization cool decoration
    安卓工作室 android studio文件和代码模板,以及汉化出错问题
    安卓工作室 android studio 汉化后,报错。 设置界面打不开。Can't find resource for bundle java.util.PropertyResourceBundle, key emmet.bem.class.name.element.separator.label
    android studio的汉化 教程 及解析
    安卓工作室Android Studio 快捷键
    安卓工作室 android studio 的 汉化 美化 定制 Android studio's Chinesization beautification customization
    VR开发 VR development
    Lakeshore 中文开发界面,示例项目,飞机大战 等 Lakeshore Chinese development interface, sample project, aircraft war, etc
  • 原文地址:https://www.cnblogs.com/skiler/p/6891233.html
Copyright © 2011-2022 走看看