Подтвердить что ты не робот

Проверьте, существует ли URL в Ruby

Как я могу проверить, существует ли URL с Ruby?

Например, для URL

https://google.com

результат должен быть правдивым, но для URL

https://no.such.domain

или

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

результат должен быть ложным

4b9b3361

Ответ 1

Используйте библиотеку 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"

Примечание. Чтобы проверить URL-адрес на основе https, атрибут 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)

Ответ 2

Извините за поздний ответ на это, но я думаю, что это заслуживает лучшего ответа.

Есть три способа взглянуть на этот вопрос:

  • Строго проверить, существует ли URL-адрес.
  • Убедитесь, что вы запрашиваете URL-адрес соответствия
  • Проверьте, можете ли вы запросить его правильно, и сервер может ответить на него правильно.

1. Строго проверить, существует ли URL

Пока 200 означает, что сервер отвечает на этот URL (таким образом, существует URL-адрес), ответ на другой код состояния не означает, что URL-адрес не существует. Например, ответ 302 - redirected означает, что 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. Проверьте, запрашиваете ли вы URL-адрес соответствия

Однако в большинстве случаев нам неинтересно видеть, существует ли URL-адрес, но если мы можем получить к нему доступ. К счастью, глядя на коды состояния HTTP, это семейство 4xx, которое указывает на ошибку клиента (таким образом, ошибка на вашей стороне, что означает, что вы не запрашиваете страницу правильно, не имеете разрешения или вообще что-либо). Это полезно для ошибок, чтобы проверить, можете ли вы получить доступ к этой странице. Из wiki:

Класс кода класса 4xx предназначен для случаев, когда клиент, похоже, ошибся. За исключением случаев, когда он отвечает на запрос HEAD, сервер должен включать в себя объект, содержащий объяснение ситуации ошибки, и является ли это временным или постоянным условием. Эти коды состояния применимы к любому методу запроса. Пользовательские агенты должны отображать для пользователя какой-либо объект.

Итак, следующий код убедитесь, что URL существует, и вы можете получить к нему доступ:

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 проверяет, не возникла ли на сервере проблема с ответом на ваш запрос. Ошибка в этом семействе в большинстве случаев связана с проблемами на самом сервере, и, надеюсь, они работают над его решением. Если Вам нужно иметь доступ к странице и получить правильный ответ сейчас, вы должны убедиться, что ответ не из семейства 4xx или 5xx, и если вы были перенаправлены, перенаправленный правильные ответы на страницы. Подобно (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

Ответ 3

Net::HTTP работает, но если вы можете работать за пределами stdlib, Faraday лучше.

Faraday.head(the_url).status == 200

(200 - это код успеха, предполагающий, что вы имели в виду "существует".)

Ответ 5

Ответ Симоны был очень полезен для меня.

Вот версия, которая возвращает true/false в зависимости от действительности URL-адреса и обрабатывает перенаправления:

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