网页抓取数据( 一个基于新的异步库(aiohttp)的请求的代替品)
优采云 发布时间: 2022-03-10 17:07网页抓取数据(
一个基于新的异步库(aiohttp)的请求的代替品)
Python网页数据抓取全记录
在本文中,我将向您展示基于新异步库 (aiohttp) 的请求的替代方案。我已经用它编写了一些非常快速的小型数据采集器,我将在下面向您展示如何操作。中描述的方法如此多样化的原因是数据“抓取”实际上涉及很多问题:您不需要使用相同的工具从数千个页面中抓取数据,同时自动化一些 Web 工作流程(例如填写一些表单然后取回数据)。
异步的基本概念
asyncio是python3.4中引入的异步IO库。您也可以通过 python3.3 中的 pypi 安装它。这很复杂,我不会详细介绍。相反,我将解释使用它编写异步代码需要了解的内容。
协程和事件循环。
协程类似于方法,但它们可以在代码中的特定点暂停和恢复。可用于在等待 IO(例如 HTTP 请求)时暂停协程,同时执行另一个请求。我们使用关键字 yield from 来设置一个状态,表明我们想要一个协程的返回值。事件循环用于调度协程的执行。
关于 asyncio 的内容还有很多,但到目前为止我们需要知道的就是这些。可能你还有点不清楚,我们来看一段代码。
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('
一个有用的方法是 asyncio.wait,它接受一个协程列表并返回一个收录所有协程的协程,所以我们可以这样写:
loop.run_until_complete(asyncio.wait([print_page('#x27;),
打印页面('
另一个是 asyncio.as_completed,它接受一个协程列表,同时返回一个迭代器,它按照完成的顺序生成协程,所以当你用它迭代时,你会尽快得到每个可用的结果。
数据抓取
现在我们知道如何执行异步 HTTP 请求,我们可以编写一个数据抓取器。我们只需要一些工具来阅读html页面,我用beautifulsoup来做这个,其他的比如pyquery或者lxml也可以实现。
我们将编写一个小型数据抓取器,从海盗湾网站 抓取一些 linux 发行版的*敏*感*词*链接,并声称是“世界上最大的 BitTorrent 跟踪器(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='使用磁铁下载这个*敏*感*词*') return a['href']
在这个协程中,url 的结果是按*敏*感*词*数排序的,所以第一个结果实际上是*敏*感*词*数最多的结果:
6 @asyncio.coroutine def print_magnet(query): url = ' 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 有多少潜力。
这种方法(实际上是所有手动方法)的一个限制是没有用于处理表单的单个库。机械方法有很多帮助提交表单非常容易,但如果你不使用它们,你将不得不自己处理这些事情。这可能会导致一些错误,所以同时我可能会编写一个这样的库(但现在不要担心)。
额外建议:不要向服务器要求太多
同时做3个请求很酷,但是同时做5000个就没那么好玩了。如果您计划同时执行太多请求,则链接可能会中断。您甚至可能被禁止连接到 Internet。
为了避免这些,您可以使用信号量。这是一个同步工具,可以用来限制同时工作的协程数量。我们只需要在构建循环之前创建一个信号量,并将我们希望允许的同时请求数作为参数传递给它:
sem = asyncio.Semaphore(5)
然后,我们只需要添加以下内容
页面 = 来自 get(url, compress=True)
替换为受信号量保护的相同事物。
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