Cómo comprobar si una URL es válida

94

¿Cómo puedo comprobar si una cadena es una URL válida?

Por ejemplo:

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

Si esta es una URL válida, ¿cómo puedo verificar si es relativa a un archivo de imagen?

Luca Romagnoli
fuente
la URL que proporcionó parece ser una URL absoluta, ¿qué quiere decir con relativa a un archivo de imagen
johannes

Respuestas:

178

Utilice el URImódulo distribuido con Ruby:

require 'uri'

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

Como dijo Alexander Günther en los comentarios, verifica si una cadena contiene una URL.

Para verificar si la cadena es una URL, use:

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

Si solo desea verificar las URL web ( httpo https), use esto:

url =~ /\A#{URI::regexp(['http', 'https'])}\z/
Mikael S
fuente
25
Eso no parece funcionar: 'http://:5984/asdf' =~ URI::regexpy 'http::5984/asdf' =~ URI::regexpambos devuelven 0. Esperaba que devolvieran nil porque ninguno de ellos es URI válido.
awendt
4
¿No es 5984 puerto 5984 en localhost?
mxcl
3
En realidad, comprueba si una variable contiene una URL válida. Aceptará " example com" como una URL válida. Porque contiene uno. Pero no es útil si espera que todo sea la URL.
Alexander Günther
2
gotqn: Sin embargo, esa no es una URL válida según RFC 1738.
Mikael S
12
No uses esto, es tan malo que "http:"pase esta expresión regular.
smathy
43

De manera similar a las respuestas anteriores, encuentro que usar esta expresión regular es un poco más preciso:

URI::DEFAULT_PARSER.regexp[:ABS_URI]

Eso invalidará las URL con espacios, a diferencia de lo URI.regexpque permite espacios por alguna razón.

Recientemente encontré un atajo que se proporciona para los diferentes rgexps de URI. Puede acceder a cualquiera de URI::DEFAULT_PARSER.regexp.keysdirectamente desde URI::#{key}.

Por ejemplo, :ABS_URIse puede acceder a la expresión regular desde URI::ABS_URI.

jonuts
fuente
3
Si planea usar URI.parse en cualquier momento, este es definitivamente el camino a seguir. URI :: regexp coincide con ciertas URL que fallarán cuando luego se use URI.parse. Gracias por el consejo.
markquezada
Lamentablemente, esto solo está disponible en Ruby 1.9, no 1.8.
Steve Madsen
1
Sin embargo, esto funciona: /^#{URI.regexp}$/. El problema es que URI.regexpno se ancla. Una cadena con un espacio no valida el espacio como parte del URI, sino todo lo que conduce al espacio. Si ese fragmento parece un URI válido, la coincidencia se realiza correctamente.
Steve Madsen
3
Aplicando el comentario de awendt a sus propuestas: 'http://:5984/asdf' =~ URI::DEFAULT_PARSER.regexp[:ABS_URI]da 0, no nil; 'http::5984/asdf'=~ URI::DEFAULT_PARSER.regexp[:ABS_URI]da 0; 'http://:5984/asdf' =~ /^#{URI.regexp}$/da 0; 'http::5984/asdf' =~ /^#{URI.regexp}$/da 0 también. Ninguna de las expresiones regulares anteriores es completamente correcta, sin embargo, fallan solo en situaciones muy extrañas y esto no es un gran problema en la mayoría de los casos.
skalee
1
FYI, URI::DEFAULT_PARSER.regexp[:ABS_URI]es idéntico a/\A\s*#{URI::regexp}\s*\z/
aidan
36

El problema con las respuestas actuales es que un URI no es una URL .

Un URI se puede clasificar además como localizador, nombre o ambos. El término "Localizador Uniforme de Recursos" (URL) se refiere al subconjunto de URI que, además de identificar un recurso, proporciona un medio para localizar el recurso describiendo su mecanismo de acceso principal (por ejemplo, su "ubicación" de red).

Dado que las URL son un subconjunto de URI, está claro que la coincidencia específica para URI coincidirá con los valores no deseados. Por ejemplo, URN :

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

Dicho esto, hasta donde yo sé, Ruby no tiene una forma predeterminada de analizar las URL, por lo que lo más probable es que necesite una joya para hacerlo. Si necesita hacer coincidir las URL específicamente en formato HTTP o HTTPS, puede hacer algo como esto:

uri = URI.parse(my_possible_url)
if uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS)
  # do your stuff
end
fotanus
fuente
@Philip Fue útil y apropiado. ¡Muchas gracias!
fotanus
2
uri.kind_of?(URI::HTTP)parece ser suficiente para ambos casos (http y https), al menos en ruby ​​1.9.3.
Andrea Salicetti
todavía sufre los problemas descritos por @skalee bajo la respuesta de los
jonuts
1
Resumen, URI.parse(string_to_be_checked).kind_of?(URI::HTTP)hace bien el trabajo.
ben
Además, un error de escritura muy común en nuestra base de datos muestra que las personas tienden a colocar muchas barras diagonales:, lo http:///neopets.comque desafortunadamente también es válido. Verificar la presencia de un nombre de host corrige esto:uri = URI(str) ; %w[http https].include?(uri.scheme) && !uri.host.nil?
Shane
19

Prefiero la joya direccionable . Descubrí que maneja las URL de manera más 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
David J.
fuente
3
Acabo de alimentar Addressable :: URI.parse () con las cadenas más extrañas para ver qué rechaza. Aceptó cosas locas. Sin embargo, la primera cadena que no aceptó fue ":-)". Hmm.
mvw
1
¿Cómo consigue esto tantos votos a favor? Addressable::URI.parseno devuelve nil con una entrada no válida.
recolector de basura
11

Esta es una entrada bastante antigua, pero pensé en seguir adelante y contribuir:

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

Ahora puedes hacer algo como:

if "http://www.omg.wtf".is_valid_url?
    p "huzzah!"
end
Wilhelm Murdoch
fuente
2
Esto funciona mucho mejor que las soluciones anteriores. No tiene las advertencias mencionadas anteriormente y tampoco acepta uris como javascript: alert ('spam').
bchurchill
2
pero también coincide http:/, que puede que no sea lo que quieres.
Bo Jeanes
10

Para mí, uso esta expresión regular:

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

Opción:

  • i - no distingue entre mayúsculas y minúsculas
  • x - ignorar los espacios en blanco en expresiones regulares

Puede configurar este método para verificar la validación 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 usarlo:

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

Prueba con URL incorrectas:

  • http://ruby3arabi - el resultado no es válido
  • http://http://ruby3arabi.com - el resultado no es válido
  • http:// - el resultado no es válido

Prueba con las URL correctas:

  • http://ruby3arabi.com - el resultado es válido
  • http://www.ruby3arabi.com - el resultado es válido
  • https://www.ruby3arabi.com - el resultado es válido
  • https://www.ruby3arabi.com/article/1 - el resultado es válido
  • https://www.ruby3arabi.com/websites/58e212ff6d275e4bf9000000?locale=en - el resultado es válido
Komsun K.
fuente
Lo siguiente está marcado como válido:, "http://test.com\n<script src=\"nasty.js\">"y cualquier dominio que use uno de los 683 TLD de más de 5 caracteres, o que tenga dos o más guiones consecutivos, se marcará como no válido. Se permiten números de puerto fuera del rango 0-65535. Las direcciones FTP e IP obviamente no están permitidas, pero vale la pena señalarlas.
aidan
1
fácilmente la mejor solución más aplicable aquí para una rápida comprobación de URL. gracias
alguna dirección
4

Esto es un poco antiguo, pero así es como lo hago. Utilice el módulo URI de Ruby para analizar la URL. Si se puede analizar, entonces es una URL válida. (Pero eso no significa accesible).

URI admite muchos esquemas, además, puede agregar esquemas personalizados usted mismo:

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 la documentación para obtener más información sobre el módulo URI.

nyzm
fuente
Me encontré con esto tratando de solucionar un error de segmento. El uso URI.parsefue en realidad la causa de esto en Ruby 2.5.5: cambié a la respuesta de @jonuts a continuación si no le importa que algunos casos extraños no se concreten. Para mis propósitos no me importaba, así que eso era ideal.
el n00b
3

En general,

/^#{URI::regexp}$/

funcionará bien, pero si solo desea hacer coincidir httpo https, puede pasarlos como opciones al método:

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

Eso tiende a funcionar un poco mejor, si desea rechazar protocolos como ftp://.

usuario2275806
fuente
-2

También puede usar una expresión regular, tal vez algo como http://www.geekzilla.co.uk/View2D3B0109-C1B2-4B4E-BFFD-E8088CBC85FD.htm asumiendo que esta expresión regular es correcta (no la he verificado por completo) lo siguiente muestra la validez de la 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
}

Los resultados del ejemplo anterior:

http://hello.it is valid
http:||bra.ziz not valid
Jamie
fuente
5
¿Qué pasa con el esquema mailto? ¿O telnet, gopher, nntp, rsync, ssh o cualquiera de los otros esquemas? Las URL son un poco más complicadas que HTTP y FTP.
mu es demasiado corto
Escribir expresiones regulares para validar las URL es difícil. ¿Por qué molestarse?
Rimian
@Rimian, tienes que molestarte porque todo lo que URIpuedo hacer está roto. Vea los comentarios debajo de las tantas respuestas arriba votadas. No estoy seguro de si la respuesta de Janie es correcta, pero votando a favor, por lo que es de esperar que la gente lo considere más en serio. Por cierto, termino haciéndolo url.start_with?("http://") || url.start_with?("https://")porque solo necesito HTTP y los usuarios deben ser responsables de usar las URL adecuadas.
akostadinov