一、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. 对reducers 理解

    var reducers = { totalInEuros : function(state, item) { return state.euros += item.price * 0.8974243 ...

  2. phalcon: 项目地址/P(.*), 项目地址/Pbaidu 与 路由

    phalcon: 项目地址/P(.*) 与 路由 有一个项目地址:因客户渠道不同,带的参数也不相同.当时想到的是伪静态规则,但是发现自己没有那么强大.该走phalcon路由规则,地址如下: www.x ...

  3. IdentityServer4在Asp.Net Core中的应用(三)

    今天的内容是授权模式中的简化模式,还是先看以下授权流程图: 在这种模式中我们将与OpenID结合使用,所以首先我们要了解OpenID和OAuth的区别,关于他们的区别,在我上一篇博客<理解Ope ...

  4. Linux命令详解-cal

    cal命令可以用来显示公历(阳历)日历.公历是现在国际通用的历法,又称格列历,通称阳历."阳历"又名"太阳历",系以地球绕行太阳一周为一年,为西方各国所通用,故 ...

  5. flask学习(六):URL传参

    1. 参数的作用:可以在相同的URL,但是指定不同的参数,来加载不同的数据 例如:简书上每一篇文章前面的URL相同,只是后面的参数不同 2. 在flask中如何使用参数: 注意: 1) 参数需要放在两 ...

  6. 1-16-1 LVM管理和ssm存储管理器使用&磁盘配额

    大纲: 1-1- LVM逻辑卷的管理 1-2- SSM管理工具的使用 1-3- 磁盘配额技巧 ====================================== 问题描述: 当我们需要在一个 ...

  7. DNS污染——domain name的解析被劫持了返回无效的ip

    看下dns污染: bash-3.2$ dig twitter.com +trace ; <<>> DiG 9.10.6 <<>> twitter.com ...

  8. jquery动态创建元素 div元素随垂直滚动条位置变化置顶显示

    刚打开页面效果 拖动滑动条之后效果 页面代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" & ...

  9. easyui(1)

    使用Easyui1.引入必要的文件 1).jquery核心库 2).easyui核心库 3).easyui中文提示信息 4).自己开发的js文件 5).easyui核心UI文件css 6).easyu ...

  10. 一道简单的JavaScript面试题

    好久没更新博客了,随便写点东西吧. 自从工作之后就特别忙,忙的过程中有时候挺迷茫的,可能是大多数时候写的都是简单的业务代码,很久没好好充电了.最近一直在零碎的上班路上等电梯时间里面学习<图解HT ...