Как проверить, действителен ли URL

94

Как я могу проверить, является ли строка действительным URL-адресом?

Например:

http://hello.it => yes
http:||bra.ziz, => no

Если это действительный URL-адрес, как я могу проверить, относится ли он к файлу изображения?

Лука Романьоли
источник
предоставленный вами URL-адрес кажется абсолютным URL-адресом, что вы имеете в виду относительно файла изображения
Йоханнес

Ответы:

178

Используйте URIмодуль, поставляемый с Ruby:

require 'uri'

if url =~ URI::regexp
    # Correct URL
end

Как сказал Александр Гюнтер в комментариях, он проверяет, содержит ли строка URL.

Для того, чтобы проверить , если строка является URL - адрес, использование:

url =~ /\A#{URI::regexp}\z/

Если вы хотите проверить только веб-URL ( httpили https), используйте это:

url =~ /\A#{URI::regexp(['http', 'https'])}\z/
Микаэль С
источник
25
Похоже, это не работает: 'http://:5984/asdf' =~ URI::regexpи 'http::5984/asdf' =~ URI::regexpоба возвращают 0. Я ожидал, что они вернут nil, потому что ни один из них не является допустимым URI.
awendt
4
Разве нет: 5984 порт 5984 на локальном хосте?
mxcl 07
3
Фактически он проверяет, содержит ли переменная действительный URL-адрес. Он примет " example com" как действительный URL. Потому что он содержит один. Но это бесполезно, если вы ожидаете, что все это будет URL-адресом.
Александр Гюнтер
2
gotqn: Это не действительный URL согласно RFC 1738.
Mikael S
12
Не используйте это, это так плохо, что "http:"проходит это регулярное выражение.
smathy
43

Как и в ответах выше, я считаю, что это регулярное выражение немного более точное:

URI::DEFAULT_PARSER.regexp[:ABS_URI]

Это сделает недействительными URL-адреса с пробелами, в отличие от тех, URI.regexpкоторые по какой-то причине допускают пробелы.

Недавно я нашел ярлык, который предоставляется для различных URI rgexps. Вы можете получить доступ к любому из URI::DEFAULT_PARSER.regexp.keysфайлов прямо из URI::#{key}.

Например, к :ABS_URIрегулярному выражению можно получить доступ из URI::ABS_URI.

jonuts
источник
3
Если вы планируете использовать URI.parse в любой момент, это определенно лучший вариант. URI :: regexp соответствует определенным URL-адресам, которые не работают при последующем использовании URI.parse. Спасибо за чаевые.
markquezada
К сожалению, это доступно только на Ruby 1.9, но не на 1.8.
Стив Мэдсен,
1
Но это работает: /^#{URI.regexp}$/. Проблема в том, что URI.regexpэто не якорь. Строка с пробелом не проверяет пробел как часть URI, но все, что ведет к пробелу. Если этот фрагмент выглядит как действительный URI, совпадение считается успешным.
Стив Мэдсен,
3
Применение комментария Авендта к вашим предложениям: 'http://:5984/asdf' =~ URI::DEFAULT_PARSER.regexp[:ABS_URI]дает 0, а не ноль; 'http::5984/asdf'=~ URI::DEFAULT_PARSER.regexp[:ABS_URI]дает 0; 'http://:5984/asdf' =~ /^#{URI.regexp}$/дает 0; 'http::5984/asdf' =~ /^#{URI.regexp}$/также дает 0. Ни одно из вышеперечисленных регулярных выражений не является полностью правильным, однако они не работают только в очень странных ситуациях, и в большинстве случаев это не имеет большого значения.
skalee
1
FYI, URI::DEFAULT_PARSER.regexp[:ABS_URI]идентично/\A\s*#{URI::regexp}\s*\z/
aidan
36

Проблема с текущими ответами заключается в том, что URI не является URL-адресом .

URI можно дополнительно классифицировать как указатель, имя или и то, и другое. Термин «унифицированный указатель ресурса» (URL) относится к подмножеству идентификаторов URI, которые, помимо идентификации ресурса, предоставляют средства определения местоположения ресурса путем описания его основного механизма доступа (например, его сетевого «местоположения»).

Поскольку URL-адреса являются подмножеством URI, ясно, что сопоставление специально для URI будет успешно соответствовать нежелательным значениям. Например, URN :

 "urn:isbn:0451450523" =~ URI::regexp
 => 0 

При этом, насколько мне известно, в Ruby нет способа по умолчанию для анализа URL-адресов, поэтому для этого вам, скорее всего, понадобится гем. Если вам нужно сопоставить URL-адреса конкретно в формате HTTP или HTTPS, вы можете сделать что-то вроде этого:

uri = URI.parse(my_possible_url)
if uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)
  # do your stuff
end
фотанус
источник
@Philip Был полезным и уместным. Большое спасибо!
fotanus 09
2
uri.kind_of?(URI::HTTP)кажется достаточным для обоих случаев (http и https), по крайней мере, в ruby ​​1.9.3.
Андреа Саличетти
все еще страдает от проблем, описанных @skalee в ответе
jonuts
1
Резюме, URI.parse(string_to_be_checked).kind_of?(URI::HTTP)делает свою работу хорошо.
Бен
Кроме того, из-за очень частой опечатки в нашей базе данных люди часто ставят много слэшей:, http:///neopets.comчто, к сожалению, также верно. Это исправляет проверка наличия имени хоста:uri = URI(str) ; %w[http https].include?(uri.scheme) && !uri.host.nil?
Шейн
19

Я предпочитаю Addressable gem . Я обнаружил, что он обрабатывает URL-адреса более разумно.

require 'addressable/uri'

SCHEMES = %w(http https)

def valid_url?(url)
  parsed = Addressable::URI.parse(url) or return false
  SCHEMES.include?(parsed.scheme)
rescue Addressable::URI::InvalidURIError
  false
end
Дэвид Дж.
источник
3
Я просто загрузил в Addressable :: URI.parse () самые странные строки, чтобы увидеть, что он отклоняет. Он принимал безумные вещи. Однако первая строка, которую он не принял, была «:-)». Хм.
mvw
1
Как за это так много голосов? Addressable::URI.parseне возвращает nil с недопустимым вводом.
garbagecollector
11

Это довольно старая запись, но я решил пойти дальше и внести свой вклад:

String.class_eval do
    def is_valid_url?
        uri = URI.parse self
        uri.kind_of? URI::HTTP
    rescue URI::InvalidURIError
        false
    end
end

Теперь вы можете сделать что-то вроде:

if "http://www.omg.wtf".is_valid_url?
    p "huzzah!"
end
Вильгельм Мердок
источник
2
Это работает намного лучше, чем приведенные выше решения. Он не имеет перечисленных выше предостережений, а также не принимает uris, например javascript: alert ('spam').
bchurchill
2
но он также совпадает http:/, что может быть не тем, что вам нужно.
Bo Jeanes
11

Для меня я использую это регулярное выражение:

/^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/ix

Вариант:

  • i - без учета регистра
  • x - игнорировать пробелы в регулярном выражении

Вы можете установить этот метод для проверки валидации URL:

def valid_url?(url)
  return false if url.include?("<script")
  url_regexp = /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/ix
  url =~ url_regexp ? true : false
end

Чтобы использовать это:

valid_url?("http://stackoverflow.com/questions/1805761/check-if-url-is-valid-ruby")

Тестирование с неправильными URL-адресами:

  • http://ruby3arabi - результат неверный
  • http://http://ruby3arabi.com - результат неверный
  • http:// - результат неверный
  • http://test.com\n<script src=\"nasty.js\"> (Просто отметьте «<скрипт»)

Протестируйте с правильными URL-адресами:

  • http://ruby3arabi.com - результат действительный
  • http://www.ruby3arabi.com - результат действительный
  • https://www.ruby3arabi.com - результат действительный
  • https://www.ruby3arabi.com/article/1 - результат действительный
  • https://www.ruby3arabi.com/websites/58e212ff6d275e4bf9000000?locale=en - результат действительный
Комсун К.
источник
Следующее помечено как действительное:, "http://test.com\n<script src=\"nasty.js\">"и любой домен, который использует один из 683 TLD , длина которого превышает 5 символов, или имеет два или более последовательных дефиса, помечается как недопустимый. Допускаются номера портов вне диапазона 0-65535. FTP и IP-адреса явно запрещены, но стоит отметить.
aidan
1
это просто лучшее, наиболее применимое решение для быстрой проверки URL. спасибо
somedirection
4

Это немного устарело, но вот как я это делаю. Используйте модуль URI Ruby для анализа URL-адреса. Если его можно проанализировать, значит, это действительный URL. (Но это не значит, что доступно.)

URI поддерживает множество схем, плюс вы можете добавлять собственные схемы самостоятельно:

irb> uri = URI.parse "http://hello.it" rescue nil
=> #<URI::HTTP:0x10755c50 URL:http://hello.it>

irb> uri.instance_values
=> {"fragment"=>nil,
 "registry"=>nil,
 "scheme"=>"http",
 "query"=>nil,
 "port"=>80,
 "path"=>"",
 "host"=>"hello.it",
 "password"=>nil,
 "user"=>nil,
 "opaque"=>nil}

irb> uri = URI.parse "http:||bra.ziz" rescue nil
=> nil


irb> uri = URI.parse "ssh://hello.it:5888" rescue nil
=> #<URI::Generic:0x105fe938 URL:ssh://hello.it:5888>
[26] pry(main)> uri.instance_values
=> {"fragment"=>nil,
 "registry"=>nil,
 "scheme"=>"ssh",
 "query"=>nil,
 "port"=>5888,
 "path"=>"",
 "host"=>"hello.it",
 "password"=>nil,
 "user"=>nil,
 "opaque"=>nil}

См. Документацию для получения дополнительной информации о модуле URI.

нюзм
источник
Я столкнулся с этим, пытаясь исправить ошибку сегментации. На URI.parseсамом деле причиной этого было использование в Ruby 2.5.5 - я переключился на ответ @jonuts ниже, если вы не возражаете против некоторых странных случаев, которые могут произойти. Для моих целей мне было все равно, так что это было идеально.
el n00b,
3

В общем,

/^#{URI::regexp}$/

будет работать хорошо, но если вы хотите только сопоставить httpили https, вы можете передать их как параметры методу:

/^#{URI::regexp(%w(http https))}$/

Это работает немного лучше, если вы хотите отклонить такие протоколы, как ftp://.

user2275806
источник
-2

Вы также можете использовать регулярное выражение, возможно, что-то вроде http://www.geekzilla.co.uk/View2D3B0109-C1B2-4B4E-BFFD-E8088CBC85FD.htm при условии, что это регулярное выражение правильное (я не полностью его проверил), следующее будет показать действительность URL.

url_regex = Regexp.new("((https?|ftp|file):((//)|(\\\\))+[\w\d:\#@%/;$()~_?\+-=\\\\.&]*)")

urls = [
    "http://hello.it",
    "http:||bra.ziz"
]

urls.each { |url|
    if url =~ url_regex then
        puts "%s is valid" % url
    else
        puts "%s not valid" % url
    end
}

Приведенный выше пример выводит:

http://hello.it is valid
http:||bra.ziz not valid
Джейми
источник
5
А как насчет схемы mailto? Или telnet, gopher, nntp, rsync, ssh или какие-то другие схемы? URL-адреса немного сложнее, чем просто HTTP и FTP.
mu слишком короткое
Написать регулярное выражение для проверки URL-адресов сложно. Зачем беспокоиться?
Rimian
@Rimian, ты должен потрудиться, потому что все, что URIможно сделать, фактически сломано. Смотрите комментарии под столькими ответами, за которые проголосовали выше. Не уверен, что ответ Джени правильный, но, надеюсь, люди воспринимают его более серьезно. TBH Я в конечном итоге делаю это, url.start_with?("http://") || url.start_with?("https://")потому что мне нужен только HTTP, и пользователи должны нести ответственность за использование правильных URL-адресов.
akostadinov