转换为可以通过open-uri打开的有效URL

时间:2013-11-20 07:47:56

标签: ruby

我需要在ruby中使用open-uri打开一些网页,然后使用Nokogori解析这些页面的内容。 我刚刚做了:

 require 'open-uri'
 content_file = open(user_input_url)

这适用于:http://www.google.co.inhttp://google.co.in,但在用户提供www.google.co.ingoogle.co.in等输入时失败。

我可以对此类输入执行一项操作,我可以附加http://https://并返回打开的页面内容。但这对我来说似乎是个大黑客。 有没有更好的方法在ruby中实现这一点(即将这些user_input转换为有效的open_uri url)。

4 个答案:

答案 0 :(得分:7)

uri = URI("www.google.com")
if uri.instance_of?(URI::Generic)
    uri = URI::HTTP.build({:host => uri.to_s}) 
end
content_file = open(uri)

还有其他方法可以参见参考:http://www.ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/HTTP.html

答案 1 :(得分:3)

如果不存在,则预先添加该方案,然后使用URI来检查URL有效性:

require 'uri'

url = 'www.google.com/a/b?c=d#e'
url.prepend "http://" unless url.start_with?('http://', 'https://')
url = URI(url) # it will raise error if the url is not valid
open url

不幸的是,你所需要的“面向对象”版本更冗长,甚至更具黑客性:

require 'uri'

case url = URI.parse 'www.google.com/a/b?c=d#e'
when URI::HTTP, URI::HTTPS
  # no-op
when URI::Generic
  # We need to split u.path at the first '/', since URI::Generic interprets
  # 'www.google.com/a/b' as a single path
  host, path = url.path.split '/', 2
  url = URI::HTTP.build host:     host         ,
                        path:     "/#{path}"   ,
                        query:    url.query    ,
                        fragment: url.fragment
else
  raise "unsupported url class (#{url.class}) for #{url}"
end
open url

如果您接受建议,请不要过分夸大其词:我经常遇到这个问题而且我很确定没有“抛光”的方法来做这件事

答案 2 :(得分:1)

你需要在网址前加上http,没有明确的方案,uri可能是任何东西,例如:本地文件。 uri不一定是http url。

您可以使用URI类或使用正则表达式来检查:

user_input_url = URI.parse(user_input_url).scheme ?
    user_input_url :
    "http://#{user_input_url}"

user_input_url = user_input_url =~ /https?:\/\// ?
    user_input_url :
    "http://#{user_input_url}"

答案 3 :(得分:0)

def instance_to_hash(instance)
  hash = {}
  instance.instance_variables.each {|var| hash[var[1..-1].to_sym] = instance.instance_variable_get(var) }
  hash
end

def url_compile(url)
  # if url without 'http://', 'https://', '//' at start of string
  # then prepend '//'
  url.prepend '//' unless url.start_with?('http://', 'https://', '//')
  uri = URI(url)
  if uri.instance_of?(URI::Generic) # if scheme nil then assume it HTTPS
    uri = URI::HTTPS.build(instance_to_hash(uri))
  end
  uri
end