【问题标题】:Ruby - Opening absolute url from relativeRuby - 从相对打开绝对网址
【发布时间】:2014-08-27 10:57:08
【问题描述】:

我已经使用 open-uri 和 nokogiri 构建了一个网络爬虫 ruby​​ 脚本,我对这一切都很陌生,但它适用于我需要从源中具有完整 URL 的几个网站中提取数据,除了一个使用相对 URL 的。

脚本的作用是打开页面,构建要打开的页面数组,然后遍历并从 css(不是 xpath)中提取数据。

我如何强制脚本在它们是相对的实例中使用完整的 URL,这一直困扰着我一段时间,我似乎无法让它运行

在我的情况下,我认为我需要在它推送 URL 时添加一些内容,有人可以指出我正确的方向吗?将不胜感激!谢谢!

require 'open-uri'
require 'nokogiri'

PAGE_URL = "http://www.OMMITED.co.uk"

page = Nokogiri::HTML(open(PAGE_URL, "User-Agent" => “OMMITED“))

links = page.css("a")

links_array = Array.new

links.each{|link|
        url = link['href'].nil? ? 'empty' : link['href']
        if url.include? 'category'  and !url.include? '/all'
                links_array.push url
        end
}

【问题讨论】:

  • 您可以使用正则表达式来检查完全限定的 URL,例如 /^[\w]*:\/\// - 如果匹配,则添加根 URL。记住在相对 URL 开头的“/”的微妙之处表示它从服务器之后路径的根开始,而不是不带斜杠的相对链接开始,它与当前页面的目录匹配。我会写一个答案,但我需要更多关于可用变量的知识。 (主要是当前全限定URL和当前页面路径。)
  • 编辑添加变量!

标签: ruby url nokogiri relative-path open-uri


【解决方案1】:

tl;dr:底部的简短回答。

好的,假设您有一个名为 @url 的类变量,其中包含当前页面的完全限定 URL:

require 'uri'

def full_url(rel, url)
  return rel if rel.match /^[\w]*:\/\//
  uri = URI(url)
  if rel[0] == '/'
    "#{uri.scheme}://#{uri.host}#{rel}"
  else
    path = uri.path.split('/')[0..-2].select{|m| !m.empty?}.join('/')
    "#{uri.scheme}://#{uri.host}/#{path}/#{rel}"
  end
end

然后你可以调用:

links_array.push full_url(url, @url)

您可以将方法放在同一个类或某个辅助类中。它使用 Ruby URI 库查找完全限定 URL 的相关部分,然后从相对路径构造一个新部分。

如果相对路径以 '/' 开头,它应该直接在主机之后。

如果它不以“/”开头,则它需要与当前页面位于同一虚拟目录中。因此,如果当前页面是:

http://www.host.com/aaa/bbb/ccc

相对路径为:

ddd

那么输出应该是:

http://www.host.com/aaa/bbb/ddd

但是,如果相对路径是:

/ddd

那么输出应该是:

http://www.host.com/ddd

代码:

uri.path.split('/')[0..-2].select{|m| !m.empty?}.join('/')

获取完整 URL 的路径,将其拆分为 '/' 给出一个数组 (['aaa','bbb', 'ccc']),然后删除最后一个元素。 (['aaa','bbb'])。选择删除任何空白元素,然后连接再次将其缝合。 ("aaa/bbb")

你可以用无聊的方式来做:

require 'uri'

URI.join("http://www.host.com/aaa/bbb/ccc", "/ddd").to_s
# => "http://www.host.com/ddd" 

URI.join("http://www.host.com/aaa/bbb/ccc", "ddd").to_s
# => "http://www.host.com/aaa/bbb/ddd" 

给定您的代码:

links.each{|link|
    url = link['href'].nil? ? 'empty' : link['href']
    if url.include? 'category'  and !url.include? '/all'
            links_array.push url
    end
}

我会改写为:

links.each do |link|
  url = link['href'].nil? ? 'empty' : link['href']
  if url.include? 'category' && !url.include? '/all'
    full_url = URI.join(PAGE_URL, url).to_s 
    puts full_url
    links_array << url
    puts links_array.inspect
  end
end

注意:在风格上,多行块应该使用 do/end 而不是 {}。缩进应该是两个空格。括号内不应该有空格。 and,后者的优先级要低得多,并且可能会导致问题。请参阅 Github 样式指南:

https://github.com/styleguide/ruby

puts 基于您的 cmets,希望能帮助您找出您的阵列不正常运行的原因。应该是,根据您输入的代码。不过,我更喜欢使用调试器 gem。 (或者如果你使用的是 Ruby 2.x,byebug)

【讨论】:

  • 感谢您的回复,我添加了您的代码并添加了一个 @url 变量,这导致停止 No such file or directory 错误,我认为这表明它将两个值放在一起,但它似乎没有打开我需要查看的 URL 数组,我会尝试修复它并回来确认您的解决方案是否有效!谢谢!
  • 检查答案的编辑结束,它显示了一个更简单和更清洁的解决方案。
  • 受你上一条评论的启发,我想出了这个code @full_url = URI.join( PAGE_URL, url ).to_s puts @full_url links_array.push @full_url end } code它正确加入(puts 正在打印完整的 URL),但它看起来不像是传递给数组。你能在这里看到任何可以解释的错误吗?
  • 我会更新问题,请耐心等待。同时,使用[] 来初始化一个数组:my_array = [] 而不是my_array = Array.new。我们不是在编写 Java 代码。 ;) 此外,您可以在要在 cmets 中突出显示的代码周围使用反引号,而不是围绕“代码”一词。 :)
  • 答案已更新,虽然我看不到问题 - 除了我提到的风格问题。
猜你喜欢
  • 2010-10-28
  • 2012-04-09
  • 2012-01-03
  • 1970-01-01
  • 2019-06-25
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多