2011-07-14 53 views
10

我试图使用Ruby检索网页的每个外部链接。我使用String.scan与此正则表达式:使用Ruby获取网页的所有链接

/href="https?:[^"]*|href='https?:[^']*/i 

然后,我可以使用GSUB删除HREF部分:

str.gsub(/href=['"]/) 

这工作得很好,但我不知道这是否是有效的条款的表现。这可以使用,或者我应该使用更具体的解析器(例如nokogiri)?哪种方法更好?

谢谢!

+4

请不要试图用正则表达式解析HTML,HTML解析器将为您提供更好的服务。 –

+0

@mu你能解释我为什么吗? –

+1

由于HTML解析比您可能认为的更复杂,并且存在大量破碎的HTML,因此简单的正则表达式将无法处理:http://stackoverflow.com/questions/4231382/regular-expression-pattern-不匹配任何字符串/ 4234491#4234491 –

回答

3

为什么你不在你的模式中使用组? 例如

/http[s]?:\/\/(.+)/i 

因此第一组已经是您搜索的链接。

1

你可以在你的正则表达式的组?这将减少你的正则表达式来代替1 2

+0

我正在学习正则表达式。我会分组看看。谢谢! –

15

使用正则表达式是罚款,一个快速和肮脏的脚本,但引入nokogiri是使用非常简单:

require 'nokogiri' 
require 'open-uri' 

fail("Usage: extract_links URL [URL ...]") if ARGV.empty? 

ARGV.each do |url| 
    doc = Nokogiri::HTML(open(url)) 
    hrefs = doc.css("a").map do |link| 
    if (href = link.attr("href")) && !href.empty? 
     URI::join(url, href) 
    end 
    end.compact.uniq 
    STDOUT.puts(hrefs.join("\n")) 
end 

如果你只想方法,重构它一点点到您的需求:

def get_links(url) 
    Nokogiri::HTML(open(url).read).css("a").map do |link| 
    if (href = link.attr("href")) && href.match(/^https?:/) 
     href 
    end 
    end.compact 
end 
+0

你能解释我的优点吗?代码看起来比使用正则表达式和扫描更复杂。我也很想知道哪种解决方案更快。 –

+0

@tokland,我想你想要Nokogiri :: HTML。还要注意只提取绝对链接的要求。 –

6

Mechanize采用引入nokogiri引擎盖下,但内置了细微解析HTML,包括链接:

require 'mechanize' 

agent = Mechanize.new 
page = agent.get('http://example.com/') 

page.links_with(:href => /^https?/).each do |link| 
    puts link.href 
end 

使用的解析器一般总是比使用正则表达式解析HTML更好。这是Stack Overflow的一个常见问题,其中this是最有名的答案。为什么会这样?因为构建一个可处理HTML真实世界变体的强大正则表达式,其中有些不是有效的,它比一个简单的解析解决方案非常困难,而且最终会比浏览器中呈现的所有页面更复杂。

+0

我同意,当你需要解析html时,你不想使用正则表达式。但在这种情况下,我认为一个正则表达式就足够了,因为你不会因html的不规则性而陷入困境(因为不存在递归性)。你能想到一个(没有人为的)例子,这个正则表达式(在我对这个问题的评论中提到的我的改进)会失败吗? – markijbema

+0

我喜欢你的解决方案更好btw,它简短易读,但我真的不喜欢过度绝对的真理,如'你不能用正则表达式来触摸html'。 – markijbema

+0

@markijbema我已经添加了一些解释。这是我见过的一个案例:'foo'。有时候还有新线。 –

4

我是Nokogiri的忠实粉丝,但为什么要重新发明轮子?

Ruby的URI模块已经有extract方法来做到这一点:

URI::extract(str[, schemes][,&blk]) 

从文档:从字符串

提取物的URI。如果给出的块,遍历所有匹配的URI。如果给定块或匹配数组,则返回nil。

require "uri" 

URI.extract("text here http://foo.example.org/bla and here mailto:[email protected] and here also.") 
# => ["http://foo.example.com/bla", "mailto:[email protected]"] 

你可以使用引入nokogiri走DOM,并把所有有网址的代码,也可以检索它只是文本,并把它传递给URI.extract,或只是让URI.extract做这一切。

而且,为什么使用解析器,如Nokogiri,而不是正则表达式模式?因为HTML和XML可以用很多不同的方式进行格式化,并且仍能在页面上正确显示或有效地传输数据。当涉及到接受糟糕的标记时,浏览器是非常宽容的。另一方面,正则表达式模式在非常有限的“可接受性”范围内工作,该范围由您预期标记变化的多好,或者相反,您预期当预测模式可能出错时的方式呈现出意外的模式。

解析器不像正则表达式那样工作。它构建了文档的内部表示形式,然后逐步介绍了该文档。它不关心文件/标记是如何布置的,它在DOM的内部表示上做了工作。 Nokogiri放宽其解析来处理HTML,因为HTML由于写得不好而臭名昭着。这有助于我们,因为大多数非验证HTML Nokogiri都可以修复它。有时候我会遇到一些写得不好的东西,Nokogiri无法正确地修复它,所以我必须在将它传递给Nokogiri之前通过调整HTML来稍微微调它;我仍然会使用解析器,而不是尝试使用模式。