如何使用OpenURI和Nokogiri快速有效地打开和解析大量网页?

时间:2014-08-30 15:41:07

标签: ruby parsing web-crawler nokogiri open-uri

我正在用Ruby编写一个应用程序,它可以从超过10000页的站点中搜索和获取数据。我使用OpenURI和Nokogiri来打开和解析网页以从中获取数据并将它们保存到本地数据文件中::

#An example
page = Nokogiri::HTML(open("http://example.com/books/title001.html"))    
#Get title, author, synopsys, etc from that page 

对于拥有ADSL连接的人来说,打开一个页面平均需要1秒钟。因为该网站有大约10000页,所以打开所有页面并获取所有书籍的数据需要3个多小时,这个应用程序的时间是不可接受的,因为我的用户不想等待那么多时间。

如何使用OpenURI和Nokogiri快速有效地打开和解析大量网页?

如果我不能和他们一起做,我该怎么办?一些漫画下载者只需要花费5-10分钟来完成大型漫画网站(大约10000个标题),那些做同样工作的应用程序(列出书籍,从页面获取所有数据并保存到文件中)如何才能花费5-10分钟? / p>

2 个答案:

答案 0 :(得分:1)

不要先使用OpenURI;如果您使用Hydra and Typhoeus,则有更好的方法。

  

像具有100个蛇头的神话兽的现代代码版本一样,Typhoeus并行运行HTTP请求,同时干净地封装处理逻辑。

     

...

     

并行请求:

hydra = Typhoeus::Hydra.new
10.times.map{ hydra.queue(Typhoeus::Request.new("www.example.com", followlocation: true)) }
hydra.run

文档中的更远......

  

如何在执行队列后获取一组响应:

hydra = Typhoeus::Hydra.new
requests = 10.times.map { 
  request = Typhoeus::Request.new("www.example.com", followlocation: true)
  hydra.queue(request) 
  request
}
hydra.run
responses = request.map { |request|
  request.response.response_body
}

request.response.response_body是您要用Nokogiri解析器包装的行:

Nokogiri::HTML(request.response.response_body)

此时,您将拥有一系列DOM来完成并处理。

但是等等!还有更多!

因为你想要节省一些处理时间,你需要设置一个线程和队列,推送解析的DOM(或者只是未解析的HTML response_body),然后让线程处理并写入文件。

这并不难,但是开始将问题超出Stack Overflow的范围,因为它变成了一本小书。阅读ThreadQueue文档,尤其是关于生产者和消费者的部分,您应该能够将它拼凑在一起。这来自ri Queue文档:

= Queue < Object

(from ruby core)
------------------------------------------------------------------------------
This class provides a way to synchronize communication between threads.

Example:

  require 'thread'
  queue = Queue.new

  producer = Thread.new do
    5.times do |i|
       sleep rand(i) # simulate expense
       queue << i
       puts "#{i} produced"
    end
  end

  consumer = Thread.new do
    5.times do |i|
       value = queue.pop
       sleep rand(i/2) # simulate expense
       puts "consumed #{value}"
    end
  end
------------------------------------------------------------------------------
= Class methods:

  new

= Instance methods:

  <<, clear, deq, empty?, enq, length, num_waiting, pop, push, shift, size

我用它来并行处理大量的URL,并且很容易设置和使用。使用Threads做所有事情是可能的,而不是使用Typhoeus,但我认为捎带现有的,写得很好的工具比尝试自己动作更明智。

  

...一些漫画下载者只需要花费5-10分钟来完成大型漫画网站的工作(如何做同样的工作(列出书籍,从页面获取所有数据并保存到文件))(约10000个标题)?

他们有:

  • 快速连接互联网。
  • 处理多个连接的CPU电源。
  • RAM用于运行多个线程并保存大量页面等待处理。

处理那么多页面并不难,你只需要对你的资源保持现实,并使用明智的用途。

我的建议是什么?

  • 不要试图一次打开100页;您的连接和CPU将被堵塞,您将降低吞吐量,加上您的内存应用程序可能会挨饿。
  • 运行测试以确定您的收益递减点在哪里,并且不允许一次性请求超过该数量。
  • 消费线程很容易保持在生产线程之前,因此您只需要一个消费者。

答案 1 :(得分:0)

相对而言,在执行http请求时会有很多等待,这是多线程/进程的一个很好的用例。您可以创建一个工作线程/进程池,从一个队列中检索请求数据,然后将结果推送到您的主线程可以读取的另一个队列中。

见这里:https://blog.engineyard.com/2014/ruby-thread-pool

  

一些应用程序如何工作(列出书籍,全部获取   来自页面的数据并保存到文件中),例如一些漫画下载程序   大型漫画网站(大约10000个)只需5-10分钟即可完成   标题)?

计算能力。如果您拥有10,000台核心计算机(或10,000台计算机,每台计算机只有一个核心),您可以为每个请求启动一个进程,然后所有请求将同时执行。完成所有请求的总时间将是完成最长请求所花费的时间 - 而不是所有请求的所有时间的总和。