0

我正在构建一个脚本来解析多个页面标题。感谢堆栈中的另一个问题,我现在有了这个工作位

curl = %x(curl http://odin.1.ai)
simian = curl.match(/<title>(.*)<\/title>/)[1]
puts simian

但是如果您尝试相同的页面没有标题,例如

 curl = %x(curl http://zales.1.ai)

它因 nill 类的未定义方法而死,因为它没有标题....我无法检查 curl 是否为 nil,因为在这种情况下它不是(它包含另一行)

即使标题不存在并移至下一页进行检查,您是否有任何解决方案可以使其正常工作?如果我们坚持使用此代码,我将不胜感激,因为我确实尝试过使用 nokogiri 和 uri 的其他解决方案 (Nokogiri::HTML(open("http:/.....") 但这也不能像 byname_meee.1 这样的子域工作.ai 不适用于默认的 open-uri,所以如果我们能坚持使用 curl 的代码,我将不胜感激。

更新

我意识到我可能遗漏了一些应该澄清的具体案例。这是为了解析 300-400 页。在第一次运行中,我注意到至少有两种情况,其中 nokogiri、hpricot 甚至更基本的 open-uri 都不起作用

1) open-uri 在一个简单的域中就失败了,像http://levant_alejandro.1.ai这是一个有效的域,并且可以与 curl 一起使用,但不能与 open_uri 或 nokogiri 一起使用 open_uri

2)第二种情况,如果页面没有像 http://zales.1.ai这样的标题

3) 第三个是一个带有图像的页面,没有像http://voldemortas.1.ai/这样的有效 HTML

第四种情况是页面只有内部服务器错误或乘客/机架错误。

可以使用此解决方案对前三个案例进行排序(感谢 #ruby IRC 频道中的 Havenwood)

curl = %x(curl http://voldemortas.1.ai/)
begin
   simian = curl.match(/<title>(.*)<\/title>/)[1]
rescue NoMethodError
   simian = "" # curl was nil?    
rescue ArguementError
   simian = "" # not html?
end
puts simian

现在我知道这既不优雅也不最优。

改写的问题

你有没有更好的方法来使用 nokogiri 或其他包含这些情况的 gem(没有标题或没有 HTML 有效页面甚至 404 页面)?鉴于我正在解析的页面具有相当简单的标题结构,上述解决方案是否合适?为了了解知识,了解为什么使用像 nokogiri 这样的额外 gem 进行解析会是更好的选择(注意:我尝试很少有 gem 依赖项,因为它们往往会随着时间的推移而破坏)。

4

3 回答 3

2

你对自己太苛刻了。

Nokogiri 不在乎您从哪里获得 HTML,它只需要文档的正文。您可以使用 Curb、Open-URI、原始 Net::HTTP 连接,它会解析返回的内容。

尝试遏制:

require 'curb'
require 'nokogiri'

doc = Nokogiri::HTML(Curl.get('http://http://odin.1.ai').body_str)
doc.at('title').text
=> "Welcome to Dotgeek.org * 1.ai"

如果你不知道你是否会有一个<title>标签,那么不要试图一次做所有的事情:

title = doc.at('title')
next if (!title)
puts title.text

看看“相当于 Ruby 的 curl? ”以获得更多想法。

于 2012-09-07T23:38:57.300 回答
1

您只需要在访问之前检查匹配项。如果curl.matchnil,则您无法访问分组:

curl = %x(curl http://odin.1.ai)
simian = curl.match(/<title>(.*)<\/title>/)
simian &&= simian[1] # only access the matched group if available
puts simian

请听从铁皮人的建议并使用 Nokogiri。您的正则表达式实际上只适用于脆弱的解决方案——当title元素分布在多行时它会失败。

更新

如果您真的不想使用 HTML 解析器并且您承诺这是一个快速脚本,您可以在标准库中使用 OpenURI(net/http 的包装器)。它至少比解析curl输出要干净一点。

require 'open-uri'

def extract_title_content(line)
  title = line.match(%r{<title>(.*)</title>})
  title &&= title[1]
end

def extract_title_from(uri)
  title = nil

  open(uri) do |page|
    page.lines.each do |line|
      return title if title = extract_title_content(line)
    end
  end
rescue OpenURI::HTTPError => e
  STDERR.puts "ERROR: Could not download #{uri} (#{e})"
end

puts extract_title_from 'http://odin.1.ai'
于 2012-09-08T00:45:47.557 回答
0

看来,您真正在寻找的是一种跳过非 HTML 响应的方法。就像 Tin Man 建议的那样,使用像 curl 这样的 curl 包装器比掉到 shell 上并在那里使用 curl 要容易得多:

1.9.3p125 :001 > require 'curb'
 => true 
1.9.3p125 :002 > response = Curl.get('http://odin.1.ai')
 => #<Curl::Easy http://odin.1.ai?> 
1.9.3p125 :003 > response.content_type
 => "text/html" 
1.9.3p125 :004 > response = Curl.get('http://voldemortas.1.ai')
 => #<Curl::Easy http://voldemortas.1.ai?> 
1.9.3p125 :005 > response.content_type
 => "image/png" 
1.9.3p125 :006 > 

所以你的代码可能看起来像这样:

response = Curl.get(url)
if response.content_type == "text/html" # or more fuzzy: =~ /text/
  match = response.body_str.match(/<title>(.*)<\/title>/)
  title = match && match[1] 
  # or use Nokogiri for heavier lifting
end

没有更多的例外让猿猴

于 2012-09-08T06:47:33.923 回答