一、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. spring mvc: rss(xml)输出

    准备: rss包插件 Rome 库及其依赖项rome-utils,jdom和slf4j <!-- rss源依赖 --> <!-- https://mvnrepository.com/ ...

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

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

  3. Svn Replacement For Git Stash

    svn 实现git stash类似的功能 % svn diff > WorkInProgress.txt % svn revert -R . <make changes> % svn ...

  4. 【转至nmap】nc命令

    什么是nc nc是netcat的简写,有着网络界的瑞士军刀美誉.因为它短小精悍.功能实用,被设计为一个简单.可靠的网络工具 nc的作用 (1)实现任意TCP/UDP端口的侦听,nc可以作为server ...

  5. RocketMQ学习分享

    消息队列的流派 什么是 MQ Message Queue(MQ),消息队列中间件.很多人都说:MQ 通过将消息的发送和接收分离来实现应用程序的异步和解偶,这个给人的直觉是——MQ 是异步的,用来解耦的 ...

  6. table 转实体

    public class Table2Entity<T> where T : class,new() { public static List<T> GetEntitys(Da ...

  7. vue elementui二级联动下拉选项demo

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. ping函数

    #!/bin/bash #note:ping monitor set -u #set -x ping_fun() { d_network= echo -n "input the networ ...

  9. 基于centos的docker安装

    1. 安装需求 内核版本3.10以上 Centos 7以上 64位版本 2. 使用root登录或者具有sudo权限 3. 确保系统是最新的 yum update 4. 添加yum源 tee /etc/ ...

  10. 模式窗体中调用父页面js与非模式化调用非父页面的js方法

    最近项目中使用模式窗体,遇到以下问题记录一下: 模式窗体:你必须关闭该窗体,才能操作其它窗体:比如说,必须按确定或取消,或者按关闭. 非模式窗体:不必关闭该窗体,就可转换到其它窗体上进行操作. 一:非 ...