Получение всех ссылок веб-страницы с использованием Ruby - PullRequest
11 голосов
/ 15 июля 2011

Я пытаюсь получить каждую внешнюю ссылку веб-страницы, используя Ruby. Я использую String.scan с этим регулярным выражением:

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

Затем я могу использовать gsub для удаления части href:

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

Это отлично работает, но я не уверен, что это эффективно с точки зрения производительности. Это нормально для использования, или я должен работать с более конкретным парсером (например, нокогири)? Какой способ лучше?

Спасибо!

Ответы [ 5 ]

16 голосов
/ 15 июля 2011

Использование регулярных выражений хорошо для быстрого и грязного сценария, но 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
6 голосов
/ 15 июля 2011

Механизация использует 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.Это часто задаваемый вопрос здесь о переполнении стека, причем этот является самым известным ответом.Почему это так?Поскольку создание надежного регулярного выражения, которое может обрабатывать реальные варианты HTML, а некоторые допустимы, а некоторые нет, очень сложно и в конечном итоге сложнее, чем простое решение для анализа, которое будет работать практически со всеми страницами, которые будут отображаться в браузере.

5 голосов
/ 16 июля 2011

Я большой поклонник Нокогири, но зачем изобретать велосипед?

Модуль Ruby's 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 печально известен своей плохой написанностью. Это помогает нам, потому что с большинством неподтвержденных HTML Nokogiri может исправить это. Иногда я сталкиваюсь с чем-то настолько написанным, что Nokogiri не может исправить это правильно, поэтому мне придется слегка подтолкнуть его, настроив HTML-код, прежде чем передать его Nokogiri; Я все еще буду использовать парсер, а не пытаться использовать шаблоны.

4 голосов
/ 15 июля 2011

почему вы не используете группы в вашем шаблоне?например,

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

, поэтому первая группа уже будет ссылкой, которую вы искали.

1 голос
/ 15 июля 2011

Можете ли вы добавить группы в свое регулярное выражение? Это уменьшит ваши регулярные выражения до 1 вместо 2.

...