【问题标题】:Why doesn't Nokogiri load the full page?为什么 Nokogiri 不加载整页?
【发布时间】:2011-09-27 08:23:04
【问题描述】:

我正在使用 Nokogiri 打开有关各个国家的维基百科页面,然后从跨维基链接(外语维基百科的链接)中提取这些国家的其他语言名称。但是,当我尝试打开the page for France 时,Nokogiri 并没有下载完整页面。也许它太大了,反正它不包含我需要的跨维基链接。我怎样才能强制它全部下载?

这是我的代码:

url = "http://en.wikipedia.org/wiki/" + country_name
page = nil
begin
  page = Nokogiri::HTML(open(url))
rescue   OpenURI::HTTPError=>e
  puts "No article found for " + country_name
end

language_part = page.css('div#p-lang')

测试:

with country_name = "France"
=> []

with country_name = "Thailand"
=> really long array that I don't want to quote here,
   but containing all the right data

也许这个问题超出了 Nokogiri 并进入了 OpenURI - 无论如何我需要找到解决方案。

【问题讨论】:

  • 你知道 open() 不是 Nokogiri 吗?

标签: ruby nokogiri open-uri


【解决方案1】:
require 'open-uri'
require 'zlib'

open('Accept-Encoding' => 'gzip, deflate') do |response|
  if response.content_encoding.include?('gzip')
    response = Zlib::GzipReader.new(response)
    response.define_singleton_method(:method_missing) do |name|
      to_io.public_send(name)
    end
  end

  yield response if block_given?

  response
end

【讨论】:

    【解决方案2】:

    Nokogiri 不检索页面,它要求 OpenURI 使用 Open::URI 返回的 StringIO 对象上的内部 read 来完成。

    require 'open-uri'
    require 'zlib'
    
    stream = open('http://en.wikipedia.org/wiki/France')
    if (stream.content_encoding.empty?)
      body = stream.read
    else
      body = Zlib::GzipReader.new(stream).read
    end
    
    p body
    

    您可以关闭以下内容:

    >> require 'open-uri' #=> true
    >> open('http://en.wikipedia.org/wiki/France').content_encoding #=> ["gzip"]
    >> open('http://en.wikipedia.org/wiki/Thailand').content_encoding #=> []
    

    在这种情况下,如果它是 [],又名“text/html”,它会读取。如果是["gzip"],它会解码。

    把上面所有的东西都做完,然后扔到:

    require 'nokogiri'
    page = Nokogiri::HTML(body)
    language_part = page.css('div#p-lang')
    

    应该会让你重回正轨。

    在完成上述所有操作后执行此操作,以直观地确认您获得了可用的东西:

    p language_part.text.gsub("\t", '')
    

    请参阅 Casper 的回答并了解为什么您会看到两个不同的结果。最初看起来 Open-URI 在处理返回的数据时不一致,但根据 Casper 所说的以及我使用 curl 看到的内容,维基百科不尊重大型文档的“Accept-Encoding”标头并返回 gzip。这对于今天的浏览器来说是相当安全的,但是像 Open-URI 这样不能自动感知编码的客户端会有问题。这就是上面的代码应该帮助解决的问题。

    【讨论】:

    • 谢谢。我实现了这个,现在我正在获取数据。唯一的问题是 Unicode 国家名称不再正确显示,例如。 G。我得到 αñ½αñ╝αÑìαñ░αñ╛αñ¿αÑìαñ 作为法国的印地语名称。这只是 gzip 编码名称的问题,其他名称会自动正确。有什么想法吗?
    • 没关系,我想通了:只需执行 page = Nokogiri::HTML(body, nil, 'UTF-8') 代替。
    • 与其假设它是“UTF-8”,不如使用响应的external_encoding 方法。假设一个站点始终使用 UTF-8 是非常安全的,但是如果您正在查看多个站点,您可能会得到不兼容的编码。只是需要考虑的事情。
    • 向铁皮人致敬! :D
    【解决方案3】:

    经过一番摸索后,问题就在这里:

    > wget -S 'http://en.wikipedia.org/wiki/France'
    Resolving en.wikipedia.org... 91.198.174.232
    Connecting to en.wikipedia.org|91.198.174.232|:80... connected.
    HTTP request sent, awaiting response...
      HTTP/1.0 200 OK
      Content-Language: en
      Last-Modified: Fri, 01 Jul 2011 23:31:36 GMT
      Content-Encoding: gzip <<<<------ BINGO!
      ...
    

    您需要解压缩 gzip 后的数据,而 open-uri 不会自动执行此操作。
    解决方案:

    def http_get(uri)
      url = URI.parse uri
    
      res = Net::HTTP.start(url.host, url.port) { |h|
        h.get(url.path)
      }
    
      headers = res.to_hash
      gzipped = headers['content-encoding'] && headers['content-encoding'][0] == "gzip"
      content = gzipped ? Zlib::GzipReader.new(StringIO.new(res.body)).read : res.body
    
      content
    end
    

    然后:

    page = Nokogiri::HTML(http_get("http://en.wikipedia.org/wiki/France"))
    

    【讨论】:

    • 实际上,Open::URI 确实对 gzip 进行了解码,但对于特定大小的页面似乎是这样做的。来自 Wikipedia 的所有结果都经过 gzip 压缩,但法国的页面未解码。来自泰国的较小的页面被解码。
    • 呃..不在我的机器上。当我请求时,泰国没有压缩。这似乎取决于您的位置,或者您碰巧碰到的维基百科服务器。使用 wget -S 泰国对我来说不是 gzip 压缩的。还查看了 open-uri 的代码,我找不到可以解压打包页面的位置...你确定它这样做了吗?
    • @Tin Man,你错了。问题是维基百科似乎在页面太大时压缩页面,即使请求中不存在 Accept-Encoding 标头。这是一个 2.7k 的小页面,您可以使用它进行测试。 open-uri 无法正确解码: open('whatsmyip.org/http_compression', "Accept-Encoding" => "gzip").read.
    • 我对 Wikipedia 的看法可能是错误的,但这是一个红鲱鱼。问题是正确解码 Open-URI 结果,这是 OP 关心的问题。
    • 是的,好吧..当然..只是不要认为 open-uri 能够解码 gzip。它不是。即使它是一个 10 字节的页面,它也不会对其进行解码。您的示例有效的原因是默认情况下 open-uri 不发送 Accept-Encoding 标头,因此大多数网络服务器只返回 text/html。一旦页面变得太大,一些服务器似乎无论如何都会压缩页面,忽略缺少的 Accept-Encoding..这就是 open-uri 失败的时候。无论如何..您的解决方案有效,但不是因为您认为的原因。
    猜你喜欢
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 2011-10-09
    • 1970-01-01
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多