从网站中抓取数据是开发者的一个典型“用例”。无论它是属于副业项目,还是你正在成立一个初创公司,抓取数据似乎都很有必要。
举个例子,倘若您想要创建一个比价网站,那么您会需要从各种电商网站上抓取价格信息;或者您想要构建一个可以识别商品并在亚马逊上自动查找价格的“人工智能”。类似的场景还有很多。
但是您有没有注意到,获取所有页面信息的速度有多慢呢?您会选择一个接一个地去抓取商品吗?应该会有更好的解决方案吧?答案是肯定的。
抓取网页可能非常耗时,因为您必须花时间等待服务器响应,抑或是速率受限。这就是为什么我们要向您展示如何通过在 Python 中使用并发来加速您的网页数据抓取项目。
前提
为了使代码正常运行,您需要安装 python 3[1]。部分系统可能已经预装了它。然后您还需要使用 pip install
安装所有必要的库。
pip install requests beautifulsoup4 aiohttp numpy
如果您了解并发背后的基础知识,可以跳过理论部分直接进入实际操作环节。
并发
并发是一个术语,用于描述同时运行多个计算任务的能力。
当您按顺序向网站发出请求时,您可以选择一次发出一个请求并等待结果返回,然后再发出下一个请求。
不过,您也可以同时发送多个请求,并在它们返回时处理对应的结果,这种方式的速度提升效果是非常显著的。与顺序请求相比,并发请求无论是否并行运行(多个 CPU),都会比前者快得多 -- 稍后会详细介绍。
要理解并发的优势。我们需要了解顺序处理和并发处理任务之间的区别。假设我们有五个任务,每个任务需要 10 秒才能完成。当按顺序处理它们时,完成五个任务所需的时间为 50 秒;而并发处理时,仅需要 10 秒即可完成。
除了提高处理速度之外,并发还允许我们通过将网页抓取任务负载分布于多个进程中,来实现在更短的时间内完成更多的工作。
这里有几种实现并行化请求的方式:例如 multiprocessing
和 asyncio
。从网页抓取的角度来看,我们可以使用这些库来并行处理对不同网站或同一网站不同页面的请求。在本文中,我们将重点关注 asyncio
,这是一个 Python 内置的模块,它提供了使用协程编写单线程并发代码的基础设施。
由于并发意味着更复杂的系统和代码,因此在使用前请考虑在您的使用场景中是否利大于弊。
并发的优势
-
在更短的时间内完成更多的工作
-
可以将空闲的网络时间投入到其他请求中
并发的危险之处
-
更不易于开发和调试
-
可能存在竞争条件
-
需要检查并使用线程安全的函数
-
一不小心就会增加程序阻塞的概率
-
并发自带系统开销,因此需要设置合理的并发级别
-
针对小型站点请求过多的话,可能会变成 DDoS 攻击
*同时释放所有请求时要小心*
为何选择 asyncio
在做出选择之前,我们有必要了解一下 asyncio
和 multiprocessing
之间的区别,以及 IO 密集型与 CPU 密集型之间的区别。
asyncio[2] “是一个使用 async/await 语法编写并发代码的库”,它在单个处理器上运行。
multiprocessing[3] “是一个支持使用 API 生产进程的包 [...] 允许程序员充分利用给定机器上的多个处理器”。每个进程将在不同的 CPU 中启动自己的 Python 解释器。
IO 密集型意味着程序将受 I/O 影响而变得运行缓慢。在我们的案例中,主要指的是网络请求。
CPU 密集型意味着程序会由于 CPU 计算压力导致运行缓慢 -- 例如数学计算。
为什么这会影响我们选择用于并发的库?因为并发成本的很大一部分是创建和维护线程/进程。对于 CPU 密集型问题,在不同的 CPU 中拥有多个进程将会提升效率。但对于 I/O 密集型的场景,情况可能并非如此。
由于网页数据抓取主要受 I/O 限制,因此我们选择了 asyncio
。但如果有疑问(或只是为了好玩),您可以使用 multiprocessing
尝试这个场景并比较一下结果。
顺序实现的版本
我们将从抓取 scrapeme.live
作为示例开始,这是一个专门用于测试的电子商务网站。
首先,我们将从顺序抓取的版本开始。以下几个片段是所有案例的一部分,因此它们将保持不变。
通过访问目标主