`
xbcoil
  • 浏览: 125934 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

[转]gcrawler:一个基于gevent的简单爬虫框架

阅读更多

引子

以前用scrapy写过一些简单的爬虫程序。但是我的需求实在太简单了,用scrapy有点大材小用,而且过于强大的缺点就是用起来太复杂,加上我也不太喜欢twisted——用各种回调实现的异步框架用起来还是不太自然。

前一阵接触了一下gevent (不知道为什么这样一个纯技术网站会在墙外),且不说据说它性能很好,关键是用patch的方式隐含提供异步支持的实现用起来真是太爽了。于是自己写了一个简单的爬虫框架,主要思路是模仿scrapy的。

关于Scrapy

首先来看Scrapy的架构(源于scrapy文档 ):

用户编写的爬虫主要是需要实现Spider和Item Pipeline两部分,Scheduler和Downloader部分是由Scrapy提供,并且整个程序由Scrapy Engine所驱动。

工 作流程是:程序启动后Scrapy Engine从Scheduler取得网址,然后通过Downloader去下载页面交给Spider处理,Spider分析页面后根据情况决定是继续抓 下一级锭接的页面(返回一个Scrapy Request)还是返回数据(返回一个Item List),Spider返回的数据(Item List)将被汇总交给Item Pipeline处理,比如存成文件或存入数据库什么的。

之所以要重复一下Scrapy的工作流程,是因为gcrawler也是模仿这个流程来做的。

Scrapy的局限及gcrawler的特点

不 过在说gcrawler之前,还是先谈一下我的需求中用Scrapy实现不太方便的地方。最主要的是我的应用中虽然也是用HTTP协议去抓取内容,但是有 很多额外的要求,比如有时需要POST,有时需要BasicAuth,有时还需要处理一些特定的HTTP Header字段,这些虽然有Download Middleware也可以实现,但很不自然——一个简单的HTTP请求操作被分割得支离破碎,对于代码的日后维护会造成很大的麻烦。

比 如要用feedparser读RSS,因为feedparser是下载解析一体的实现,这时Scrapy就不太方便,虽然也可以拆开实现:用Scrapy 下载再在Spider里用feedparser,但显然这种麻烦不是必要的。再比如说需要用OAuth进行REST API调用的话,用Scrapy的 Download Middleware我还真不知道要怎么做才好。

因此,gcrawler与scrapy最大的不同就是 Downloader的功能也将由用户实现(放在类似Scrapy的Spider中),用户可以自由控制下载操作的种种细节。乍一看似乎这样会增加用户的 工作量,但实际上是简化了开发。比如前面说的feedparser或是OAuth调用,都可以直接实现,不用考虑分拆到不同的地方去做。那么由用户来实现 下载工作会不会影响性能呢?Scrapy的一大优势就在于利用Twisted这个异步网络框架提供了网络访问的高性能。答案是不会,因为gevent提供 了更为简单和高效的实现方式。二者的差异在于:

因为Scrapy是基于Twisted,而它是一个基于回调的异步框架,这就意味着用它来写 Downloader固然可以得到高性能(托异步的福),但同时带来额外的复杂性(拜回调所赐)。所以Scrapy内部实现了这个高性能的 Downloader,在提代高性能的同时屏蔽了这种复杂性,但代价就是前面所说的,用户自定义操作需要通过Middleware实现,结果还是增加了一 些复杂性,并且使程序结构变得不太清晰。

而如前面所说,gevent通过patch的方式提供了隐含的异步支持,这样用户编写Downloader就成了简单的貌似“同步阻塞操作”,于是结构清晰与高性能得以兼得。个人根据gevent与twisted的性能比较 猜测,能有与scrapy相当甚至可能更好的性能(纯猜测,待求证)。这是gevent相对twisted的一个巨大的优点。

gcrawler的使用

gcrawler 的使用很简单:用户需要实现一个spider类,提供三个成员函数:scheduler, worker, pipeline。其中scheduler是一个生成器,用yield依次返回请求项(比如URL),worker相当于Scrapy里的 Downloader+Spider,用于下载页面内容并解析,pipeline与Scrapy的item pipeline类似,都是用于存储最终结果用。

scheduler和pipeline没什么好说的,重点在worker里。因为 gevent有隐含异步支持,所以这里不再需要像Scrapy里那样下载一个页面解析完后需要把请求重新发回Scheduler进行调度,而是可以直接 继续下载下一级链接的页面继续解析,整个流程会清晰很多,但又不会降低性能。当然要实现像Scrapy那样的方式也可以,只要在worker里把下一级页 面链接再传递给scheduler即可。如果worker没有什么结果需要保存(比如上述把链接交给scheduler作下一步处理的情况),只需要简单 返回一个None即可,pipeline会自动略过的。

一个简单的下载页面的例子如下:

 

  1. from gcrawler import GCrawler  
  2. import urllib2  
  3. import traceback  
  4. urls=['http://www.163.com''http://www.qq.com''http://www.sina.com.cn''http://www.sohu.com''http://www.yahoo.com''http://www.baidu.com''http://www.google.com''http://www.microsoft.com']  
  5. class DummySpider:  
  6.     def scheduler(self):  
  7.         for u in urls:  
  8.             yield u  
  9.     def worker(self, item):  
  10.         try:  
  11.             f = urllib2.urlopen(item)  
  12.             data = f.read()  
  13.             f.close()  
  14.             return (item, len(data))  
  15.         except:  
  16.             traceback.print_exc()  
  17.             return None  
  18.     def pipeline(self, results):  
  19.         for r in results:  
  20.             print "Downloaded : %s(%s)" % r  
  21. spider = DummySpider()  
  22. crawler = GCrawler(spider, workers_count = 3)  
  23. crawler.start()  

 

当然,为了方便实现类似Scrapy的应用,gcrawler提供了一个默认的Downloader,所以上面的例子可以简化如下,只需要提供urls,并实现pipeline保存结果即可:
  1. from gcrawler import GCrawler, Downloader  
  2. import urllib2  
  3. import traceback  
  4.   
  5. urls=['http://www.163.com''http://www.qq.com''http://www.sina.com.cn''http://www.sohu.com''http://www.yahoo.com''http://www.baidu.com''http://www.google.com''http://www.microsoft.com']  
  6.   
  7. class DummyDownloader(Downloader):  
  8.     def pipeline(self, results):  
  9.         for r in results:  
  10.             r['datasize'] = len(r['data'])  
  11.             print "Data fetched : %(url)s(%(datasize)s)" % r  
  12.   
  13. spider = DummyDownloader(urls)  
  14. crawler = GCrawler(spider, workers_count = 3)  
  15. crawler.start()  
另外还提供了一个retryOnURLError的decorator,用于自动重试,用法参见源码。

gcrawler的源码

代码在这里下载:1.5KB (好 吧,我的个人技术网站在春节前也可耻滴被墙外了,大家自己想办法去下载吧不要问我。要是连这都不会,还是不要在中国搞技术了,因为太多的先进技术被墙 外)。License还没想好用什么,先标一个copyright,反正在中国标什么License结果都一样的。等稳定一段时间发google code上去再确定用什么License吧。

附录:gevent的安装

在使用这个框架之前首先需要安装gevent,而 gevent又依赖libevent和greenlet。本来这事不复杂,gevent的文档里都有说,但可耻滴如前面所说,它的官网在中国大陆是“被不 存在的”,所以我还是在这里说一下具体安装方法,并向有关部门坚决竖中指,你们是中国技术创新进步的最大阻力!

Ubuntu 10.04:

apt-get install python-dev libevent-1.4-2 libevent-dev
easy_install greenlet
easy_install gevent

Debian 5:

#先在sources.list里增加一个sid源,比如:
#deb http://mirrors.163.com/debian/ sid main
apt-get install python-gevent
#如果还缺什么则参考ubuntu

FreeBSD 8:

cd /usr/ports/devel/py-gevent
make install clean

Mac OS X 10.6.x:

#当然需要安装XCode和HomeBrew(或其它方式安装libevent)
brew install libevent
easy_install greenlet
easy_install gevent

Windows……这个自己Google去吧,友情建议还是不要在windows下浪费生命为好。

分享到:
评论

相关推荐

    gcrawler:使用gevent的轻量级爬虫框架

    `gcrawler` 是一个专为Python设计的轻量级爬虫框架,它充分利用了gevent库的协程特性,实现高效的网页抓取。gevent是一个Python网络编程库,通过使用greenlet进行并发处理,能够以非阻塞的方式执行I/O密集型任务,极...

    基于gevent的mini-scrapy爬虫框架.zip

    gevent是一个基于cooperative multitasking(协作式多任务)的Python网络库,它通过greenlet(轻量级线程)实现了高效的并发。 1. **gvent简介** - gevent是Python的一个库,用于编写高度并发的网络应用。它通过...

    python2-gevent-1.1.2-2.el7.x86_64.rpm

    官方离线安装包,亲测可用。使用rpm -ivh [rpm完整包名] 进行安装

    mini-scrapy:基于gevent的mini-scrapy爬虫框架

    Gevent是一个基于libev事件库的Python库,用于编写异步、非阻塞I/O程序。它通过使用greenlet(轻量级线程)和协同调度机制,使得Python代码可以像执行同步任务一样编写异步代码,极大地提高了程序的执行效率。 **3....

    淘宝爬虫原型,基于gevent.zip

    gevent是一个基于cooperative multitasking(协作式多任务)的Python库,特别适合处理I/O密集型的任务,如网络爬虫。在爬虫领域,它能显著提高爬取速度,因为它可以同时处理多个网络请求,而不是依次执行。 描述中...

    基于Flask和Gevent的后端框架LuPureApi.zip

    这是一个基于Flask和Gevent的Api后端框架,名为LuPureApi。LuPureApi设计用于开发高并发的api后端应用。它提供了基于Flask的Web框架和Gevent的异步处理能力,使得开发者可以快速地构建高性能的Api服务。项目版本记录...

    python开发,基于gunicorn+gevent+restful框架,标准的高并发flask项目,可用于高并发应用开发模板

    python开发,基于gunicorn+gevent+restful框架,标准的高并发flask项目,可用于高并发应用开发模板 flask高并发标准项目框架 启动服务: gunicorn -c gun.py manage:app 新增接口方式: 1.app/_apis/firstApi....

    solt_sftp:基于Paramiko的Gevent SFTP服务器

    这个 SFTP 实现基于 Paramiko,但采用了思想和功能代码,提出了一个基于 gevent 协程而不是线程的解决方案。 此 SFTP 实现仅支持公钥身份验证,即使添加其余的安全选项也只是代码问题。 Redis 用于存储用户配置,...

    Python-Jolla是一个纯API服务器框架基于gevent

    Jolla框架的核心是gevent,这是一个基于greenlet的并发库。greenlet是一种轻量级线程,它可以在单个操作系统线程内实现协程(coroutines)的调度。gevent通过monkey-patching(即在运行时替换标准库中的阻塞I/O操作...

    python Gevent程序员指南.pdf

    - **定义**:Gevent 是一个基于 libev 的并发库,为 Python 提供了一个简洁高效的 API 来处理并发和网络相关的任务。 - **目标用户**:本书面向具备中级 Python 基础的程序员,不需要对并发编程有深入了解即可开始...

    gevent 1.0.2

    `gevent 1.0.2`是该库的一个版本,它基于`libev`或`libuv`事件循环,这两个都是高性能的事件库,用于处理异步I/O操作。 **1. 协程与greenlet** 协程是一种用户级的并发机制,它允许在一个程序中同时执行多个任务,...

    cyberbot:基于gevent的轻量级批量扫描框架

    Cyber​​bot是一个基于gevent的轻量级批量扫描框架。 安全研究人员使用Cyber​​bot框架轻松地进行批处理扫描,从而编写了许多示例PoC来查找漏洞。 安装 基于库的Cyber​​bot框架,必须首先安装gevent库: pip ...

    gevent-21.12.0-cp310-cp310-win_amd64.whl.zip

    本文将围绕“gevent-21.12.0-cp310-cp310-win_amd64.whl.zip”这个压缩包,深入讲解其中的核心组件——gevent,一个强大的基于协程的并发库。首先,让我们来了解一下什么是whl文件和gevent。 **whl文件** 是Python...

    Python使用grequests(gevent+requests)并发发送请求过程解析

    5. grequests的引入:grequests是基于gevent和requests的结合体,它允许开发者以非常简单的方式来实现并发请求。grequests在内部使用gevent库来实现非阻塞IO操作,从而达到并发请求的效果。 6. grequests的基本使用...

    gevent-socketio:gevent-socketio的官方存储库

    该项目的一个目标是提供一个基于gevent的API,该API可以跨不同的基于WSGI的Web框架(金字塔,Pylons,Flask,web2py,Django等)使用。 在您的框架中gevent-socketio只需约3行代码。 注意:您需要使用gevent python...

    gfirefly firefly-gevent是firefly的gevent版本基于coroutine的python网络开发框架

    gevent就是一个基于coroutine的python网络开发框架。协程是一种并发模型,但不同于thread和callback,它的所有task都是可以在一个线程里面执行,然后可以通过在一个task里面主动放弃执行来切换到另一个task执行,它...

    Python-美女写真图爬虫gevent版

    gevent是一个基于greenlet的协程库,它可以实现非阻塞的IO操作,从而在单个线程内实现并发执行。greenlet是轻量级的线程,它们共享同一内存空间,切换速度快,消耗资源少。gevent通过 monkey-patching 技术,可以...

    gevent文档

    在gevent中创建并发任务非常简单,可以通过`gevent.spawn()`函数启动一个greenlet,然后使用`gevent.joinall()`来等待所有任务完成。以下是一个简单的示例: ```python from gevent import monkey; monkey.patch_...

Global site tag (gtag.js) - Google Analytics