是否有更有效的方法来刮取大量的URL(> 30k)?

时间:2017-03-08 19:48:59

标签: javascript ruby elasticsearch phantomjs nokogiri

我经营一家网上商店,经营收藏品,其市场价格根据消费者需求不断变化。

我最近开始记录我的竞争对手'价格通过每日运行一个ruby脚本(一个rake任务),通过一个~30k的URL列表,抓取一些相关的数据,并将它们填充到Elasticsearch索引中。我依靠Nokogiri和PhantomJS来实现这一目标,因为并非所有站点都能正确呈现我需要的数据而无需运行JavaScript。

我的程序在运行时目前占用大约4GB内存,而PhantomJS占据了大部分消耗(~2.5GB)。脚本也需要花费很多时间才能运行 - 我不确定多长时间,但我认为它超过10个小时。

我非常愿意接受有关如何减少内存消耗和提高我的速度的建议。我希望我的竞争对手能为我提供一个不错的JSON API,但不幸的是我们没有这种关系。

4 个答案:

答案 0 :(得分:2)

最明显的工作是确定哪些网站需要完整的浏览器处理,哪些网站可以直接翻录而不需要任何网站。

第二件事是检查正在运行的JavaScript应用程序,看看是否有任何方法可以直接从它正在使用的API获取所需的数据。在客户端应用程序(例如Angular,React,Ember)中通常会出现与服务器通信的某种JSON API。如果您可以直接与该API接口,它实际上大大简化了您的数据收集过程:您甚至可能根本不需要解析任何HTML!

Ruby在处理事物方面通常都很不错,但它并不总是最有效的。需要考虑的是,如果使用JRuby并且线程可能会提高性能,通常它是一个替代品,运行速度提高约40%,但代价是更高的初始内存占用。

您可能还想探索使用Node.js执行大量获取/执行JavaScript的肮脏工作的可行性,因为与Ruby的许多JavaScript运行时相比,它的重量非常轻。它甚至可以作为一个非常好的预取器,然后可以将内容移交给Ruby后端以进行更多处理。

使用数据库,Redis或RabbitMQ中间层作为队列或持久性机制,构建这样的混合系统非常容易。

答案 1 :(得分:1)

在不影响吞吐量的情况下,您可以做一些事情来提高抓取速度并保持较低的内存消耗。

为了减少内存消耗,您可以将URL保存在平面文件或数据库中,而不是通过数据结构将其保存在内存中。

在几次迭代后也清空任何数据的数据结构。

我假设您按顺序提出请求,因为每次抓取网址所花费的平均时间大于1.2秒(10 * 60 * 60/30000 = 1.2)。您可以一次对一堆请求进行异步调用,因为您的代码将等待一个请求完成,直到它成为下一个请求。

您可以参考“Building blocks of a scalable web crawler”,其中涵盖了可扩展抓取的大部分方面。

由于我没有关于您的代码的任何信息,因此我可以给出一些建议。

答案 2 :(得分:0)

我认为你应该节省时间并使用SaaS服务。

  • Datafiniti使用现有的超过8000万种产品的数据库,可以访问产品的定价和其他数据。如果他们还没有你想要的数据,他们可能会有兴趣添加它。
  • 80legs根据您的参数提供按需自定义网络抓取功能。您可以根据需要随意抓取任意数量的网址,然后通过API或信息中心获取数据。
  • Import.io提供自定义抓取和提取功能,并在其上方添加了一个灵活的用户界面。像80legs一样,内置相当强大的提取功能。同样,设置它,根据需要随时抓取数据,并通过API或仪表板检索结果。

我相信有时间和地点推出自己的功能。例如,如果您想出于估价原因将其构建为您自己的IP,或者您认为这可以抓住您组织的核心竞争力(即,这是一种竞争优势)。但是,考虑到你需要它能够很好地工作而不是在爬行,这是站在别人肩膀上的好时机。

答案 3 :(得分:-2)

我假设你在单线程上运行你的刮板(因为需要时间来完成)。您应该考虑在多个线程上运行脚本。 https://www.tutorialspoint.com/ruby/ruby_multithreading.htm