ruby - 使用 nokogiri 干搜索网站的每一页

标签 ruby web-scraping web-crawler nokogiri dry

我想搜索网站的每一页。我的想法是找到一个页面上保留在域内的所有链接,访问它们,然后重复。我也必须采取措施,不再重蹈覆辙。

所以它很容易开始:

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

我还在研究最后一点……但这看起来是正确的方法吗?

谢谢。

最佳答案

其他人建议您不要编写自己的网络爬虫。 如果性能和稳健性是您的目标,我同意这一点。但是,这可能是一个很好的学习练习。你写了这个:

"[…] but I don't know the best way to ensure I don't repeat myself"

递归是这里的关键。类似于下面的代码:

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。
  • 使用递归来继续抓取每个页面上的每个链接,但如果您已经看过该页面,则退出。

关于ruby - 使用 nokogiri 干搜索网站的每一页,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17035503/

相关文章:

ruby-on-rails - Rails 预编译index.html.erb

javascript - 使用 Python 抓取 Meteor

web-scraping - 使用 AutoHotKey 查找并填充输入字段

python - 抓取特定文件类型的网页

python - Scrapy make_requests_from_url(url)

ruby-on-rails - 如何避免 `#{str}` 中的安全问题

ruby - 从 Rack 应用程序提供非公共(public)二进制文件

ruby-on-rails - Rails 在 View 上使用之前强制声明实例变量

c# - 如何使用 C# 从 html 页面中抓取文本?

web-crawler - 确定使用技术构建的最佳爬虫?