TinScrapy-简化的Scrapy原码-查看爬虫的执行流程

时间:2021-10-10 07:55:40

 

学习了自定义的TinyScrapy框架,整理出以下定注释的代码

TinScrapy-简化的Scrapy原码-查看爬虫的执行流程TinScrapy-简化的Scrapy原码-查看爬虫的执行流程
  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()#开始执行
TinyScrapy