7

我正在尝试下载超过 100 万页(以序列 ID 结尾的 URL)。我已经实现了一种多用途下载管理器,具有可配置的下载线程数和一个处理线程。下载器批量下载文件:

curl = Curl::Easy.new

batch_urls.each { |url_info|
    curl.url = url_info[:url]
    curl.perform
    file = File.new(url_info[:file], "wb")
    file << curl.body_str
    file.close
    # ... some other stuff
}

我曾尝试下载 8000 页样本。使用上面的代码时,我在 2 分钟内得到 1000。当我将所有 URL 写入文件并在 shell 中执行时:

cat list | xargs curl

我在两分钟内生成了所有 8000 页。

问题是,我需要它在 ruby​​ 代码中使用它,因为还有其他监控和处理代码。

我试过了:

  • Curl::Multi - 它速度更快,但会丢失 50-90% 的文件(不下载它们并且没有给出原因/代码)
  • 使用 Curl::Easy 的多线程 - 与单线程的速度大致相同

为什么重用 Curl::Easy 比后续命令行 curl 调用慢,我怎样才能让它更快?或者我做错了什么?

我宁愿修复我的下载管理器代码,也不愿以不同的方式下载此案例。

在此之前,我正在调用命令行 wget,我提供了一个带有 URL 列表的文件。然而,并非所有错误都得到处理,使用 URL 列表时也无法分别为每个 URL 指定输出文件。

现在在我看来,最好的方法是使用多线程系统调用“curl”命令。但是为什么我可以在 Ruby 中直接使用 Curl 呢?

下载管理器的代码在这里,如果它可能有帮助:下载管理器(我玩过超时,从不设置它到各种值,它似乎没有帮助)

任何提示表示赞赏。

4

6 回答 6

5

这可能是Typhoeus的合适任务

像这样的东西(未经测试):

require 'typhoeus'

def write_file(filename, data)
    file = File.new(filename, "wb")
    file.write(data)
    file.close
      # ... some other stuff
end

hydra = Typhoeus::Hydra.new(:max_concurrency => 20)

batch_urls.each do |url_info|
    req = Typhoeus::Request.new(url_info[:url])
    req.on_complete do |response|
      write_file(url_info[:file], response.body)
    end
    hydra.queue req
end

hydra.run

想一想,由于文件数量庞大,您可能会遇到内存问题。防止这种情况的一种方法是永远不要将数据存储在变量中,而是直接将其流式传输到文件中。您可以为此使用em-http-request

EventMachine.run {
  http = EventMachine::HttpRequest.new('http://www.website.com/').get
  http.stream { |chunk| print chunk }
  # ...
}
于 2010-05-18T13:30:06.417 回答
3

因此,如果您不设置 on_body 处理程序,则遏制将缓冲下载。如果您正在下载文件,您应该使用 on_body 处理程序。如果您想使用 Ruby Curl 下载多个文件,请尝试使用 Curl::Multi.dow​​nload 接口。

require 'rubygems'
require 'curb'

urls_to_download = [
  'http://www.google.com/',
  'http://www.yahoo.com/',
  'http://www.cnn.com/',
  'http://www.espn.com/'
]
path_to_files = [
  'google.com.html',
  'yahoo.com.html',
  'cnn.com.html',
  'espn.com.html'
]

Curl::Multi.download(urls_to_download, {:follow_location => true}, {}, path_to_files) {|c,p|}

如果您只想下载单个文件。

Curl::Easy.download('http://www.yahoo.com/')

这是一个很好的资源: http: //gist.github.com/405779

于 2010-05-18T17:47:55.033 回答
1

已经完成了一些基准测试,将遏制与其他方法(例如 HTTPClient)进行了比较。几乎所有类别的赢家都是 HTTPClient。另外,有一些记录在案的场景中,遏制在多线程场景中不起作用。

和你一样,我有过你的经历。我在 20 多个并发线程中运行 curl 的系统命令,它比在 20 多个并发线程中运行 curl 快 10 倍。无论如何,我尝试了什么,总是这样。

从那以后我切换到 HTTPClient,差异很大。现在它的运行速度与 20 个并发 curl 系统命令一样快,并且使用的 CPU 也更少。

于 2013-01-11T22:47:25.397 回答
0

首先让我说我对 Ruby 几乎一无所知。

我所知道的是 Ruby 是一种解释型语言。毫不奇怪,它比为特定平台编译的经过高度优化的代码慢。每个文件操作可能都会对其进行检查,curl但不会。“其他一些东西”会使事情变得更加缓慢。

您是否尝试过分析您的代码以查看大部分时间都花在了哪里?

于 2010-05-18T10:32:00.197 回答
0

斯蒂维,

Net ::HTTP是否足以简单下载 HTML 页面?

于 2010-05-18T11:21:51.263 回答
0

您没有指定 Ruby 版本,但 1.8.x 中的线程是用户空间线程,不是由操作系统调度的,因此整个 Ruby 解释器只使用一个 CPU/内核。除此之外,还有一个全局解释器锁,可能还有其他锁,它们会干扰并发性。由于您正在尝试最大化网络吞吐量,因此您可能未充分利用 CPU。

产生与机器内存一样多的进程,并限制对线程的依赖。

于 2012-01-15T04:33:05.530 回答