2011-05-06 108 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