在Python3中使用asyncio库进行快速数据抓取的教程_python

web数据抓取是一个经常在python的讨论中出现的主题。有很多方法可以用来进行web数据抓取,然而其中好像并没有一个最好的办法。有一些如scrapy这样十分成熟的框架,更多的则是像mechanize这样的轻量级库。DIY自己的解决方案同样十分流行:你可以使用requests、beautifulsoup或者pyquery来实现。

方法如此多样的原因在于,数据“抓取”实际上包括很多问题:你不需要使用相同的工具从成千上万的页面中抓取数据,同时使一些Web工作流自动化(例如填一些表单然后取回数据)。我喜欢DIY的原因在于其灵活性,但是却不适合用来做大量数据的抓取,因为需要请求同步,所以大量的请求意味着你不得不等待很长时间。

在本文中,我将会为你展示一个基于新的异步库(aiohttp)的请求的代替品。我使用它写了一些速度的确很快的小数据抓取器,下面我将会为你演示是如何做到的。

asyncio的基本概念
asyncio是在python3.4中被引进的异步IO库。你也可以通过python3.3的pypi来安装它。它相当的复杂,而且我不会介绍太多的细节。相反,我将会解释你需要知道些什么,以利用它来写异步的代码。

简而言之,有两件事情你需要知道:协同程序和事件循环。协同程序像是方法,但是它们可以在代码中的特定点暂停和继续。当在等待一个IO(比如一个HTTP请求),同时执行另一个请求的时候,可以用来暂停一个协同程序。我们使用关键字yield from来设定一个状态,表明我们需要一个协同程序的返回值。而事件循环则被用来安排协同程序的执行。

关于asyncio还有很多很多,但是以上是我们到目前为止需要知道的。可能你还有些不清楚,那么让我们来看一些代码吧。

aiohttp
aiohttp是一个利用asyncio的库,它的API看起来很像请求的API。到目前为止,相关文档还不健全。但是这里有一些非常有用的例子。我们将会演示它的基本用法。

首先,我们会定义一个协同程序用来获取页面,并打印出来。我们使用 asyncio.coroutine将一个方法装饰成一个协同程序。aiohttp.request是一个协同程序,所以它是一个可读方法,我们需要使用yield from来调用它们。除了这些,下面的代码看起来相当直观:
 

@asyncio.coroutine
def print_page(url):
  response = yield from aiohttp.request('GET', url)
  body = yield from response.read_and_close(decode=True)
  print(body)

如你所见,我们可以使用yield from从另一个协同程序中调用一个协同程序。为了从同步代码中调用一个协同程序,我们需要一个事件循环。我们可以通过asyncio.get_event_loop()得到一个标准的事件循环,之后使用它的run_until_complete()方法来运行协同程序。所以,为了使之前的协同程序运行,我们只需要做下面的步骤:
 

loop = asyncio.get_event_loop()
loop.run_until_complete(print_page('http://example.com'))

一个有用的方法是asyncio.wait,通过它可以获取一个协同程序的列表,同时返回一个将它们全包括在内的单独的协同程序,所以我们可以这样写:
 

loop.run_until_complete(asyncio.wait([print_page('http://example.com/foo'),
                   print_page('http://example.com/bar')]))

另一个是asyncio.as_completed,通过它可以获取一个协同程序的列表,同时返回一个按完成顺序生成协同程序的迭代器,因此当你用它迭代时,会尽快得到每个可用的结果。

数据抓取
现在我们知道了如何做异步HTTP请求,因此我们可以来写一个数据抓取器了。我们仅仅还需要一些工具来读取html页面,我使用了beautifulsoup来做这个事情,其余的像 pyquery或lxml也可以实现。

在这个例子中,我们会写一个小数据抓取器来从海盗湾抓取一些linux distributions的torrent 链路(海盗湾(英语:The Pirate Bay,缩写:TPB)是一个专门存储、分类及搜索Bittorrent种子文件的网站,并自称“世界最大的BitTorrent tracker(BT种子服务器)”,提供的BT种子除了有自由版权的收集外,也有不少被著作人声称拥有版权的音频、视频、应用软件与电子游戏等,为网络分享与下载的重要网站之一–译者注来自维基百科)

首先,需要一个辅助协同程序来获取请求:
 

@asyncio.coroutine
def get(*args, **kwargs):
  response = yield from aiohttp.request('GET', *args, **kwargs)
  return (yield from response.read_and_close(decode=True))

解析部分。本文并非介绍beautifulsoup的,所以这部分我会简写:我们获取了这个页面的第一个磁链。
 

def first_magnet(page):
  soup = bs4.BeautifulSoup(page)
  a = soup.find('a', title='Download this torrent using magnet')
  return a['href']

在这个协同程序中,url的结果通过种子的数量进行排序,所以排名第一的结果实际上是种子最多的:
 

@asyncio.coroutine
def print_magnet(query):
  url = 'http://thepiratebay.se/search/{}/0/7/0'.format(query)
  page = yield from get(url, compress=True)
  magnet = first_magnet(page)
  print('{}: {}'.format(query, magnet))

最后,用下面的代码来调用以上所有的方法。
 

distros = ['archlinux', 'ubuntu', 'debian']
loop = asyncio.get_event_loop()
f = asyncio.wait([print_magnet(d) for d in distros])
loop.run_until_complete(f)

结论
好了,现在我们来到了这个部分。你有了一个异步工作的小抓取器。这意味着多个页面可以同时被下载,所以这个例子要比使用请求的相同代码快3倍。现在你应该可以用相同的方法写出你自己的抓取器了。

你可以在这里找到生成的代码,也包括一些额外的建议。

你一旦熟悉了这一切,我建议你看一看asyncio的文档和aiohttp的范例,这些都能告诉你 asyncio拥有怎样的潜力。

这种方法(事实上是所有手动的方法)的一个局限在于,没有一个独立的库可以用来处理表单。机械化的方法拥有很多辅助工具,这使得提交表单变得十分简单,但是如果你不使用它们,你将不得不自己去处理这些事情。这可能会导致一些bug的出现,所以同时我可能会写一个这样的库(不过目前为止无需为此担心)。

额外的建议:不要敲打服务器
同时做3个请求很酷,但是同时做5000个就不那么好玩了。如果你打算同时做太多的请求,链接有可能会断掉。你甚至有可能会被禁止链接网络。

为了避免这些,你可以使用semaphore。这是一个可以被用来限制同时工作的协同程序数量的同步工具。我们只需要在建立循环之前创建一个semaphore ,同时把我们希望允许的同时请求的数量作为参数传给它既可:
 

sem = asyncio.Semaphore(5)

然后,我们只需要将下面
 

page = yield from get(url, compress=True)

替换成被semaphore 保护的同样的东西。
 

with (yield from sem):
  page = yield from get(url, compress=True)

这就可以保证同时最多有5个请求会被处理。

额外建议:进度条
这个东东是免费的哦:tqdm是一个用来生成进度条的优秀的库。这个协同程序就像asyncio.wait一样工作,不过会显示一个代表完成度的进度条。
 

@asyncio.coroutine
def wait_with_progress(coros):
  for f in tqdm.tqdm(asyncio.as_completed(coros), total=len(coros)):
    yield from f

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索python
, 爬虫
asyncio
python asyncio、python3 asyncio、python asyncio详解、python3.5 asyncio、python asyncio 教程,以便于您获取更多的相关知识。

时间: 2024-12-09 12:32:10

在Python3中使用asyncio库进行快速数据抓取的教程_python的相关文章

在Python中使用cookielib和urllib2配合PyQuery抓取网页信息_python

刚才好无聊,突然想起来之前做一个课表的点子,于是百度了起来. 刚开始,我是这样想的:在写微信墙的时候,用到了urllib2[两行代码抓网页],那么就只剩下解析html了.于是百度:python解析html.发现一篇好文章,其中介绍到了pyQuery. pyQuery 是 jQuery 在 Python 中的实现,能够以 jQuery 的语法來操作解析 HTML 文档.使用前需要安装,Mac安装方法如下: sudo easy_install pyquery OK!安装好了! 我们来试一试吧: fr

在Python中使用NLTK库实现对词干的提取的教程_python

什么是词干提取? 在语言形态学和信息检索里,词干提取是去除词缀得到词根的过程─-得到单词最一般的写法.对于一个词的形态词根,词干并不需要完全相同:相关的词映射到同一个词干一般能得到满意的结果,即使该词干不是词的有效根.从1968年开始在计算机科学领域出现了词干提取的相应算法.很多搜索引擎在处理词汇时,对同义词采用相同的词干作为查询拓展,该过程叫做归并. 一个面向英语的词干提取器,例如,要识别字符串"cats"."catlike"和"catty"是

WebGIS中基于控制点库进行SHP数据坐标转换的一种查询优化策略

文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.前言 目前项目中基于控制点库进行SHP数据的坐标转换,流程大致为:遍历图层要素,获取每个要素的坐标串,查询控制点库,分别进行坐标转换,构建新的要素,最后构建新的图层.此方法效率如下: a.控制点库有100W个控制点对. b.待转换SHP数据有5K个面要素. 转换完毕大概需要120分钟. 此效率是相对较低的,如果想对更多包含大量要素的图层组数据进行批量转换,耗时会成倍

用javascript解决外部数据抓取中的乱码问题_应用技巧

我们一般会在两个地方用到外部数据抓取,一个是在asp中,一个是在hta中.如果外部数据是gb2312编码的,就涉及到转码的问题,但是传统的用vbs函数进行处理的方法,运算量比较大,有些特殊字符还会出错.    如果用adodb.stream控件来进行转码,就简单多了,不需要借助vbs的二进制处理函数了,同时速度快了很多. 复制代码 代码如下: <script>  function loadData(sUrl){      var xh      xh=new ActiveXObject(&qu

请问“豆瓣东西”、“果库”怎么通过Url抓取商品信息(ID、标题、价格、图片集等)?

问题描述 请问"豆瓣东西"."果库"怎么通过Url抓取商品信息(ID.标题.价格.图片集等)? 淘宝API有调用次数的限制,我觉得他们应该不是用淘宝API获取数据的.是不是用类似HtmlAgilityPack这样的HTML解析类?天猫的商品价格好像是异步加载的,这个怎么获取? 解决方案 可以用jsoup抓取指定url的网页数据 下载一个jsoup-1.6.2.jar 解决方案二: http://www.shenjianshou.cn/index.php?r=mark

用javascript解决外部数据抓取中的乱码问题

我们一般会在两个地方用到外部数据抓取,一个是在asp中,一个是在hta中.如果外部数据是gb2312编码的,就涉及到转码的问题,但是传统的用vbs函数进行处理的方法,运算量比较大,有些特殊字符还会出错.     如果用adodb.stream控件来进行转码,就简单多了,不需要借助vbs的二进制处理函数了,同时速度快了很多. 复制代码 代码如下: <script>  function loadData(sUrl){      var xh      xh=new ActiveXObject(&q

PHP的cURL库功能简介:抓取网页,POST数据及其他

  使用PHP的cURL库可以简单和有效地去抓网页.你只需要运行一个脚本,然后分析一下你所抓取的网页,然后就可以以程序的方式得到你想要的数据了.无论是你想从从一个链接上取部分数据,或是取一个XML文件并把其导入数据库,那怕就是简单的获取网页内容,cURL 是一个功能强大的PHP库.本文主要讲述如果使用这个PHP库. 启用 cURL 设置 首先,我们得先要确定我们的PHP是否开启了这个库,你可以通过使用php_info()函数来得到这一信息. ﹤?php phpinfo(); ?﹥ 如果你可以在网

python采用requests库模拟登录和抓取数据的简单示例_python

如果你还在为python的各种urllib和urlibs,cookielib 头疼,或者还还在为python模拟登录和抓取数据而抓狂,那么来看看我们推荐的requests,python采集数据模拟登录必备利器! 这也是python推荐的HTTP客户端库: 本文就以一个模拟登录的例子来加以说明,至于采集大家就请自行发挥吧. 代码很简单,主要是展现python的requests库的简单至极,代码如下: s = requests.session() data = {'user':'用户名','pass

Python3中常用的处理时间和实现定时任务的方法的介绍_python

无论哪种编程语言,时间肯定都是非常重要的部分,今天来看一下python如何来处理时间和python定时任务,注意咯:本篇所讲是python3版本的实现,在python2版本中的实现略有不同,有时间会再写一篇以便大家区分.1.计算明天和昨天的日期  #! /usr/bin/env python #coding=utf-8 # 获取今天.昨天和明天的日期 # 引入datetime模块 import datetime #计算今天的时间 today = datetime.date.today() #计算