抓取 500 万个网页的最有效(时间、成本)方法是什么?

抓取 500 万个网页的最有效(时间、成本)方法是什么?

我有一份需要抓取、解析并将结果数据存储到数据库中的网页列表。总数约为 5,000,000 个。

我目前认为解决此问题的最佳方法是部署约 100 个 EC2 实例,为每个实例提供 50,000 个页面进行抓取,然后让其运行,一旦该过程完成,再将数据库合并在一起。假设运行大约需要一天时间(加载、解析和保存每个页面需要 600 毫秒)。

有人有在有限时间内进行如此大量页面抓取的经验吗?我以前做过大量页面抓取(150 万),但那只是在一台机器上进行的,而且只花了一个多星期才完成。

我的情况中的瓶颈是页面的下载,解析时间不超过 2 毫秒,所以我正在寻找可以简化页面下载过程的东西。

答案1

假设下载时间(以及带宽使用量)是您的限制因素,我提出以下建议:

首先,选择 m1.large 实例。在三个“级别”的 I/O 性能(包括带宽)中,m1.large 和 m1.xlarge 实例都提供“高”I/O 性能。由于您的任务不受 CPU 限制,因此最便宜的实例将是首选。

其次,您的实例的下载速度将远快于任何站点提供的页面速度 - 不要在给定实例上一次下载单个页面,而是同时运行任务 - 您应该能够同时下载至少 20 个页面(虽然,我猜您可能可以毫不费力地完成 50-100 个页面)。 (以从您的评论中的论坛下载为例 - 这是一个动态页面,需要服务器花时间生成 - 并且还有其他用户使用该站点的带宽等)。 继续增加并发性,直到达到实例带宽的限制。 (当然,不要同时向同一个站点发出多个请求)。

如果您确实想要最大限度地提高性能,您可以考虑在适当的地理位置启动实例以最大限度地减少延迟(但这需要对所有 URL 进行地理定位,这可能不切实际)。

需要注意的一点是,实例带宽是可变的,有时您会获得更高的性能,有时您会获得更低的性能。在较小的实例上,性能变化更为显著,因为物理链接由更多服务器共享,其中任何一个都可能降低您的可用带宽。在 m1.large 实例之间,在 EC2 网络(同一可用区域)内,您应该获得接近理论上的千兆位吞吐量。

一般来说,使用 AWS 时,使用较大的实例几乎总是比使用多个较小的实例更有效率(除非您专门考虑需要多个实例的情况,例如故障转移等)。

我不知道您的设置需要什么,但是当我以前尝试过这种方法(100 万到 200 万个链接,定期更新)时,我的方法是维护一个链接数据库,在发现新链接时添加新链接,并分叉进程以抓取和解析页面。将检索(随机)一个 URL 并在数据库中将其标记为正在进行,脚本将下载该页面,如果成功,则在数据库中将该 URL 标记为已下载,并将内容发送到解析该页面的另一个脚本,在发现新链接时将其添加到数据库中。此处数据库的优势是集中化 - 多个脚本可以同时查询数据库,并且(只要事务是原子的)可以确保每个页面只会下载一次。

另外需要注意的几点是,您可以同时运行的按需实例数量是有限制的(我相信是 20 个),如果您计划超出这些限制,则需要请求 AWS 增加您帐户的限制。对于您来说,运行现货实例并在现货价格较低时扩大您的数量会更经济(也许一个按需实例可以保持一切井井有条,其余的则是现货实例)。

如果对您来说时间比成本更重要,则集群计算实例可提供 10Gbps 带宽 - 并且应该提供最大的下载带宽。

回顾:尝试几个大型实例(而不是许多小实例)并在每个实例上运行多个并发下载 - 如果发现带宽有限,则添加更多实例,如果发现 CPU/内存受限,则转移到更大的实例。

答案2

我们也尝试做过类似的事情,以下是我的看法:

  1. 获得 2-3 个便宜的无限量服务器,例如,无需支付带宽费用。

  2. 使用带有 asyncore 的 python。Asyncore 是做事的老方法,但我们发现它比任何其他方法都快。缺点是 DNS 查找是阻塞的,即不是“并行的”。使用 asyncore,我们设法在 40 分钟内抓取了 1M 个 URL,使用单个 XEON 4 核、8 GB RAM。服务器上的平均负载小于 4(对于 4 核来说,这非常好)。

  3. 如果你不喜欢 asyncore,可以试试 gevent。它甚至可以实现非阻塞 DNS。使用 gevent,在同样的硬件上下载 1M 需要大约 50 分钟。服务器上的平均负载非常大。

注意,我们确实测试了很多 Python 库,例如 grequests、curl、liburl/liburl2,但我们没有测试过 Twisted

  1. 我们确实测试了 PHP + curl + 几个进程,它运行了大约一个小时,但是服务器的平均负载非常大。

相关内容