我正在用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分钟就可以做到这一点?
不要先使用OpenURI;如果你使用九头蛇和伤寒,有一个更好的方法。
就像拥有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开始将这个问题排除在范围之外。阅读线程和队列文档,尤其是关于生产者和消费者的部分,您应该能够将其拼凑在一起。这来自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本)上只需要5-10分钟就可以做到这一点?
他们有:
- 快速连接到互联网
- CPU处理多个连接的能力
- RAM可以运行多个线程并保存大量等待处理的页面
处理这么多页面并不难,你只需要现实地对待你的资源,并明智地使用可用的资源。
我的建议是什么?
- 不要试图一次打开100页;你的连接和CPU会堵塞,你的吞吐量会降低,而且你的应用程序可能会缺少RAM
- 运行测试来确定回报递减点在哪里,并且一次不允许超过这个数量的请求
- 消费线程将很容易领先于生产线程,因此您只需要一个消费者
相对而言,在执行http请求时会有很多等待,这对于多个线程/进程来说是一个很好的用例。您可以创建一个工作线程/进程池,从一个队列中检索请求数据,然后将结果放入另一个队列,主线程可以从中读取。
请参见此处:https://blog.engineyard.com/2014/ruby-thread-pool
一些做同样工作的应用程序如何工作(列出书籍,获取全部页面中的数据并保存到文件中),例如一些漫画下载程序对于大型漫画网站(约10000个标题)?
计算能力。如果你有一台10000核心的计算机(或者10000台每个有一个核心的计算机),你可以为每个请求启动一个进程,然后所有请求都会同时执行。完成所有请求的总时间将是完成最长请求所需的时间,而不是所有请求的所有时间之和。