使用Ruby获取网页的所有链接
我正在尝试使用Ruby检索网页的每个外部链接。 我正在使用String.scan
与此正则表达式:
/href="https?:[^"]*|href='https?:[^']*/i
然后,我可以使用gsub删除href部分:
str.gsub(/href=['"]/)
这很好用,但我不确定它在性能方面是否有效。 这可以使用,或者我应该使用更具体的解析器(例如nokogiri)? 哪种方式更好?
谢谢!
你为什么不在你的模式中使用群组? 例如
/http[s]?:\/\/(.+)/i
所以第一组已经是您搜索的链接。
使用正则表达式适用于快速而脏的脚本,但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
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上经常被问到的问题, 这是最着名的答案。 为什么会这样? 因为构建一个可以处理HTML的真实变体的健壮的正则表达式,一些有效的非正式表达式,非常困难,并且最终比简单的解析解决方案更复杂,该解决方案几乎适用于将在浏览器中呈现的所有页面。
我是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:test@example.com and here also.") # => ["http://foo.example.com/bla", "mailto:test@example.com"]
您可以使用Nokogiri来遍历DOM并拉出所有包含URL的标记,或者让它只检索文本并将其传递给URI.extract
,或者让URI.extract
完成所有操作。
而且,为什么要使用像Nokogiri这样的解析器而不是正则表达式? 因为HTML和XML可以通过许多不同的方式进行格式化,并且仍然可以在页面上正确呈现或有效地传输数据。 在接受糟糕的标记时,浏览器非常宽容。 另一方面,正则表达式模式在“可接受性”的非常有限的范围内工作,其中该范围由您预测标记变化的程度来定义,或者相反,您预测模式出错的方式有多好出现意想不到的模式。
解析器不像正则表达式那样工作。 它构建了文档的内部表示,然后逐步完成。 它不关心文件/标记的布局方式,它在DOM的内部表示上工作。 Nokogiri放松了解析来处理HTML,因为HTML因写得不好而臭名昭着。 这有助于我们,因为大多数非validationHTML Nokogiri可以解决它。 偶尔我会遇到一些写得很糟糕的东西,Nokogiri无法正确地解决它,所以我必须通过在将它传递给Nokogiri之前调整HTML来轻微推动它; 我仍然会使用解析器,而不是尝试使用模式。
你能把团体放在你的正则表达式吗? 这会将正则表达式减少到1而不是2。