一、Twisted的使用

在 Twisted 中,有一种特殊的对象用于实现事件循环。这个对象叫做 reactor。可以把反应器
(reactor)想象为 Twisted 程序的中枢神经。除了分发事件循环之外,反应器还做很多重要的
工作:定时任务、线程、建立网络连接、监听连接。为了让反应器可以正常工作,需要启动
事件循环。
1、一个简单的使用
#########################
# 1.利用getPage创建socket
# 2.将socket添加到事件循环中
# 3.开始事件循环(自动结束)
#########################
def response(content):
print(content) # 该装饰器装饰的内容,只要yield是一个阻塞的对象都会转交给reactor接手
@defer.inlineCallbacks
def task():
url = "http://www.baidu.com"
d = getPage(url.encode('utf-8'))
d.addCallback(response)
yield d
url = "http://www.baidu.com"
d = getPage(url.encode('utf-8'))
d.addCallback(response)
yield d def done(*args,**kwargs):
reactor.stop() li = []
for i in range(10):
d = task()
li.append(d)
# DeferredList也属于defer的对象,也会转交给reactor接手
dd = defer.DeferredList(li)
# 给它增加了一个回调函数
dd.addBoth(done) reactor.run()

二、自定义爬虫包

from twisted.internet import reactor  # 事件循环(终止条件,所有的socket都已经移除)
from twisted.web.client import getPage # socket对象(如果下载完成,自动从时间循环中移除...)
from twisted.internet import defer # defer.Deferred 特殊的socket对象 (不会发请求,手动移除) # 自定义一个Request 类
class Request(object):
def __init__(self, url, callback):
"""
初始化接受url和callback回调函数
:param url: 请求的url
:param callback: 获取内容后的callback
"""
self.url = url
self.callback = callback # 响应对象
class HttpResponse(object):
def __init__(self, content, request):
"""
初始化相应内容
:param content: 下载 下来的响应的content
:param request: response对应的request
"""
# 响应的内容
self.content = content
# 响应的请求
self.request = request
# response对应的request
self.url = request.url
# 将内容转换为文本
self.text = str(content, encoding='utf-8') class ChoutiSpider(object):
"""
初始化顶一个小蜘蛛
"""
name = 'chouti' # 蜘蛛一开始的执行方法
def start_requests(self):
start_url = ['http://www.baidu.com', 'http://www.bing.com', ]
for url in start_url:
yield Request(url, self.parse) # 收到response后的解析函数
def parse(self, response):
print(response) # response是下载的页面
yield Request('http://www.cnblogs.com', callback=self.parse) import queue
# 这里是调度器
Q = queue.Queue() # 定义了一个引擎类
class Engine(object):
def __init__(self):
# 引擎关闭
self._close = None
# 最大的请求数
self.max = 5
# 正在爬的请求
self.crawlling = [] # 拿着相应的回调函数
def get_response_callback(self, content, request):
""" :param content: 响应的content
:param request: 响应对应的request
:return:
"""
# 一旦执行回调函数,就可以从调度中拿走这个请求
self.crawlling.remove(request)
# 将内容封装成 一个 HttpResponse对象
rep = HttpResponse(content, request)
# 调用请求时的回调函数,将封装的HttpResponse传递进去
result = request.callback(rep)
import types
# 查看回调函数是否继续返回迭代器对象
if isinstance(result, types.GeneratorType):
# 将回调函数 新的请求放到调度器
for req in result:
Q.put(req) # 从调度器取请求,执行,下载,并控制最大并发数
def _next_request(self):
"""
去取request对象,并发送请求
最大并发数限制
:return:
"""
print(self.crawlling, Q.qsize())
# 如果调度器的长度为0,而且处于正在爬取的数目也为 0 ,那么就说明该关闭了
if Q.qsize() == 0 and len(self.crawlling) == 0:
# 直接调用 defer.Deferred().callback(None)就会关闭defer
self._close.callback(None)
return # 如果正在爬取的数目超过了最大的并发限制,直接返回
if len(self.crawlling) >= self.max:
return
# 如果没有达到并发限制,就执行以下内容
while len(self.crawlling) < self.max:
try:
# 从 调度器 取一个请求 任务
req = Q.get(block=False)
# 把拿到的请求放到 正在爬取的列表中
self.crawlling.append(req)
# 获取相应的页面
d = getPage(req.url.encode('utf-8'))
# 页面下载完成,get_response_callback,调用用户spider中定义的parse方法,并且将新请求添加到调度器
d.addCallback(self.get_response_callback, req)
# 未达到最大并发数,可以再去调度器中获取Request
# 继续给d添加回调函数,这个回调函数可以是匿名函数
d.addCallback(lambda _: reactor.callLater(0, self._next_request))
except Exception as e:
print(e)
return @defer.inlineCallbacks
def crawl(self, spider):
# 将start_requests包含的生成器,初始Request对象添加到调度器
start_requests = iter(spider.start_requests())
while True:
try:
# 拿到每个request,放到调度器中
request = next(start_requests)
Q.put( request)
except StopIteration as e:
break # 去调度器中取request,并发送请求
# self._next_request() reactor.callLater(0, self._next_request)
# 初始化self._close
self._close = defer.Deferred()
yield self._close # 初始化一个抽屉爬虫
spider = ChoutiSpider() _active = set()
# 实例化一个引擎对象
engine = Engine() # 引擎对象 调用 crawl方法,运行指定的spider
d = engine.crawl(spider) # 将crawl方法放到set中
_active.add(d)
# 实例化一个DeferredList,将_active 内容放进去,返回一个defer.Deferred()对象,若defer.Deferred()被关闭,dd就为空
dd = defer.DeferredList(_active) # 一旦dd里面为空,就调用reactor.stop()方法
dd.addBoth(lambda a: reactor.stop()) # 让它run起来
reactor.run()

Scrapy源码研究前戏的更多相关文章

  1. OAuth2学习及DotNetOpenAuth部分源码研究

    OAuth2学习及DotNetOpenAuth部分源码研究 在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2. 一.什么是OAuth2 OAuth是 ...

  2. Scrapy源码学习(一)

    用Scrapy已经有一段时间了,觉得该是看一下源码的时候了.最开始用的时候还是0.16的版本,现在稳定版已经到了0.18.结合使用Scrapy的过程,先从Scrapy的命令行看起. 一.准备 下载源代 ...

  3. Android开源项目 Universal imageloader 源码研究之Lru算法

    https://github.com/nostra13/Android-Universal-Image-Loader universal imageloader 源码研究之Lru算法 LRU - Le ...

  4. zepto源码研究 - zepto.js - 1

    简要:网上已经有很多人已经将zepto的源码研究得很细致了,但我还是想写下zepto源码系列,将别人的东西和自己的想法写下来以加深印象也是自娱自乐,文章中可能有许多错误,望有人不吝指出,烦请赐教. 首 ...

  5. dubbo源码研究(一)

    1. dubbo源码研究(一) 1.1. dubbo启动加载过程 我们知道,现在流行注解方式,用spring管理服务,dubbo最常用的就是@Reference和@Service了,那么我首先找到这两 ...

  6. 【JavaScript】$.extend使用心得及源码研究

    最近写多了js的面向对象编程,用$.extend写继承写得很顺手.但是在使用过程中发现有几个问题. 1.深拷贝 $.extend默认是浅拷贝,这意味着在继承复杂对象时,对象中内嵌的对象无法被拷贝到. ...

  7. underscore.js源码研究(8)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  8. underscore.js源码研究(7)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

  9. underscore.js源码研究(6)

    概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...

随机推荐

  1. TileMode(平铺模式) 枚举的成员:

    TileMode(平铺模式) 枚举的成员:   成员名称 说明   FlipX 与 Tile 相同,只不过图块的交替列被水平翻转. 基本图块本身不翻转.   FlipXY FlipX 和 FlipY ...

  2. 新男人八题---AStringGame

    终于完成进度男人1/8,为了这题学了sam= = 题意先有一个串,n个子串,两个人轮流每次在子串上加字符,要求加完后还是原串的子串,最后不能加的就是输者,求赢的人 解法:sam之后在构造的状态图上跑s ...

  3. nyoj20——有向无环图深搜模板

    吝啬的国度 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 在一个吝啬的国度里有N个城市,这N个城市间只有N-1条路把这个N个城市连接起来.现在,Tom在第S号城市, ...

  4. Linux服务器中木马(肉鸡)手工清除方法(转)

    首先剧透一下后门木马如下: (当然这是事后平静下来后慢慢搜出来的,那个时候喝着咖啡感觉像个自由人) 木马名称 Linux.BackDoor.Gates.5 http://forum.antichat. ...

  5. Idea_01_安装与激活

    一.前言 二.安装 1.下载 https://www.jetbrains.com/idea/ 2.安装 默认安装即可 三.激活 Idea激活有如下两种方式 Activation code Lisenc ...

  6. C++设计模式之-外观模式

    意图: 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一系统更加容易使用. 适用性: 1.在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典的三层架构,就需要 ...

  7. Java虚拟机读写其他进程的数据--Process对象

    使用Runtime对象的exec()方法可以运行平台上的其他程序,该方法产生一个Process对象,Process对象代表由该Java程序启动的子进程. Process类提供了3个方法,用于让程序和其 ...

  8. c# Request.Files["xx"]取不到值解决办法

  9. 【SQL查询】正则表达式匹配字符串

    1. 元字符说明 元字符 含义 ^ 匹配输入字符串的开始位置. $ 匹配输入字符串的结尾位置. * 匹配前面的字符零次或多次. + 匹配前面的字符一次或多次. ? 匹配前面的字符零次或一次. . 匹配 ...

  10. SQL Server(MSSQLSERVER)启动失败,提示“请求失败或服务未及时响应

    1.SQL Server(MSSQLSERVER)启动失败,提示“请求失败或服务未及时响应. --------------------------- SQL Server 配置管理器 -------- ...