TinScrapy-简化的Scrapy原码-查看爬虫的执行流程
学习了自定义的TinyScrapy框架,整理出以下定注释的代码
1 from twisted.web.client import getPage,defer 2 from twisted.internet import reactor 3 import queue 4 5 class Response(object): 6 ''' 7 对返回内容进行封装为UTF8格式 8 ''' 9 def __init__(self,body,request): 10 self.body=body 11 self.request=request 12 self.url=request.url 13 14 @property 15 def text(self): 16 return self.body.decode('utf-8') 17 18 class Request(object): 19 ''' 20 封装,请求的URL 与回调函数 21 ''' 22 def __init__(self,url,callback): 23 self.url=url 24 self.callback=callback 25 26 class Scheduler(object):#调度器 27 ''' 28 任务调度器 29 ''' 30 def __init__(self,engine): 31 self.q=queue.Queue()#队列 32 self.engine=engine 33 def enqueue_request(self,request): 34 self.q.put(request)#加入队列 35 def next_request(self): 36 try: 37 req=self.q.get(block=False)#从队列中取出 38 except Exception as e: 39 req=None 40 return req 41 def size(self): 42 return self.q.qsize()#队列是的个数 43 44 class ExecutionEngine(object): #爬虫引擎 45 46 def __init__(self):#构造 47 self._closewait=None #关闭引擎调用 默认为不关闭 48 self.running=True#引擎默认为运行状态 49 self.start_requests=None #开始爬取任务 50 self.scheduler=Scheduler(self)#调度器 类 构造自己放入调度器,传回自身 51 self.inprogress =set() #集合 并发数 52 53 def check_empty(self,response):#检测任务是否为空 54 if not self.running: 55 print('任务终止。。。。') 56 self._closewait.callback(None) 57 58 def _next_request(self):#下一个爬取任务 59 while self.start_requests:#存在爬取任务 60 try: 61 request=next(self.start_requests) 62 except StopIteration:#如果执行出错 63 self.start_requests=None#任务变为空 64 else: 65 self.scheduler.enqueue_request(request)#放入调度器中 66 print(len(self.inprogress),'=>总任务数',self.scheduler.size(),'=>调度器中的任务数') 67 while len(self.inprogress)< 5 and self.scheduler.size()>0: #最大并发数为 5 68 request=self.scheduler.next_request()#调度器中任务 调用自身 69 if not request: 70 break 71 self.inprogress.add(request)#加入任务并发 72 d=getPage(bytes(request.url,encoding='utf-8'))#开始爬取任务 73 #d.addError=()#任务出错时执行 74 #d.addCallback=()#任务成功完成时执行 75 d.addBoth(self._handle_downloader_output,request)#下载 任务 #无论是否成功都执行 有返回值 运行_handle 76 d.addBoth(lambda x,req:self.inprogress.remove(req),request)#正在运行的进行移除 77 d.addBoth(lambda x:self._next_request())#执行本身的函数 78 if len(self.inprogress)==0 and self.scheduler.size()==0: 79 self._closewait.callback(None)#执行关闭程序 80 81 def _handle_downloader_output(self,body,request):#任务后的回调函数 82 ''' 83 获取内容,执行回调函数,并且把回调函数中的返回值获取,并添加到队列中 84 :param response: 85 :param request: 86 :return: 87 ''' 88 import types 89 response=Response(body,request)#进行封装 90 func=request.callback or self.spider.parse#如果有回返值,func取回返值 否则 等于任务的最开始内容 91 gen=func(response) 92 if isinstance(gen,types.GeneratorType):#是否是生成器对象 93 for req in gen: 94 self.scheduler.enqueue_request(req) 95 96 @defer.inlineCallbacks 97 def start(self): 98 self._closewait=defer.Deferred()#生成一个空任务对象 用于保持程序 99 yield self._closewait 100 101 @defer.inlineCallbacks 102 def open_spider(self,spider,start_requests):#传入封装好的Requset ,任务迭代器 103 self.start_requests=start_requests#任务迭代器 104 self.spider=spider#封装好的Requset (请求的URL 与回调函数) 105 yield None #生成器,断点缓存 106 reactor.callLater(0,self._next_request)#立刻执行 下一个爬取任务 107 108 class Crawler(object):#爬虫执行类 109 def __init__(self,spidercls):#传入任务(ChoutiSpider,等) 110 self.spidercls=spidercls 111 self.spider =None 112 self.engine=None 113 114 @defer.inlineCallbacks 115 def crawl(self): 116 self.engine=ExecutionEngine()#类实例化 引擎 117 self.spider=self.spidercls()#实例化任务 118 start_requests =iter(self.spider.start_requests())#迭代器 119 yield self.engine.open_spider(self.spider,start_requests)#引擎启动 120 yield self.engine.start()#开始执行 121 122 class CrawlerProcess(object):#爬虫任务器 类 123 def __init__(self): 124 self._active=set()#已经执行任务集合 125 self.crawlers=set()# 爬虫任务集合 126 127 def crawl(self,spidercls,*args,**kwargs):#传入爬虫任务 128 crawler=Crawler(spidercls)#爬虫任务开始 实例化 129 self.crawlers.add(crawler)#爬虫任务集合 130 d=crawler.crawl(*args,**kwargs)#爬取任务实例化 运行 131 self._active.add(d)#加入已经执行任务集合 132 return d 133 134 def start(self): 135 d1=defer.DeferredList(self._active)#实例化执行对象 136 d1.addBoth(self._stop_reactor)#所有任务结束 调用 stop方法 137 reactor.run() 138 139 def _stop_reactor(self,_=None):#任务停止 140 reactor.stop() 141 142 class Spider(object): 143 def start_requests(self): 144 for url in self.start_urls: 145 yield Request(url,self.parse)#生成器,对URL与回调函数进行封装 146 147 #=========具体爬虫任务=======start==========# 148 class ChoutiSpider(Spider):# 149 name='chouti' 150 start_urls=['http://dig.chouti.com/',] 151 def parse(self,response): 152 print(response.text) 153 154 class CnblogsSpider(Spider): 155 name='cnblogs' 156 start_urls=['http://www.cnblogs.com/',] 157 def parse(self,response): 158 print(response.text) 159 #=========具体爬虫任务=======end==========# 160 161 if __name__=='__main__': 162 spider_cls_list=[ChoutiSpider,CnblogsSpider]#加入任务列表 163 crawler_process=CrawlerProcess()#实例化爬虫任务器 164 for spider_cls in spider_cls_list: 165 crawler_process.crawl(spider_cls)#传入任务 166 crawler_process.start()#开始执行
您的资助是我最大的动力!
金额随意,欢迎来赏!
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的
因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【莫柔落切】!
联系或打赏博主【莫柔落切】!https://home.cnblogs.com/u/uge3/