【问题标题】:How to open and parse a large number of web pages fast and effectively with OpenURI and Nokogiri?如何使用 OpenURI 和 Nokogiri 快速有效地打开和解析大量网页?
【发布时间】:2014-08-30 15:41:07
【问题描述】:

我正在用 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 快速有效地打开和解析大量网页?

如果我不能对他们这样做,我该怎么办?一些执行相同工作(列出书籍、从页面获取所有数据并保存到文件)的应用程序(例如某些漫画下载器)如何在大型漫画网站(大约 10000 个标题)上只需要 5 到 10 分钟就能做到这一点?

【问题讨论】:

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


    【解决方案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 变成一本小书,这个问题开始超出了 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,但我认为搭载现有的、编写良好的工具比尝试使用自己的工具更明智。

    ... 一些执行相同工作(列出书籍、从页面获取所有数据并保存到文件)的应用程序(例如某些漫画下载器)如何在大型漫画网站(大约10000 个标题)?

    他们有:

    • 快速连接到互联网。
    • 处理多个连接的 CPU 能力。
    • RAM 用于运行多个线程并持有大量等待处理的页面。

    处理这么多页面并不难,您只需要对自己的资源持现实态度并明智地使用可用的资源。

    我有什么建议?

    • 不要试图一次打开 100 个页面;您的连接和 CPU 将被阻塞,您将降低吞吐量,而且您可能会耗尽您的应用程序的 RAM。
    • 运行测试以确定您的收益递减点在哪里,并且一次不允许超过该数量的请求。
    • 消费线程将轻松领先于生产线程,因此您只需要一个消费者。

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

      • 谢谢,我会试试你的解决方案。关于计算能力,我认为漫画下载软件不是这样,因为即使在高速连接的低规格计算机上,它们也不需要很长时间打开和获取数据,只需要30-45分钟以下.顺便说一句,它们大多是用编译语言编写的,这有关系吗?
      • 我认为您计算机的处理能力与漫画无关——重要的是漫画的计算能力。我不知道漫画是什么,但如果你向漫画发送一个请求,上面写着“给我所有这些网页”。漫画发出请求,然后汇总结果并将它们发送到您的计算机,漫画的计算能力决定了所有请求的完成速度。
      • :(。漫画只是日本漫画书的词,不是硬件。我使用“漫画下载器”,因为这些软件是这样命名的。An example。如果你被激怒了,对不起。
      • 好的,所以漫画是您下载的一些软件——那么与您计算机的处理能力有关。如果你是单核电脑,而且下载速度很快,那我觉得软件肯定是用线程的。
      猜你喜欢
      • 2016-01-15
      • 1970-01-01
      • 2020-10-10
      • 2021-05-06
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      • 2013-01-05
      • 1970-01-01
      相关资源
      最近更新 更多