De cero a héroe del Regex
Extraer y analizar cadenas tipo URL con una sola expresión regular
Tabla de Contenidos
- 🚀 Introducción
- 🔍 Extracción de URLs a partir de texto
- 🛳️ La expresión regular de más de 120 bytes
- 🧩 Desglose paso a paso
- 🛠️ Ejemplo de análisis
- ☑️ Próximos pasos
- 📝 Resumen
- 📚 Aprendizaje adicional
TL;DR: Salta directamente a la expresión regular de más de 120 bytes.
🚀 Introducción
Extraer URLs de texto sin procesar a veces se siente como jugar a un tedioso juego de matar topos. La puntuación, los delimitadores entre paréntesis y el formato ambiguo conspiran para complicar el proceso. Ya sea que estés desarrollando un scraper web, un analizador de datos o una aplicación de chat, garantizar la precisión en la extracción de URLs es un requisito fundamental.
En esta publicación, abordaremos el problema de frente con un enfoque flexible de dos pasos. Nuestro objetivo es capturar primero todas las cadenas con aspecto de URL potenciales y luego gestionar la validación en un proceso posterior.
💡 Nota: ¡Este patrón no sirve para validar URLs! Es intencionalmente permisivo con la puntuación y los errores ortográficos.
🔍 Objetivo: Extraer URLs del texto
Al extraer URLs de texto sin procesar, un enfoque de dos pasos es efectivo:
- Capturar todo lo que parezca URL: Extender una red amplia para atrapar todas las cadenas que podrían ser URLs. Aquí es donde destaca nuestra “expresión regular de más de 120 bytes”.
- Validar: Una vez capturados estos candidatos, utiliza comprobaciones secundarias (p. ej., resolución DNS, comparación con dominios conocidos) para descartar las entradas inválidas.
Visualizar el desafío
Los términos extract y parse suelen emplearse de forma indistinta, aunque denotan procesos distintos. Extraer URLs implica identificar y capturar cadenas candidatas a partir de un texto más extenso. Por el contrario, el parse consiste en descomponer esas URLs en sus componentes.
Cuando menciono parse o “partes de una URL”, me refiero a los siguientes componentes:
Haz clic para ver una captura de pantalla de la coincidencia de subcadenas en RegEx101.
Antes de profundizar demasiado en la expresión regular, utilicemos una herramienta visual para verificar qué tan bien captura coincidencias múltiples nuestro patrón:

La expresión regular de más de 120 bytes
A continuación se presenta una expresión regular compacta, diseñada para extraer y analizar URLs en un solo paso. Soporta diversos protocolos, dominios, rutas y secciones opcionales de consulta (query) y fragmento.
No te preocupes, lo desglosaremos paso a paso.
const urlRegex = /([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)/gi;// Compatibilidad: ES5+
// Mismo patrón, dividido en líneas para mayor legibilidad:([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)Comparte las expresiones regulares más salvajes que hayas encontrado (o creado) en los comentarios a continuación! 🚀
🧩 Desglosando paso a paso
Diseccionamos la expresión regular en sus componentes para entender cómo opera:
1. Protocolo (Grupo 1): ([-.a-z0-9]+:/{1,3})
- Propósito: Coincide con la sección del protocolo de la URL (p. ej.,
http://,ftp://,custom-scheme://). Explicación:
[-.a-z0-9]+: Coincide con una o más letras minúsculas, dígitos, guiones o puntos (frecuentes en esquemas de protocolo).:/{1,3}: Coincide con dos puntos seguidos de una a tres barras (:/,://o:///).
2. Dominio (Grupo 2): ([^-/.[](|)s?][^`/s]?]+)
- Propósito: Captura la parte del dominio o host de la URL.
Explicación:
[^-/.[](|)\s?]: Coincide con cualquier carácter salvo los caracteres especiales y espacios en blanco especificados.[^`/\s]?]+: Coincide con uno o más caracteres salvo comillas invertidas, barras, espacios en blanco o corchetes de cierre.
3. Ruta (Grupo 3): ([-_a-z0-9!@$%^&*()=+;/~\.]*)
- Propósito: Coincide con el componente de ruta de la URL.
Explicación:
[-_a-z0-9!@$%^&()=+;/~.]: Coincide con cero o más caracteres seguros para URL, comunes en las rutas.
4. Query (Grupo 4): [?]?([^#\s`?]*)
- Propósito: Coincide opcionalmente con una cadena de consulta, que comienza con cualquier carácter
?. Explicación:
[?]?: Coincide opcionalmente con un?. (Los corchetes no son estrictamente necesarios, pero resultan un poco más claros que el ultraconciso doble??. Además, establecen un paralelismo visual con el siguiente grupo de coincidencia (similar)[#]?.)([^#\s`?]*): Coincide con cero o más caracteres que no sean hash, espacio en blanco, acento invertido o signo de interrogación.
5. Fragmento (Grupo 5): [#]?([^#\s’”`.,!]*)
- Propósito: Coincide opcionalmente con el identificador de fragmento, que comienza con un
#. Explicación:
[#]?: Coincide opcionalmente con un#.([^#\s’”`.,!]*): Coincide con cero o más caracteres que no sean puntuación prohibida ni espacio en blanco.
🛠️ Ejemplo de análisis
Así es como puedes ejecutar este regex con un poco de JavaScript:
const text = `Check this out: https://example.com/path?query=123#sectionAnd also (ftp://files.server.org/index).Plus a weird one: custom-scheme://host/param;weird^stuff`;
const urlRegex = /([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)/gi;
const matches = [ ...text.matchAll(urlRegex),].map((match) => match[0]);console.log("Extracted URLs:", matches);
const parts = [ ...text.matchAll(urlRegex),].map((match) => match.slice(1));console.log("Extracted Parts:", parts);[ "https://example.com/path?query=123#section", "ftp://files.server.org/index", "custom-scheme://host/param;weird^stuff"][ [ "https://", // Protocol "example.com", // Domain "/path", // Path "query=123", // Query "section" // Fragment ], [ "ftp://", // Protocol "files.server.org", // Domain "/index", // Path "", // Query "" // Fragment ], [ "custom-scheme://", // Protocol "host", // Domain "/param;weird^stuff", // Path "", // Query "" // Fragment ]]☑️ Siguientes pasos
Dependiendo de tu caso de uso, podrías necesitar ajustar este regex o agregar más pasos de validación y postprocesamiento.
Diferentes proyectos, diferentes necesidades
Los proyectos presentan requisitos y preocupaciones de seguridad diversos:
- Web Scraping: Validar URLs para garantizar accesibilidad y confianza.
- Procesamiento de datos: Extraer URLs de contenido generado por usuarios manteniendo controles de seguridad.
- Análisis de datos: Filtrar duplicados o enlaces irrelevantes para investigación o marketing.
- Aplicaciones para el usuario: Generar hipervínculos automáticos en chats o foros.
Postprocesamiento y validación
Una vez recopiladas las URLs candidatas, aplicar verificaciones adicionales:
- Consulta DNS: Verificar resolución de dominios.
- Verificaciones de seguridad: Utilizar servicios para detectar sitios maliciosos o de phishing.
- Reglas personalizadas: Aplicar filtros específicos del proyecto (p. ej., TLDs permitidos, longitud máxima de URL).
📝 Resumen
Extraer datos de cadenas semiestructuradas suele ser la parte más satisfactoria del dominio de las expresiones regulares.
Aquí tienes un resumen de los puntos clave:
- Usa una herramienta visual para escribir, probar y comprender tus patrones de regex.
- Divide el desafío en partes y resuélvelas por separado. En cierto sentido, los grupos de captura actúan como ‘marcadores de ruta’ figurativos para nuestra regex.
- Utiliza expresiones de coincidencia ‘flexibles’, evita la conformidad estricta con la especificación durante la ingestión de datos.
- Aplicar pasos de validación tras la extracción inicial es imprescindible; considera siempre la seguridad de tu proyecto y sus requisitos específicos.
Al seguir estos pasos, podrás extraer de manera efectiva cualquier dato en cadena semiestructurada, sentando las bases para un posterior procesamiento y validación.
📚 Recursos para profundizar
- No olvides probar el demo en vivo en RegEx101.com!
- Pregunta original en StackOverflow y un enlace a mi respuesta aquí.
- Documentación de MDN sobre Expresiones Regulares
- Técnicas avanzadas de regex: Explora lookahead, lookbehind y otros patrones avanzados para lograr coincidencias más precisas.
- RFC 3986 - Sintaxis genérica de URI