【问题标题】:Ruby Mechanize web scraper library returns file instead of pageRuby Mechanize 网络爬虫库返回文件而不是页面
【发布时间】:2011-08-02 20:36:35
【问题描述】:

我最近一直在使用 ruby​​ 中的 Mechanize gem 来编写一个刮板。不幸的是,我尝试抓取的 URL 在 GET 请求时返回 Mechanize::File 对象而不是 Mechanize::Page 对象。

我不知道为什么。我尝试过的所有其他 URL 都返回了一个 Mechanize::Page 对象。

有没有办法强制 Mechanize 返回一个Page 对象?

【问题讨论】:

  • 给出两个不同结果的页面示例
  • 这可能是目标服务器基于 User-Agent 或类似的东西所做的事情。

标签: ruby object mechanize scraper


【解决方案1】:

这就是正在发生的事情。

当您下载“普通”网页时,其标题将包含类似Content-Type text/html 的字段。当 Mechanize 看到这一点时,它知道将页面内容解释为 HTML 并将其解析为 Mechanize::Page 对象,并包含链接和表单等等。

但是,如果您曾经点击过“下载 CSV 数据”或“下载 PDF”的链接,或者简而言之,任何非 HTML 的链接,您将收到一个没有 Content-Type 的页面text/html。由于 Mechanize 无法将非 html 解析为 Mechanize::Page 对象,因此会将内容打包成 Mechanize::File 对象。

您对 Mechanize::File 对象所做的工作取决于您要完成的工作。例如,如果您知道您访问的页面是 CSV 数据而不是 HTML,您可以像这样提取 CSV 数据:

page = web_agent.get(some_url_that_references_csv_data)
parsed_csv = CSV.parse(page.body)

如果您想花点心思,您可以编写自己的解析器,让 Mechanize 处理非 HTML 格式。如果你想走那条路,请参阅the Mechanize docs on PluggableParser。但是您可以通过直接使用 Mechanize::File 对象来完成很多工作。

对@user741072 评论的补充

另一方面,如果页面 HTML 并且有人忽略了将 content-type 设置为 HTML,您可以编写一个方法在 html 解析器中交换默认解析器的时间足够长解析页面。这将强制解析为 HTML:

def with_html_parser(agent, &body)
  original_parser = agent.pluggable_parser.default
  agent.pluggable_parser.default = agent.pluggable_parser['text/html']
  begin
    yield
  ensure
    agent.pluggable_parser.default = original_parser
  end
end

让我知道这是否有效。

【讨论】:

  • 公平地说,我的回答并没有真正说出@zhon 和 gogogopenny 还没有说过的话——我只是用更简单的术语来说明。
  • 这是一个html页面。问题是写它的白痴没有在标题中说明这一点。我可以强制 Mechanize 将其解释为 HTML 文件吗?
【解决方案2】:

当网站不返回内容类型作为响应的一部分时,您可以在后连接挂钩中自己设置内容类型:

agent = Mechanize.new { |a|
  a.post_connect_hooks << lambda { |_,_,response,_|
    if response.content_type.nil? || response.content_type.empty?
      response.content_type = 'text/html'
    end
  }
}

【讨论】:

  • 这正是我所需要的。小修正:response.content_type 可能为零,因此您需要对此进行测试,例如使用blank?
  • 谢谢。顺便说一句“”。空白?是 rails 的一部分而不是 ruby​​。
  • 什么?您可以在没有导轨的情况下运行 ruby​​ 吗? ;)
  • 是的,库比编写它的语言更受欢迎。
【解决方案3】:

使用 curl (curl yoururl -i) 查看 HTTP-Headers 中特定 url 的 Content-Type。在您的代码中,您可能需要在获取 url 之前检查内容类型:

require 'net/http'
url = URI.parse('http://www.gesetze-im-internet.de/bundesrecht/bgb/gesamt.pdf')
res = Net::HTTP.start(url.host, url.port) {|http| http.head(url.path)}
puts res['content-type']

#=> application/pdf

或者您可以检查您的 Mechanize 对象是否属于 Mechanize::Page 类:

agent = Mechanize.new
unknown_body = agent.get(url)
if unknown_body.class == Mechanize::Page
  self.body = unknown_body
else
  puts "Discarded binary content!"
end

请注意,这种方法会慢得多,因为它无论如何都会“下载”请求的资源。但如果您想存储文件以供以后使用,它可能会很有用。

【讨论】:

  • Mechanize 支持head 请求。
  • 您可以从 Mechanize::File 中检索 Content-Type,并且您可以安装可插入的解析器,如上所述。无需额外的工作,机械化为您完成所有工作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-26
  • 1970-01-01
相关资源
最近更新 更多