07-爬虫的多线程调度 | 01.数据抓取 | Python

07-爬虫的多线程调度

郑昀 201005 隶属于《01.数据抓取》小节

一般让爬虫在一个进程内多线程并发,有几种方法:

Stackless :Stackless Python是Python的一个增强版本。Stackless Python修改了Python的代码,提供了对微线程的支持。微线程是轻量级的线程,与前边所讲的线程相比,微线程在多个线程间切换所需的时间更多,占用资源也更少。

Twisted :主要利用 Twisted 中的异步编程能力。如 addCallback , callLater , defer.succeeddeferToThread , callFromThread 和 callInThread 等等。

threading 和 Queue :这都是 Python 原生库。从这个库可以衍生出很多线程池的第三方实现。如2003年的一个实现。比如 Christopher Arndt 的一个版本。比如2010年的一个实现

greenlet 和 eventlet :greenlet 不是一种真正的并发机制,而是在同一线程内,在不同函数的执行代码块之间切换,实施“你运行一会、我运行一会”,并且在进行切换时必须指定何时切换以及切换到哪。粗糙来讲,greenlet是“阻塞了我就先干点儿别的,但是程序员得明确告诉greenlet能先干点儿啥以及什么时候回来”。

 

(注:关于 Python Threading 代码片段,参考:http://code.activestate.com/recipes/langs/python/tags/meta:requires=threading/ ,有很多例子)

 

Twisted 的 callInThread 和 callFromThread 区别

这两个函数的定义在  IReactorThreads 的文档里。

 

Method callInThread :

Run the callable object in a separate thread.

Method callFromThread :

Cause a function to be executed by the reactor thread.

Use this method when you want to run a function in the reactor's thread from another thread. Calling callFromThread should wake up the main thread (where reactor.run() is executing) and run the given callable in that thread.

If you're writing a multi-threaded application the callable may need to be thread safe, but this method doesn't require it as such. If you want to call a function in the next mainloop iteration, but you're in the same thread, use callLater with a delay of 0.

 

也就是说,reactor.callFromThread 是在由 reactor.run() 激发的主消息循环(main event loop)中执行,所以也就能被 reactor.stop() 终止执行。甚至可以通过:

reactor.callFromThread(reactor.stop)

来主动要求主消息循环关闭 reactor 的主线程运行。

callFromThread 有时候比较危险,如果压的任务太多,会阻塞主消息循环,造成其他事件无法得到及时的处理。

 

参考 callInThread 的代码,可以看出它是在 reactor 的一个私有线程池里工作的:

def callInThread(self, _callable, *args, **kwargs):

    if self.threadpool is None:

        self._initThreadPool()

    self.threadpool.callInThread(_callable, *args, **kwargs)

所以,我们可以通过

from twisted.internet import reactor

reactor.suggestThreadPoolSize(15)

来设置该线程池的大小。默认最小是5个线程,最大10个(the default reactor uses a minimum size of 5 and a maximum size of 10)。

 

这里有两个问题:

1、如何通知 callInThread 执行任务的线程退出呢,如何确保线程池内的工作线程安全退出呢?

2、如果让工作线程去某网站抓取页面,由于 TCP/IP 的不确定性,可能该工作线程挂起,长时间不返回。如果线程池内的每一个线程被这样耗尽,没有空闲线程,就相当于抓取全部停止了。某个线程或许会因请求超时而退出,但这也未必可靠。一般通过代码:

import timeoutsocket 
timeoutsocket.setDefaultSocketTimeout(120)

设置 socket 超时时间,但有时候就是会莫名其妙地挂住线程。

 

threads.deferToThread和reactor.callInThread的区别

twisted.internet.threads.deferToThread 与 callInThread 一样,默认用 reactor.getThreadPool() 所开辟的线程池。它调用这个线程池的 threadpool.callInThreadWithCallback 方法,实际效果和 reactor.callInThread 一样。区别只是 deferToThread 可以返回一个deferred对象,从而允许你设定回调函数。

示范代码:

def finish_success(request): 
    pass 
threads.deferToThread(parseData, body).addCallback(lambda x: finish_success(request))

 

twisted 的 defer.succeed

twisted还提供了一个简易办法

twisted. internet.defer.succeed(result)

Return a Deferred that has already had '.callback(result)' called.

This is useful when you're writing synchronous code to an asynchronous interface: i.e., some code is calling you expecting a Deferred result, but you don't actually need to do anything asynchronous. Just return defer.succeed(theResult).

代码示范参考 howto 文档的 Returning Deferreds from synchronous functions 

还可以参考 《Twisted 服务器开发技巧(1) - 将性能优化到底》中的示范。

defer.succeed 说白了就是为了让某函数 A 返回一个 Deferred 对象,从而让 A.addCallback(…) 异步触发成为现实。

时间: 2024-12-21 22:54:19

07-爬虫的多线程调度 | 01.数据抓取 | Python的相关文章

05-访问超时设置 | 01.数据抓取 | Python

05-访问超时设置 郑昀 201005 隶属于<01.数据抓取>小节 设置 HTTP 或 Socket 访问超时,来防止爬虫抓取某个页面时间过长.   pycurl 库的调用中,可以设置超时时间: c.setopt(pycurl.CONNECTTIMEOUT, 60)   在 Python 2.6 版本下,httplib 库由于有如下构造函数: class HTTPConnection:     def __init__(self, host, port=None, strict=None, 

10-穿墙代理的设置 | 01.数据抓取 | Python

10-穿墙代理的设置 郑昀 201005 隶属于<01.数据抓取>小节   我们访问 Twitter 等被封掉的网站时,需要设置 Proxy . 1.使用HTTP Proxy 下面是普通HTTP Proxy的设置方式: 1.1.pycurl 的设置 _proxy_connect = "http://127.0.0.1:1984" c = pycurl.Curl() - c.setopt(pycurl.PROXY, _proxy_connect)   1.2.urllib2

《用Python写网络爬虫》——第2章 数据抓取 2.1 分析网页

第2章 数据抓取 在上一章中,我们构建了一个爬虫,可以通过跟踪链接的方式下载我们所需的网页.虽然这个例子很有意思,却不够实用,因为爬虫在下载网页之后又将结果丢弃掉了.现在,我们需要让这个爬虫从每个网页中抽取一些数据,然后实现某些事情,这种做法也被称为抓取(scraping). 首先,我们会介绍一个叫做Firebug Lite的浏览器扩展,用于检查网页内容,如果你有一些网络开发背景的话,可能已经对该扩展十分熟悉了.然后,我们会介绍三种抽取网页数据的方法,分别是正则表达式.Beautiful Sou

php爬虫:知乎用户数据爬取和分析

背景说明:小拽利用php的curl写的爬虫,实验性的爬取了知乎5w用户的基本信息:同时,针对爬取的数据,进行了简单的分析呈现.demo 地址 php的spider代码和用户dashboard的展现代码,整理后上传github,在个人博客和公众号更新代码库,程序仅供娱乐和学习交流:如果有侵犯知乎相关权益,请尽快联系本人删除. 无图无真相 移动端分析数据截图 pc端分析数据截图 整个爬取,分析,展现过程大概分如下几步,小拽将分别介绍 curl爬取知乎网页数据 正则分析知乎网页数据 数据数据入库和程序

百万级别知乎用户数据抓取与分析之PHP开发_php实例

这次抓取了110万的用户数据,数据分析结果如下: 开发前的准备 安装Linux系统(Ubuntu14.04),在VMWare虚拟机下安装一个Ubuntu: 安装PHP5.6或以上版本: 安装curl.pcntl扩展. 使用PHP的curl扩展抓取页面数据 PHP的curl扩展是PHP支持的允许你与各种服务器使用各种类型的协议进行连接和通信的库. 本程序是抓取知乎的用户数据,要能访问用户个人页面,需要用户登录后的才能访问.当我们在浏览器的页面中点击一个用户头像链接进入用户个人中心页面的时候,之所以

数据抓取的艺术(二):数据抓取程序优化

      续前文:<数据抓取的艺术(一):Selenium+Phantomjs数据抓取环境配置>. 程序优化:第一步开始: for i in range(startx,total):     for j in range(starty,total):         BASE_URL = createTheUrl([item[i],item[j]])         driver.get(BASE_URL)         driver = webdriver.PhantomJS()    

一淘网的数据抓取和插件屏蔽涉及的法律问题

今天探讨B2C购物网站京东商城和阿里巴巴集团旗下的比价网站一淘网的数据抓取和插件屏蔽涉及的法律问题.说明下,笔者和两网站均无业务关系和联系.先介绍背景: 2011年11月,京东商城采取技术措施,屏蔽一淘网搜索引擎,一淘网随后在微博表示不再直接抓取京东商城的价格数据,但近期又在京东商城与苏宁的价格战中实时公布京东商城和其他购物网站商品价格比较.近日,京东商城又在其网站屏蔽了一淘网的浏览器插件,该插件向用户实时提供包括京东商城在内的各大购物网站同款商品价格,一淘网方面表示京东商城是在破坏其软件运行.

python-模拟登录post数据抓取不到

问题描述 模拟登录post数据抓取不到 想用python写的爬虫模拟登录知乎,但是登录的时候用chrome抓取post数据的页面一 闪而过,看不到需要post的数据,请问各位大神这是怎么回事,要怎么才能抓取到 post的数据 解决方案 用fiddler,它可以记录所有的http通讯.你可以回放. 解决方案二: 模拟Post/get提交数据 并抓取返回数据模拟Post/get提交数据 并抓取返回数据Webbrowser截获POST数据和模拟POST提交数据 解决方案三: httpwatch fid

数据抓取的艺术(三):抓取Google数据之心得

 本来是想把这部分内容放到前一篇<数据抓取的艺术(二):数据抓取程序优化>之中.但是随着任务的完成,我越来越感觉到其中深深的趣味,现总结如下:    (1)时间      时间是一个与抓取规模相形而生的因素,数据规模越大,时间消耗往往越长.所以程序优化变得相当重要,要知道抓取时间越长,出错的可能性就越大,这还不说程序需要人工干预的情境.一旦运行中需要人工干预,时间越长,干预次数越多,出错的几率就更大了.在数据太多,工期太短的情况下,使用多线程抓取,也是一个好办法,但这会增加程序复杂度,对最终数