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

时间:2023-03-10 02:30:01
TinScrapy-简化的Scrapy原码-查看爬虫的执行流程

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

 from twisted.web.client import getPage,defer
from twisted.internet import reactor
import queue class Response(object):
'''
对返回内容进行封装为UTF8格式
'''
def __init__(self,body,request):
self.body=body
self.request=request
self.url=request.url @property
def text(self):
return self.body.decode('utf-8') class Request(object):
'''
封装,请求的URL 与回调函数
'''
def __init__(self,url,callback):
self.url=url
self.callback=callback class Scheduler(object):#调度器
'''
任务调度器
'''
def __init__(self,engine):
self.q=queue.Queue()#队列
self.engine=engine
def enqueue_request(self,request):
self.q.put(request)#加入队列
def next_request(self):
try:
req=self.q.get(block=False)#从队列中取出
except Exception as e:
req=None
return req
def size(self):
return self.q.qsize()#队列是的个数 class ExecutionEngine(object): #爬虫引擎 def __init__(self):#构造
self._closewait=None #关闭引擎调用 默认为不关闭
self.running=True#引擎默认为运行状态
self.start_requests=None #开始爬取任务
self.scheduler=Scheduler(self)#调度器 类 构造自己放入调度器,传回自身
self.inprogress =set() #集合 并发数 def check_empty(self,response):#检测任务是否为空
if not self.running:
print('任务终止。。。。')
self._closewait.callback(None) def _next_request(self):#下一个爬取任务
while self.start_requests:#存在爬取任务
try:
request=next(self.start_requests)
except StopIteration:#如果执行出错
self.start_requests=None#任务变为空
else:
self.scheduler.enqueue_request(request)#放入调度器中
print(len(self.inprogress),'=>总任务数',self.scheduler.size(),'=>调度器中的任务数')
while len(self.inprogress)< 5 and self.scheduler.size()>0: #最大并发数为 5
request=self.scheduler.next_request()#调度器中任务 调用自身
if not request:
break
self.inprogress.add(request)#加入任务并发
d=getPage(bytes(request.url,encoding='utf-8'))#开始爬取任务
#d.addError=()#任务出错时执行
#d.addCallback=()#任务成功完成时执行
d.addBoth(self._handle_downloader_output,request)#下载 任务 #无论是否成功都执行 有返回值 运行_handle
d.addBoth(lambda x,req:self.inprogress.remove(req),request)#正在运行的进行移除
d.addBoth(lambda x:self._next_request())#执行本身的函数
if len(self.inprogress)==0 and self.scheduler.size()==0:
self._closewait.callback(None)#执行关闭程序 def _handle_downloader_output(self,body,request):#任务后的回调函数
'''
获取内容,执行回调函数,并且把回调函数中的返回值获取,并添加到队列中
:param response:
:param request:
:return:
'''
import types
response=Response(body,request)#进行封装
func=request.callback or self.spider.parse#如果有回返值,func取回返值 否则 等于任务的最开始内容
gen=func(response)
if isinstance(gen,types.GeneratorType):#是否是生成器对象
for req in gen:
self.scheduler.enqueue_request(req) @defer.inlineCallbacks
def start(self):
self._closewait=defer.Deferred()#生成一个空任务对象 用于保持程序
yield self._closewait @defer.inlineCallbacks
def open_spider(self,spider,start_requests):#传入封装好的Requset ,任务迭代器
self.start_requests=start_requests#任务迭代器
self.spider=spider#封装好的Requset (请求的URL 与回调函数)
yield None #生成器,断点缓存
reactor.callLater(0,self._next_request)#立刻执行 下一个爬取任务 class Crawler(object):#爬虫执行类
def __init__(self,spidercls):#传入任务(ChoutiSpider,等)
self.spidercls=spidercls
self.spider =None
self.engine=None @defer.inlineCallbacks
def crawl(self):
self.engine=ExecutionEngine()#类实例化 引擎
self.spider=self.spidercls()#实例化任务
start_requests =iter(self.spider.start_requests())#迭代器
yield self.engine.open_spider(self.spider,start_requests)#引擎启动
yield self.engine.start()#开始执行 class CrawlerProcess(object):#爬虫任务器 类
def __init__(self):
self._active=set()#已经执行任务集合
self.crawlers=set()# 爬虫任务集合 def crawl(self,spidercls,*args,**kwargs):#传入爬虫任务
crawler=Crawler(spidercls)#爬虫任务开始 实例化
self.crawlers.add(crawler)#爬虫任务集合
d=crawler.crawl(*args,**kwargs)#爬取任务实例化 运行
self._active.add(d)#加入已经执行任务集合
return d def start(self):
d1=defer.DeferredList(self._active)#实例化执行对象
d1.addBoth(self._stop_reactor)#所有任务结束 调用 stop方法
reactor.run() def _stop_reactor(self,_=None):#任务停止
reactor.stop() class Spider(object):
def start_requests(self):
for url in self.start_urls:
yield Request(url,self.parse)#生成器,对URL与回调函数进行封装 #=========具体爬虫任务=======start==========#
class ChoutiSpider(Spider):#
name='chouti'
start_urls=['http://dig.chouti.com/',]
def parse(self,response):
print(response.text) class CnblogsSpider(Spider):
name='cnblogs'
start_urls=['http://www.cnblogs.com/',]
def parse(self,response):
print(response.text)
#=========具体爬虫任务=======end==========# if __name__=='__main__':
spider_cls_list=[ChoutiSpider,CnblogsSpider]#加入任务列表
crawler_process=CrawlerProcess()#实例化爬虫任务器
for spider_cls in spider_cls_list:
crawler_process.crawl(spider_cls)#传入任务
crawler_process.start()#开始执行

TinyScrapy