2013-06-11 2 views
4

Я хочу найти каждую страницу сайта. Моя мысль состоит в том, чтобы найти все ссылки на странице, которые остаются в пределах домена, посещать их и повторять. Я должен будет принять меры, чтобы не повторять попыток.DRY искать каждую страницу сайта с nokogiri

Так начинается очень легко:

page = 'http://example.com' 
nf = Nokogiri::HTML(open(page)) 

links = nf.xpath '//a' #find all links on current page 

main_links = links.map{|l| l['href'] if l['href'] =~ /^\//}.compact.uniq 

«main_links» теперь массив ссылок с активной страницы, которые начинаются с «/» (которые должны быть ссылки только на текущем домене).

Отсюда я могу прокормить и прочесть эти ссылки в аналогичном коде выше, но я не знаю, как наилучшим образом гарантировать, что я не повторяюсь. Я думаю, я начинаю собирать все посещенные ссылки, как я посещаю их:

main_links.each do |ml| 
visited_links = [] #new array of what is visted 
np = Nokogiri::HTML(open(page + ml)) #load the first main_link 
visted_links.push(ml) #push the page we're on 
np_links = np.xpath('//a').map{|l| l['href'] if l['href'] =~ /^\//}.compact.uniq #grab all links on this page pointing to the current domain 
main_links.push(np_links).compact.uniq #remove duplicates after pushing? 
end 

Я все еще работаю этот последний бит ... но это, похоже, как грамотный подход?

Спасибо.

+0

В чем вопрос? Как отслеживать посещаемые ссылки? – pguardiario

+0

Не пишите свой собственный веб-искатель. Есть много хороших, на выбор. –

ответ

8

Другие рекомендовали вам не писать собственный веб-гусеничный механизм. Я согласен с этим , если ваши цели и производительность и надежность -. Однако это может быть отличное учебное упражнение. Вы это писали:

«[...], но я не знаю, лучший способ гарантировать, что я не повторять себя»

Рекурсия является ключевым здесь. Что-то вроде следующего кода:

require 'set' 
require 'uri' 
require 'nokogiri' 
require 'open-uri' 

def crawl_site(starting_at, &each_page) 
    files = %w[png jpeg jpg gif svg txt js css zip gz] 
    starting_uri = URI.parse(starting_at) 
    seen_pages = Set.new      # Keep track of what we've seen 

    crawl_page = ->(page_uri) do    # A re-usable mini-function 
    unless seen_pages.include?(page_uri) 
     seen_pages << page_uri    # Record that we've seen this 
     begin 
     doc = Nokogiri.HTML(open(page_uri)) # Get the page 
     each_page.call(doc,page_uri)  # Yield page and URI to the block 

     # Find all the links on the page 
     hrefs = doc.css('a[href]').map{ |a| a['href'] } 

     # Make these URIs, throwing out problem ones like mailto: 
     uris = hrefs.map{ |href| URI.join(page_uri, href) rescue nil }.compact 

     # Pare it down to only those pages that are on the same site 
     uris.select!{ |uri| uri.host == starting_uri.host } 

     # Throw out links to files (this could be more efficient with regex) 
     uris.reject!{ |uri| files.any?{ |ext| uri.path.end_with?(".#{ext}") } } 

     # Remove #foo fragments so that sub-page links aren't differentiated 
     uris.each{ |uri| uri.fragment = nil } 

     # Recursively crawl the child URIs 
     uris.each{ |uri| crawl_page.call(uri) } 

     rescue OpenURI::HTTPError # Guard against 404s 
     warn "Skipping invalid link #{page_uri}" 
     end 
    end 
    end 

    crawl_page.call(starting_uri) # Kick it all off! 
end 

crawl_site('http://phrogz.net/') do |page,uri| 
    # page here is a Nokogiri HTML document 
    # uri is a URI instance with the address of the page 
    puts uri 
end 

Короче:

  • отслеживать, какие страницы вы уже видели, используя Set. Делайте это не по значению href, а по полному каноническому URI.
  • Используйте URI.join, чтобы превратить возможные относительные пути в правильный URI относительно текущей страницы.
  • Используйте рекурсию, чтобы выполнять сканирование каждой ссылки на каждой странице, но при этом спасение, если вы уже видели эту страницу.
+0

Благодарим за разъясняющий пост! Здесь много, чтобы учиться! Благодарю. – twinturbotom

+0

Мне нравится ваш подход, а набор и жемчужины очень полезны. Спасибо за ваши усилия и поделиться своими знаниями! Я ценю это! Я все еще не понимаю все, но я буду! – twinturbotom

1

Это более сложная проблема, чем вы, кажется, понимаете. Использование библиотеки вместе с Nokogiri, вероятно, путь. Если вы не используете окна (например, я), вы можете посмотреть в Anemone.

+0

Я видел ссылки на Anemone, поскольку я изучал это; Я проверю это, спасибо! – twinturbotom

3

Вам не хватает некоторых предметов.

Местная ссылка может начинаться с /, но она также может начинаться с ., .. или даже без специального символа, то есть ссылка находится в текущем каталоге.

JavaScript также может использоваться как ссылка, поэтому вам нужно будет искать по всему документу и находить теги, используемые в качестве кнопок, а затем анализировать URL-адрес.

Это:

links = nf.xpath '//a' #find all links on current page 
main_links = links.map{|l| l['href'] if l['href'] =~ /^\//}.compact.uniq 

может быть лучше написано:

links.search('a[href^="/"]').map{ |a| a['href'] }.uniq 

В общем, не делайте этого:

....map{|l| l['href'] if l['href'] =~ /^\//}.compact.uniq 

, потому что это очень неудобно. Условное значение в map приводит к nil элементам в результирующем массиве, поэтому не делайте этого. Используйте select или reject, чтобы уменьшить набор ссылок, соответствующих вашим критериям, а затем используйте map, чтобы преобразовать их. При использовании здесь предварительная фильтрация с использованием ^= в CSS делает его еще проще.

Не храните ссылки в памяти. Вы потеряете весь прогресс, если вы нарушите или остановите свой код. Вместо этого, как минимум, используйте что-то вроде базы данных SQLite на диске в качестве хранилища данных. Создайте поле «href», которое уникально, чтобы избежать многократного попадания на ту же страницу.

Используйте встроенный URI-класс Ruby или Addressable gem для анализа и обработки URL-адресов. Они сохраняют вашу работу и будут делать все правильно, когда вы начнете кодирование/декодирование запросов и попытаетесь нормализовать параметры, чтобы проверить уникальность, извлечение и управление путями и т. Д.

Многие сайты используют идентификаторы сеанса в запросе URL для идентификации посетителя.Этот идентификатор может сделать каждую ссылку разной, если вы начнете, затем остановитесь, затем запустите снова или если вы не вернете файлы cookie, полученные с сайта, так что вам нужно вернуть файлы cookie и выяснить, какие параметры запроса значительны, и какие собираются сбросить ваш код. Сохраните первый и выбросьте второй, когда вы храните ссылки для последующего анализа.

Используйте HTTP-клиент, такой как Typhoeus with Hydra, для одновременного получения нескольких страниц и сохранения их в своей базе данных с помощью отдельного процесса, который анализирует их и передает URL-адреса для анализа в базу данных. Это может существенно повлиять на общее время обработки.

Почитайте файл robots.txt сайта и подавите ваши запросы, чтобы избежать избиения своего сервера. Никто не любит полосчатые свиньи и потребляет значительную часть пропускной способности сайта или времени процессора без разрешения - это хороший способ, чтобы его заметили, а затем запретили. В этот момент ваш сайт достигнет нулевой пропускной способности.

+0

Это очень полезно; Спасибо. Множество, чтобы рассмотреть, узнать и реализовать! – twinturbotom

+0

+1 для 'robots.txt' и дросселирования (ни один из которых не включен в мой ответ). – Phrogz

Смежные вопросы