我最近一直在使用 ruby 中的 Mechanize gem 来编写刮板。不幸的是,我试图抓取的 URL 在请求时返回一个Mechanize::File
对象而不是一个Mechanize::Page
对象GET
。
我不知道为什么。我尝试过的每个其他 URL 都返回了一个Mechanize::Page
对象。
有什么方法可以强制 Mechanize 返回一个Page
对象吗?
这是正在发生的事情。
当您下载“普通”网页时,其标题将包含一个类似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 格式。如果你想走这条路,请参阅PluggableParser 上的 Mechanize 文档。但是您可以通过直接使用 Mechanize::File 对象来完成很多工作。
另一方面,如果页面是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
让我知道这是否有用。
当网站不返回内容类型作为响应的一部分时,您可以在后连接挂钩中自己设置内容类型:
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
}
}
使用 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
请注意,这种方法会慢得多,因为它无论如何都会“下载”请求的资源。但如果您想存储文件以供以后使用,它可能会很有用。