学习了自定义的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

TinScrapy-简化的Scrapy原码-查看爬虫的执行流程的更多相关文章

  1. Yii2 源码分析 入口文件执行流程

    Yii2 源码分析  入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...

  2. asyncio源码分析之基本执行流程

    基于async关键字的原生协程 # 定义一个简单的原生协程cor async def cor(): print('enter cor') print('exit cor') print(type(co ...

  3. 【推理引擎】从源码看ONNXRuntime的执行流程

    目录 前言 准备工作 构造 InferenceSession 对象 & 初始化 让模型 Run 总结 前言 在上一篇博客中:[推理引擎]ONNXRuntime 的架构设计,主要从文档上对ONN ...

  4. 浩哥解析MyBatis源码(一)——执行流程

    原创作品,可以转载,但是请标注出处地址: 一.MyBatis简介 MyBatis框架是一种轻量级的ORM框架,当下十分流行,配合Spring+Spring MVC组成SSM框架,能够胜任几乎所有的项目 ...

  5. Mybatis 系列10-结合源码解析mybatis 的执行流程

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  6. scrapy 6023 telnet查看爬虫引擎相关状态

    Telnet终端(Telnet Console) Scrapy提供了内置的telnet终端,以供检查,控制Scrapy运行的进程. telnet仅仅是一个运行在Scrapy进程中的普通python终端 ...

  7. Flask源码解析:Flask应用执行流程及原理

    WSGI WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的 ...

  8. 通过更改scrapy源码进行spider分发实现一个综合爬虫

    最近我正写一个项目,项目的需求如下一,要爬取大约100种几百个网页的类容,并且这些网页的爬取频率不一样,有些一天爬取一次,有些一周爬取一次,二,网页爬取内容有变化,也就是说要爬取的内容会根据需求进行改 ...

  9. 基于Python,scrapy,redis的分布式爬虫实现框架

    原文  http://www.xgezhang.com/python_scrapy_redis_crawler.html 爬虫技术,无论是在学术领域,还是在工程领域,都扮演者非常重要的角色.相比于其他 ...

随机推荐

  1. [Swift]LeetCode1005. K 次取反后最大化的数组和 | Maximize Sum Of Array After K Negations

    Given an array A of integers, we must modify the array in the following way: we choose an i and repl ...

  2. 读取Json,并替换json中的指定字符

    string jsonfile = @"E:\history.json";//JSON文件路径 using (System.IO.FileStream file = new Fil ...

  3. android自动化必备之SDK

    进入到SDK包中,通过打开SDK manager.exe即可看到SDK管理界面,可能部分童靴发现一直在加载出不来,我们需要设置代理来解决: 选择工具栏上的Tools->Options打开如下窗口 ...

  4. ReentrantLock 实现原理

    使用 synchronize 来做同步处理时,锁的获取和释放都是隐式的,实现的原理是通过编译后加上不同的机器指令来实现. 而 ReentrantLock 就是一个普通的类,它是基于 AQS(Abstr ...

  5. 死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 删除元素 删除元素本身比较简单,就是采用二叉树的删除规则. (1)如果删除的位置有两个叶子节点 ...

  6. 从.Net到Java学习第二篇——IDEA and start spring boot

    从.Net到Java学习第一篇——开篇 所谓工欲善其事,必先利其器,做java开发也一样,在比较了目前最流行的几个java IDE(eclipse,myeclipse.IDEA)之后,我果断选择IDE ...

  7. Android--拦截系统BroadcastReceiver

    前言 上一篇博客,讲了BroadcastReceiver的一些基础内容,如何注册以及发送一个广播,那是基础,不清楚的可以先看看:Android--BroadcastReceiver.但是在实际开发当中 ...

  8. 使用开源项目Alipay.AopSdk.Core完成支付宝网页登录

    核心方法: public IActionResult UserInfoCallBackAli() { var alipayClient = new DefaultAopClient(ConfigAli ...

  9. 利用Sklearn实现加州房产价格预测,学习运用机器学习的整个流程(包含很多细节注解)

    Chapter1_housing_price_predict .caret, .dropup > .btn > .caret { border-top-color: #000 !impor ...

  10. 学习编写Dockerfile

    前言 我们学习docker的话,其主要目的还是要用来改变我们部署应用程序的传统习惯,达到解放生产力,解放人力的目的.这篇则自己来熟悉一下dockerfile常用命令.并且尝试改变下生产环境手动部署应用 ...