2011-05-06 109 views
43

如何检查使用Ruby的URL是否存在?检查Ruby中是否存在URL

例如,对于URL

https://google.com 

结果应该是truthy,但对于网址

https://no.such.domain 

https://stackoverflow.com/no/such/path 

结果应该是falsey

+3

问题是不够好,符合我的谷歌搜索,答案是有价值 – kranzky 2017-01-27 04:13:00

+0

我同意。这个问题很有用。 – 2017-03-24 01:43:40

+0

我认为这是一个有用的答案很好的问题。它被关闭的原因(“必须证明最低限度的理解”)在SO上不再有效。我编辑了这个问题来添加一些例子。那么,我认为这个问题现在可以重新开放了。 – 2017-07-08 16:45:21

回答

55

使用Net::HTTP库。

require "net/http" 
url = URI.parse("http://www.google.com/") 
req = Net::HTTP.new(url.host, url.port) 
res = req.request_head(url.path) 

此时res是包含请求的结果的Net::HTTPResponse对象。然后,您可以检查响应代码:

do_something_with_it(url) if res.code == "200" 

注意:要检查https基于URL,use_ssl属性应该是true为:

require "net/http" 
url = URI.parse("https://www.google.com/") 
req = Net::HTTP.new(url.host, url.port) 
req.use_ssl = true 
res = req.request_head(url.path) 
+3

重定向怎么办? 302 http代码 – 2014-03-23 19:07:44

+0

在生产中,对于每一个URL,这是返回我200代码.. 我已经解析了这个URL,并给了我200 OK ...但是哪个是错的......这里有什么问题?任何想法? 注意:这在Local Env中工作正常。 – 2016-10-06 11:34:40

3

Simone的回答是对我很有帮助。

下面是根据URL有效性返回true/false一个版本,它处理重定向:

require 'net/http' 
require 'set' 

def working_url?(url, max_redirects=6) 
    response = nil 
    seen = Set.new 
    loop do 
    url = URI.parse(url) 
    break if seen.include? url.to_s 
    break if seen.size > max_redirects 
    seen.add(url.to_s) 
    response = Net::HTTP.new(url.host, url.port).request_head(url.path) 
    if response.kind_of?(Net::HTTPRedirection) 
     url = response['location'] 
    else 
     break 
    end 
    end 
    response.kind_of?(Net::HTTPSuccess) && url.to_s 
end 
+0

如果服务器不支持HEAD请求,该怎么办? – 2017-11-12 16:04:46

20

Net::HTTP的作品,但如果你能在外面工作STDLIB,Faraday更好。

Faraday.head(the_url).status == 200 

(200是成功的代码,假设这是你所说的“存在”。意思)

+1

为什么你认为它更好? – Dennis 2014-07-04 17:53:15

+2

您也可以使用[RestClient库](https://github.com/rest-client/rest-client)。 '需要'rest_client'; RestClient.head(url).code!= 404' – Dennis 2014-07-04 18:35:12

43

对不起,这个后期的答复,但我认为这deservers一个更好的答案。

有三种方式来看待这个问题:

  1. 严格检查URL是否存在
  2. 检查您所请求的网址correclty
  3. 检查,如果你能正确地要求它与服务器能回答它正确

1.严格检查是否存在URL

虽然200意味着服务器回答该URL(因此该URL存在),但回答其他状态码并不意味着该URL不存在。例如,回答302 - redirected意味着该URL存在并正在重定向到另一个URL。浏览时,302多次表现与最终用户的200相同。如果URL存在,可以返回的其他状态代码是500 - internal server error。毕竟,如果URL不存在,那么应用程序服务器如何处理您的请求,而仅返回404 - not found

所以实际上只有一种情况下,当一个URL不存在时:当服务器不存在或当服务器存在但无法找到给定的URL路径不存在。 因此,检查URL是否存在的唯一方法是检查服务器是否应答并且返回代码不是404.以下代码就是这样做的。

require "net/http" 
def url_exist?(url_string) 
    url = URI.parse(url_string) 
    req = Net::HTTP.new(url.host, url.port) 
    req.use_ssl = (url.scheme == 'https') 
    path = url.path if url.path.present? 
    res = req.request_head(path || '/') 
    res.code != "404" # false if returns 404 - not found 
rescue Errno::ENOENT 
    false # false if can't find the server 
end 

2.检查您所请求的网址correclty

然而,大部分的时间我们都没有兴趣看有没有存在的网址,但如果能访问它。幸运的是,看到HTTP status codes系列,即4xx系列,其中声明客户端错误(因此,您身边的错误,这意味着您没有正确请求页面,没有任何权限)。这是一个很好的错误来检查您是否可以访问此页面。从维基:

The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server should include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. These status codes are applicable to any request method. User agents should display any included entity to the user.

所以下面的代码确认网址存在,并且您可以访问它

require "net/http" 
def url_exist?(url_string) 
    url = URI.parse(url_string) 
    req = Net::HTTP.new(url.host, url.port) 
    req.use_ssl = (url.scheme == 'https') 
    path = url.path if url.path.present? 
    res = req.request_head(path || '/') 
    if res.kind_of?(Net::HTTPRedirection) 
    url_exist?(res['location']) # Go after any redirect and make sure you can access the redirected URL 
    else 
    res.code[0] != "4" #false if http code starts with 4 - error on your side. 
    end 
rescue Errno::ENOENT 
    false #false if can't find the server 
end 

3.检查,如果你能正确地要求它和服务器能够回答正确

就像4xx家族检查您是否可以访问URL一样,5xx系列会检查服务器是否有任何问题回答您的请求。大多数时候这个家族的错误是服务器本身的问题,希望他们正在努力解决它。如果您需要能够访问该页面并现在得到正确的答案,您应该确保答案不是来自4xx5xx系列,并且如果您被重定向,则重定向页面可以正确回答。如此多的相似(2),你可以简单地使用下面的代码:

require "net/http" 
def url_exist?(url_string) 
    url = URI.parse(url_string) 
    req = Net::HTTP.new(url.host, url.port) 
    req.use_ssl = (url.scheme == 'https') 
    path = url.path if url.path.present? 
    res = req.request_head(path || '/') 
    if res.kind_of?(Net::HTTPRedirection) 
    url_exist?(res['location']) # Go after any redirect and make sure you can access the redirected URL 
    else 
    ! %W(4 5).include?(res.code[0]) # Not from 4xx or 5xx families 
    end 
rescue Errno::ENOENT 
    false #false if can't find the server 
end 
+1

如果你用https-urls做这个,你可能会得到一个'Net :: HTTPBadResponse:错误的状态行'错误。这是因为你必须告诉Net:HTTP来使用ssl。为了使它适用于https,也可以使用'req.use_ssl =(url。方案=='https')'调用'request_head'之前 – 2014-01-06 08:29:13

+0

@YoLudke感谢您的贡献 – fotanus 2014-01-06 10:23:57

+1

另一件事:如果您请求(或重定向转到)'http://www.example.com'(不带尾部' /'),那么你会得到一个'ArgumentError:HTTP请求路径为空'。这可以通过将'res = req.request_head(url.path)'行改为'path = url.path if url.path.present?'和'req.request_head(path ||'/')' – 2014-01-08 08:49:04