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
TinScrapy-简化的Scrapy原码-查看爬虫的执行流程的更多相关文章
- Yii2 源码分析 入口文件执行流程
Yii2 源码分析 入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...
- asyncio源码分析之基本执行流程
基于async关键字的原生协程 # 定义一个简单的原生协程cor async def cor(): print('enter cor') print('exit cor') print(type(co ...
- 【推理引擎】从源码看ONNXRuntime的执行流程
目录 前言 准备工作 构造 InferenceSession 对象 & 初始化 让模型 Run 总结 前言 在上一篇博客中:[推理引擎]ONNXRuntime 的架构设计,主要从文档上对ONN ...
- 浩哥解析MyBatis源码(一)——执行流程
原创作品,可以转载,但是请标注出处地址: 一.MyBatis简介 MyBatis框架是一种轻量级的ORM框架,当下十分流行,配合Spring+Spring MVC组成SSM框架,能够胜任几乎所有的项目 ...
- Mybatis 系列10-结合源码解析mybatis 的执行流程
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- scrapy 6023 telnet查看爬虫引擎相关状态
Telnet终端(Telnet Console) Scrapy提供了内置的telnet终端,以供检查,控制Scrapy运行的进程. telnet仅仅是一个运行在Scrapy进程中的普通python终端 ...
- Flask源码解析:Flask应用执行流程及原理
WSGI WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的 ...
- 通过更改scrapy源码进行spider分发实现一个综合爬虫
最近我正写一个项目,项目的需求如下一,要爬取大约100种几百个网页的类容,并且这些网页的爬取频率不一样,有些一天爬取一次,有些一周爬取一次,二,网页爬取内容有变化,也就是说要爬取的内容会根据需求进行改 ...
- 基于Python,scrapy,redis的分布式爬虫实现框架
原文 http://www.xgezhang.com/python_scrapy_redis_crawler.html 爬虫技术,无论是在学术领域,还是在工程领域,都扮演者非常重要的角色.相比于其他 ...
随机推荐
- iFace安全专家揭秘:存放在区块链钱包中的比特币,其实已经早就不属于你……
自MoreToken钱包跑路之后,2019年3月以来陆续多个钱包.交易所跑路,造成了大量用户账户被盗,仅MoreToken钱包用户损失总价值就达12.2亿人民币,用户损失惨重.为什么这么多钱包.交易所 ...
- java热加载和热部署
JAVA热部署和热加载 联系与区别 Java热部署与热加载的联系 1.不重启服务器编译/部署项目 2.基于Java的类加载器实现 区别 部署方式 热部署在服务器运行时重新部署项目 热加载在运行时重新加 ...
- java中过多if-else分支语句的优化方案
利用Map优化过的的if-else分支 package com.taiping.test; import java.util.HashMap;import java.util.Map; public ...
- python日志syslog运用
syslog的官方说明在: https://docs.python.org/2/library/syslog.html#module-syslog 该模块的主要方式为: #!/usr/bin/pyth ...
- 【译】如何高效的使用 Git
原文链接 代码昨天还是运行好好的今天就不行了. 代码被删了. 突然出现了一个奇怪的 bug,但是没人知道怎么回事. 如果你出现过上面的任何一种情况,那本篇文章就是为你准备的. 除了知道 git add ...
- 多机部署redis5.0集群环境
redis5.0集群部署 一.集群介绍 Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施(installation). Redis 集群不支持那些需要同时处理多个键的 Redi ...
- Jquery制作小星星(常用于评价)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- C#2.0导航
主要特性 泛型 类型和方法的参数化 可空类型 值类型可为null 委托 更简化的方式 迭代器 简单的foreach,不简单的状态机
- Vue依赖收集引发的问题
问题背景 在我们的项目中有一个可视化配置的模块,是通过go.js生成canvas来实现的.但是,我们发现这个模块在浏览器中经常会引起该tab页崩溃.开启chrome的任务管理器一看,进入该页面内存和c ...
- Config安全控制
1.config server引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <a ...