abr 2007
Expresiones regulares para interpretar URLs
Estoy desarrollando un pequeño script que sirva de araña para recoger pequeños conjuntos de recursos que sirvan para hacer algunos análisis. Entre los procesos que hay que realizar, es necesario desarrollar una sencilla araña que me permita gestionar las URLs según lo que esté escaneando.
Sé que existen varias herramientas de arañas, y bastantes de ellas libres, pero la cuestión es poder tratar la información según como venga para ya tener los datos procesados en el momento de finalizar el crawling.
A lo que iba: entre las fases de proceso de una URL, está una parte que toca un poco la moral: Absolutizar los enlaces. Esto significa normalizar la estructura del enlace para que quede siempre como:
protocolo://dominio/uri?parametros#ancla
Para conseguir esta tarea, he desarrollado una pequeña función basada en expresiones regulares y programada en PHP. No es infalible, pero para quien le pueda servir...
Como precondición, es necesario disponer de una URL referencial (es decir, la URL donde estaba el enlace). Esta URL referencial tiene una estructura de Array del siguiente tipo:
$mxReferer = array ("protocol" => "","domain"=>"","uri"=>"","params"=>"","anchor"=>"");
LLamaremos a la función enviando la URL, que puede ser relativa, pero siempre al referente. Es decir: recogeremos del referente la información que falta en la URL analizada.
Para el caso que presento en el código de muestra, se supone que estamos en el documento "http://www.sopadebits.com/content/view/un_primer_post" y nos encontramos con un enlace cuyo atributo href es "/content/view/cuando_etiquetas_un_recurso".
El código de la función es:
$mxReferer = array
(
"protocol" => "http",
"domain" => "www.sopadebits.com",
"uri" => "content/view/",
"params" => "",
"anchor" => ""
);
print_r(tokenizeUrl("/content/view/un_primer_post"));
function tokenizeUrl($szUrl = false,$mxReferer = false)
{
if($szUrl == false || $mxReferer == false)
return false;
$mxTokens = array();
preg_match
(
"/^(((http|https|ftp):)?(\/\/)?|\/)([\w\.\/\_-%=&@]+)*((?)[\w\.\/\_-%=&@]+)?((#)[\w\.\/\_-%=&@]+)?$/i",
$szUrl,
$mxUrl
);
if($mxUrl[5] == '')
$mxUrl[5] = $mxReferer['uri'];
$mxUri = explode("/",$mxUrl[5]);
if($mxUrl[1] == $mxUrl[4])
{
$mxTokens['protocol'] = $mxReferer['protocol'];
if($mxUrl[4]=='//')
{
// no protocol, yes domain
$mxTokens['domain'] = $mxUri[0];
array_shift($mxUri);
}
else
{
// no protocol, no domain
$mxTokens['domain'] = $mxReferer['domain'];
}
}
else
{
if($mxUrl[4] == '//')
{
// no protocol, yes domain
$mxTokens['protocol'] = $mxUrl[3];
$mxTokens['domain' ] = $mxUri[0];
array_shift($mxUri);
}
else
{
// no protocol, no domain
$mxTokens['protocol'] = $mxReferer['protocol'];
$mxTokens['domain' ] = $mxReferer['domain'];
}
}
if(isset($mxUrl[7]))
{
$mxTokens['params'] = substr($mxUrl[6],1,strlen($mxUrl[6])-1);
}
if(isset($mxUrl[9]))
{
$mxTokens['anchor'] = substr($mxUrl[8],1,strlen($mxUrl[8])-1);
}
$mxTokens['uri'] = implode("/",$mxUri);
return $mxTokens;
}
?>
El resultado debe(ría) ser:
array
(
"protocol" => "http",
"domain" => "www.sopadebits.com",
"uri" => "/content/view/cuando_etiquetas_un_recurso"
)
Los parámetros y el ancla son opcionales. La simplicidad apremia, ya que trato lo que va "después" del dominio como un todo, sin importarme si se trata de un archivo php, html ni nada por el estilo.
A lo que vamos: la expresión regular. A ver si así se ve mejor:
/^(((http|https|ftp):)?(\/\/)?|\/)([\w\.\/\_-%=&@]+)*((?)[\w\.\/\_-%=&@]+)?((#)[\w\.\/\_-%=&@]+)?$/i
No me estoy acordando de la familia de nadie, que conste...
Explicación de la expresión regular
Primera parte: la expresión se da al principio de la cadena
^ : Esto indica que la expresión se debe encontrar al principio de línea (es decir, que si hubiera espacios en blanco al principio, no se encontrarían coincidencias. Se podría realizar un trim pero eso ya lo dejo para quien le interese).
Segunda parte: el protocolo
(((http|https|ftp):)?(\/\/)?|\/)
Esta parte verifica si la URL tiene el protocolo indicado, y en caso afirmativo, verifica que se trata de http, https o ftp. Incluso el FTP podría sobrar, pero mira... viene de propina ;-)
Como vemos, se abren tres paréntesis. Esto indica que la cadena que concuerde se guardará en una matriz ($mxUrl para el caso del código anterior).
- El primer paréntesis guarda el protocolo (es decir, me va a guardar "http", "https" o "ftp") ó la barra inicial de la uri (si la hubiera). Mención aparte para el carácter "|" que indica opción (o protocolo o barra).
- El segundo paréntesis guarda el protocolo y los dos puntos (:)
- El tercer paréntesis guarda las dos barras (\/\/) que van después del protocolo y los dos puntos.
Esto es importante: las URLs se pueden indicar en la forma "//dominio/uri...". Es decir, no siempre debe indicarse el protocolo. Lo más conocido es que suponga el nombre de dominio y que por lo tanto empiece por "/uri...", pero he querido asegurar esta posibilidad.
URI, parámetros y ancla
Los tres paréntesis siguientes son prácticamente idénticos. El primero corresponde a la URI, el segundo a los parámetros indicados con el interrogante y el tercero es el ancla. Echando un vistazo, sólo se diferencian del primer carácter de la expresión, que es lo que indica cuándo empieza uno y acaba el otro. La expresión:
([\w\.\/\_-%=&@]+)*
Busca 0 o más coincidencias (el asterisco) para un carácter alfanumérico (w), punto, barra, guión bajo o medio, signo % (esto es para los caracteres codificados), signo igual, ampersand y arroba. Esto puede ser una cadena (el signo + después del corchete indica que se pueden encontrar 1 o más ocurrencias) y los caracteres aceptados pueden estar en cualquier orden (se indican dentro de un corchete).
Bueno, creo que con esto ya basta por hoy. Si falta algún carácter, quizá se lo haya tragado el editor de HTML.
Fuentes RSS
Etiquetas
Aleatoriedad apis-mashups Buscadores Clustering del.icio.us Desarrollo web estadísticas estándares Expresiones regulares Flash Fractales fuentes de información Gestión documental Google grafos Gráficos estadísticos Innovación Java lenguajes documentales Lingüística Linux Lógica borrosa modelización Muestreo estadístico Navegadores off-topic open source PHP PLN Productividad Profesionales Publicidad Recuperación información relevancia reseñas SEO spam Tagging Usabilidad utilidades veracidad visualizacion Web social yahoo pipes