Como verificar se um URL é válido


93

Como posso verificar se uma string é um URL válido?

Por exemplo:

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

Se este for um URL válido, como posso verificar se ele é relativo a um arquivo de imagem?


o url que você forneceu parece ser um url absoluto, o que você quer dizer com em relação a um arquivo de imagem
johannes

Respostas:


177

Use o URImódulo distribuído com Ruby:

require 'uri'

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

Como Alexander Günther disse nos comentários, ele verifica se uma string contém um URL.

Para verificar se a string é um URL, use:

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

Se você deseja verificar apenas URLs da web ( httpou https), use:

url =~ /\A#{URI::regexp(['http', 'https'])}\z/

24
Isso não parece funcionar: 'http://:5984/asdf' =~ URI::regexpe 'http::5984/asdf' =~ URI::regexpambos retornam 0. Eu esperava que eles retornassem nil porque nenhum deles é URIs válidos.
awendt

4
Não é: 5984 porta 5984 no localhost?
mxcl

3
Na verdade, ele verifica se uma variável contém um url válido. Ele aceitará " example com" como um URL válido. Porque contém um. Mas não é útil se você espera que tudo seja o URL.
Alexander Günther

2
gotqn: No entanto, esse não é um URL válido de acordo com o RFC 1738.
Mikael S

12
Não use isso, é tão ruim que "http:"passa esse regexp.
smathy

43

Semelhante às respostas acima, acho que usar esta regex é um pouco mais preciso:

URI::DEFAULT_PARSER.regexp[:ABS_URI]

Isso invalidará URLs com espaços, em vez de URI.regexppermitir espaços por algum motivo.

Recentemente encontrei um atalho fornecido para os diferentes rgexps de URI. Você pode acessar qualquer um URI::DEFAULT_PARSER.regexp.keysdiretamente de URI::#{key}.

Por exemplo, o :ABS_URIregexp pode ser acessado de URI::ABS_URI.


3
Se você planeja usar URI.parse em qualquer ponto, este é definitivamente o caminho a percorrer. URI :: regexp corresponde a certos URLs que falharão ao usar URI.parse posteriormente. Obrigado pela dica.
markquezada de

Infelizmente, isso só está disponível no Ruby 1.9, não 1.8.
Steve Madsen de

1
Mas, isso funciona: /^#{URI.regexp}$/. O problema é que URI.regexpisso não ancora. Uma string com um espaço não está validando o espaço como parte do URI, mas tudo que leva ao espaço. Se esse fragmento parecer um URI válido, a correspondência será bem-sucedida.
Steve Madsen de

3
Aplicar o comentário de awendt às suas propostas: 'http://:5984/asdf' =~ URI::DEFAULT_PARSER.regexp[:ABS_URI]dá 0, não nulo; 'http::5984/asdf'=~ URI::DEFAULT_PARSER.regexp[:ABS_URI]dá 0; 'http://:5984/asdf' =~ /^#{URI.regexp}$/dá 0; 'http::5984/asdf' =~ /^#{URI.regexp}$/dá 0 também. Nenhuma das expressões regulares acima está totalmente correta, no entanto, elas falham apenas em situações muito estranhas e isso não é um grande problema na maioria dos casos.
skalee

1
Para sua informação, URI::DEFAULT_PARSER.regexp[:ABS_URI]é idêntico a/\A\s*#{URI::regexp}\s*\z/
aidan

34

O problema com as respostas atuais é que um URI não é um URL .

Um URI pode ser classificado como um localizador, um nome ou ambos. O termo "Uniform Resource Locator" (URL) refere-se ao subconjunto de URIs que, além de identificar um recurso, fornecem um meio de localizar o recurso, descrevendo seu mecanismo de acesso primário (por exemplo, sua "localização" de rede).

Como os URLs são um subconjunto de URIs, está claro que a correspondência específica para URIs corresponderá com êxito a valores indesejados. Por exemplo, URNs :

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

Dito isso, até onde eu sei, Ruby não tem uma maneira padrão de analisar URLs, então você provavelmente precisará de uma gema para fazer isso. Se precisar corresponder URLs especificamente no formato HTTP ou HTTPS, você pode fazer algo assim:

uri = URI.parse(my_possible_url)
if uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)
  # do your stuff
end

@Philip foi útil e apropriado. Muito obrigado!
fotanus

2
uri.kind_of?(URI::HTTP)parece ser suficiente para ambos os casos (http e https), pelo menos em ruby ​​1.9.3.
Andrea Salicetti

ainda sofre os problemas descritos por @skalee sob a resposta do
jonuts

1
Resumo, URI.parse(string_to_be_checked).kind_of?(URI::HTTP)faz bem o trabalho.
ben

19

Eu prefiro a gema endereçável . Eu descobri que ele lida com URLs de forma mais inteligente.

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
Acabei de alimentar Addressable :: URI.parse () com as strings mais estranhas para ver o que ele rejeita. Aceitou coisas malucas. No entanto, a primeira string que não aceitou foi ":-)". Hmm.
mvw

1
Como isso consegue tantos votos positivos? Addressable::URI.parsenão retorna nulo com entrada inválida.
garbagecollector

11

Esta é uma entrada bastante antiga, mas pensei em prosseguir e contribuir:

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

Agora você pode fazer algo como:

if "http://www.omg.wtf".is_valid_url?
    p "huzzah!"
end

2
Isso funciona muito melhor do que as soluções acima. Ele não tem as advertências listadas acima e também não aceita URLs como javascript: alert ('spam').
bchurchill de

2
mas também corresponde http:/, o que pode não ser o que você deseja.
Bo Jeanes

10

Para mim, uso esta expressão regular:

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

Opção:

  • i - não diferencia maiúsculas de minúsculas
  • x - ignorar espaços em branco no regex

Você pode definir este método para verificar a validação de URL:

def valid_url?(url)
  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

Para usá-lo:

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

Testando com URLs errados:

  • http://ruby3arabi - o resultado é inválido
  • http://http://ruby3arabi.com - o resultado é inválido
  • http:// - o resultado é inválido

Teste com URLs corretos:

  • http://ruby3arabi.com - o resultado é válido
  • http://www.ruby3arabi.com - o resultado é válido
  • https://www.ruby3arabi.com - o resultado é válido
  • https://www.ruby3arabi.com/article/1 - o resultado é válido
  • https://www.ruby3arabi.com/websites/58e212ff6d275e4bf9000000?locale=en - o resultado é válido

O seguinte é marcado como válido: "http://test.com\n<script src=\"nasty.js\">"e qualquer domínio que use um dos 683 TLDs com mais de 5 caracteres ou dois ou mais hifens consecutivos é marcado como inválido. Números de porta fora do intervalo 0-65535 são permitidos. Os endereços FTP e IP obviamente não são permitidos, mas vale a pena observar.
aidan

1
facilmente a melhor solução mais aplicável aqui para verificação rápida de url. obrigado
alguma direção

4

Isso é um pouco antigo, mas aqui está como eu faço. Use o módulo URI do Ruby para analisar a URL. Se puder ser analisado, é um URL válido. (Mas isso não significa acessível.)

O URI suporta muitos esquemas, além disso, você mesmo pode adicionar esquemas personalizados:

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}

Consulte a documentação para obter mais informações sobre o módulo URI.


Corri para tentar consertar um segfault. O uso URI.parsefoi na verdade a causa disso no Ruby 2.5.5 - eu mudei para a resposta @jonuts abaixo se você não se importa com alguns casos estranhos que não aparecem. Para meus propósitos, não me importava, então isso era o ideal.
el n00b

3

Em geral,

/^#{URI::regexp}$/

funcionará bem, mas se você quiser apenas fazer a correspondência httpou https, poderá passá-los como opções para o método:

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

Isso tende a funcionar um pouco melhor, se você quiser rejeitar protocolos como ftp://.


-2

Você também pode usar um regex, talvez algo como http://www.geekzilla.co.uk/View2D3B0109-C1B2-4B4E-BFFD-E8088CBC85FD.htm presumindo que este regex esteja correto (eu não verifiquei totalmente) o seguinte irá mostrar a validade do 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
}

O exemplo acima resulta:

http://hello.it is valid
http:||bra.ziz not valid

5
E quanto ao esquema mailto? Ou telnet, gopher, nntp, rsync, ssh ou qualquer um dos outros esquemas? URLs são um pouco mais complicados do que apenas HTTP e FTP.
mu é muito curto

Escrever regex para validar URLs é difícil. Porque se importar?
Rimian de

@Rimian, você precisa se preocupar porque tudo o que URIposso fazer está de fato quebrado. Veja os comentários nas tantas respostas votadas acima. Não tenho certeza se a resposta de Janie está certa, mas estou votando, então espero que as pessoas considerem isso mais seriamente. Acabo fazendo TBH url.start_with?("http://") || url.start_with?("https://")porque preciso apenas de HTTP e os usuários devem ser responsáveis ​​por usar URLs adequados.
akostadinov
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.