随机代理不起作用

Random proxies not working

我有一个名为 Proxies 的模型,我在其中存储我的代理。

我希望 Nokogiri 每次打开网页时使用 IP 和端口获取随机代理。

这是我的代码:

total_proxies = @proxies.count
random_proxies = rand(total_proxies - total_proxies..total_proxies -1)
doc = Nokogiri::HTML(open(page.uri, :proxy => '#{random_proxies.ip}:#{random_proxies.port}'))

我在这里错过了什么?当我尝试 运行 它给我这个错误:

bad URI(is not URI?): #{random_proxies.ip}:#{random_proxies.port}

您对 :proxy 的字符串参数使用了单引号。您需要双引号,以便对 #{} 进行插值。像这样:

doc = Nokogiri::HTML(open(page.uri, :proxy => "#{random_proxies.ip}:#{random_proxies.port}"))

I want nokogiri to fetch a random proxy...

您不了解 Nokogiri 的工作原理。它什么也拿不到。曾经。它可以 读取 一个打开的文件或者它可以接受一个字符串,但它不知道如何 "fetch" 任何东西。

这项工作落在 OpenURI in your code, which is patching the Kernel's open 理解 URL 的方法上。 OpenURI returns 一个打开的文件给 Nokogiri,然后从中读取。因此,这将 Nokogiri 从获取任何东西的问题中移除。读取错误

bad URI(is not URI?)

再仔细一点,您会发现它来自 OpenURI,而不是 Nokogiri。

作为 (归功于他,而不是我),真正的问题是你使用单引号而不是双引号来包裹你的内插字符串。考虑一下:

class RandomProxies
  def ip 
    '127.0.0.1'
  end

  def port
    42
  end
end

random_proxies = RandomProxies.new

'#{random_proxies.ip}:#{random_proxies.port}'
# => "\#{random_proxies.ip}:\#{random_proxies.port}"

"#{random_proxies.ip}:#{random_proxies.port}"
# => "127.0.0.1:42"

...correct me if I am not wrong dosen open-uri close the connection. since after it opens up the proxy for each page I get this error "503 Too many open connections".

不,Nokogiri 不会关闭连接;我也不认为这是它的责任。

您可以查看 parse and read_io 源代码来确认这一点,and/or,如果您在 Linux 上,您可以 运行 进行一些小测试:

  1. 在您的磁盘上创建一个简单的 XML 文件。
  2. 启动IRB并加载Nokogiri:

    require 'nokogiri'
    
  3. 告诉 Nokogiri 使用以下方法解析文档:

    Nokogiri::XML(open('path/to/file.xml'))
    
  4. 切换到另一个shell和运行中的命令行:

    lsof | grep path/to/file.xml
    

    lsof 命令将显示 Ruby 文件仍然打开,尽管 Nokogiri 读取了它。 ("lsof"代表"list open files")。

  5. 退出 IRB 并再次 运行 lsof 命令。你会看到它已经关闭,因为文件没有出现。

您遇到问题的原因是您打开了一堆文件而不是关闭它们。 Often/usually 我们可以不关闭它们而侥幸逃脱,因为有大量可用的文件句柄并且代码在退出之前不会耗尽这些句柄。但这并不是真正好的编程习惯。我们应该 明确关闭它们,或者依赖 methods/structures 在完成后关闭文件。

因为您正在使用 Nokogiri 将文件解析为 DOM,Nokogiri 会将整个文件读入内存以进行处理。在这种情况下,不要简单地使用 open,而是使用 open 后跟 read,这将在完成后关闭文件。来自文档:

read ensures the file is closed before returning.

或者,您可以将文档的解析(无论是文件还是从网络中读取 URL)放入一个块中,以便它自动关闭。像这样未经测试的代码应该可以工作:

require 'open-air'
%w[urls to read].each do |url|
  open(url) do |xml|
    dom = Nokogiri::XML(xml) 
    # do something with the dom
  end
end

记住当块退出时using a block with open ensures that the file will be automatically closed

If a block is specified, it will be invoked with the IO object as a parameter, and the IO will be automatically closed when the block terminates. The call returns the value of the block.

这应该可以避免“503 打开的连接过多”问题的发生,因为您的代码一次只会打开一个到主机的连接。

如果您请求的那么多页面 运行 没有连接,另一种解决方案是使用 Typhoeus and Hydra,它可以并行获取 URL,并且可以被告知如何限制连接。 (您 ARE 将您的请求限制在合理的范围内,不是吗?如果不是,您应该得到 503 错误。)