Alters

Aventuras de DoHITB: parte II (b)

Buenas!

Seguimos con la parte técnica del proyecto en el que estuve metido...

En la anterior entrada definimos unas cuantas funciones:


  • getContent(String $url)
  • getMarkList(String $fgc, String $token)
  • createBigIndex(Array $list)

Y justo acabó la entrada con nuestro proyecto en un "checkpoint" en el que teníamos las rutas de todos los archivos comprimidos.

El siguiente paso es obtener dichos archivos, descomprimirlos y procesarlos de manera similar al fichero anterior, para obtener todas las categorías.

Para ello, partimos del fichero en el que guardamos los enlaces.

Haremos una función que lea el fichero, y retorne en un array los enlaces; además, trataremos el fichero de manera que toda línea que empiece por "#" se tome como comentario (es decir, no se procesa).

function getIndexList($path){
$ret = array();
$content = explode("\n", getContent($path));
for($i=0;$i<count($content);$i++){
$aux = explode("#", $content[$i]);
if(count($aux) == 1){
$aux = explode(" = ", $content[$i]);
if(count($aux) == 2)
$ret[] = trim($aux[1]);
}
}
return $ret;
}

Explicaré cómo funciona este código:

Primero obtenemos el contenido de $path, y lo partimos por los saltos de línea ("\n"). Seguidamente, iteramos el array obtenido, y creamos un arreglo auxilar ($aux), resultado de partir la línea actual por el carácter "#". Si dicho arreglo es una única pieza (es decir, no contiene "#"), se tratará la línea; en caso contrario lo tomaremos como comentario y seguiremos.

En el caso que tratemos la línea, la separamos por el "=", y guardamos en el array de resultados el segundo índice del array resultante.

Al final del archivo, retornamos el array con las direcciones.

Es decir, si tenemos esta línea:

[1] = paginaweb.es/buscar/Hola

Al partirla por "=", obtendremos:

array("[1]", " paginaweb.es/buscar/Hola")

Con la función trim(), eliminamos todos los espacios y saltos de línea al principio y fin del String que le pasamos (si os fijáis, hay un espacio en blanco después del =, que llega al array).


Ahora tenemos la lista de elementos (comprimidos) que debemos obtener.

Para obtenerlos, tenemos que hacer exactamente lo mismo que hicimos con el índice general, solo que añadiremos una cláusula a la función get_file_contents(), de manera que, arreglando nuestra función getContent(), podríamos hacer:

function getContent($url, $isCompressed = false){
        $extra = "";

        if($isCompressed)
                $extra = "compress.zlib://";

        return file_get_contents($extra.$url);
}

Hemos añadido un parámetro nuevo ($isCompressed), que por defecto sera false. De esta manera, para archivos no comprimidos, llamaremos a la función así:

getContent($url) ó getContent($url, false)

Ya que hemos establecido que por defecto $isCompressed será false (y por tanto, siempre que sea "false" podemos no indicarlo).

Para un archivo comprimido, enviaríamos:

getContent($url, true)

Y la función añadiría la cláusula "compress.zlib://", para indicar que es un fichero comprimido.

Ahora, solo haría falta obtener con getMarkList() la lista de "<loc>"... pero aquí me surgió un problema:

Resulta que estos archivos comprimidos no tenían saltos de línea, por lo que no tuve más remedio que modificar la función getMarkList(), para que, dependiendo de la ocasión, partiera por los saltos de línea ("\n") o por la apertura de marca ("<").

De esta manera, nuestra función getMarkList quedará así:

function getMarkList($fgc, $token, $pre = false){
$t = "\n";
$e = "<";
if($pre){
$t = "<";
$e = "";
}
$splitted = explode($t, $fgc);
$ret = array();
for($i=0;$i<count($splitted);$i++){
$aux = explode($e.$token.">", $splitted[$i]);
if(count($aux) == 2){
$aux = explode($e."/".$token.">", $aux[1]);
$ret[] = $aux[0];
}
}
return $ret;
}

De manera que añadimos un nuevo parámetro ($pre), para indicar si hay o no salto de línea.

En caso que $pre sea omitido o false (nuestro primer caso, que quedaría con la misma sintaxis) se procedería de la misma manera. En caso contrario, se cambiarían las variables $t y $e por "<" y "", respectivamente.

Luego, el sistema sería el mismo, solo que el contenido de "explode" sería generado dinámicamente usando las variables $t y $e.

Para nuestros archivos comprimidos, llamaríamos así a la función:

getMarkList($fgc, 'loc', true);

Pero nos estamos adelantando... volvamos hacia atrás:

De momento tenemos seguro el método de obtener los archivos que debemos descomprimir y analizar, devueltos como un array. Para procesar el array y almacenar las categorías tenemos que descomprimir cada archivo (lo hemos visto), trocearlo (también visto), analizarlo, e insertar las categorías en un archivo.

Vamos pues, a ver cómo analizar el archivo descomprimido, y en forma de array:

Para ello, lo que hice fue mostrar por pantalla el array que me llegaba, y vi lo siguiente:
  • Algunas posiciones del array venían vacías, por lo que tendría que "limpiar" el array antes de procesarlo
  • Las URL que me interesaban tenían esta forma: "texto/texto/texto/texto/texto"
  • Dentro de esta forma, las "URL buenas" (que llevaban al índice de una categoría", tenían la última parte vacía ("texto/texto/texto/texto/")
  • Dentro de estas "URL buenas", me interesaba el índice 3 (es decir, "texto/texto/texto/texto/texto")
Después de este análisis, podemos ver la necesidad de dos funciones: una que limpie, y otra que analice. Vamos primero con la de limpiar:

function clean($list){
$ret = array();
for($i=0;$i<count($list);$i++)
if($list[$i] != "")
$ret[] = $list[$i];
return $ret;
}

El funcionamiento es de lo más sencillo: recibimos un array, lo iteramos; si el índice revisado tiene texto, lo agregamos a un nuevo array, que devolveremos al final.

Ahora veremos cómo analizar la línea actual, siguiendo el patrón anteriormente descrito:

function getCat($item){
$pieces = explode("/", $item);
if(count($pieces) == 5)
if($pieces[4] == "")
return $pieces[3];
return "";
}

Partimos la línea por "/", y si tiene 5 piezas y la pieza de índice 4 está vacía, retornamos la pieza de índice 3.
En caso contrario, devolvemos un String vacío.

Ahora, solo nos falta una función que vaya escribiendo en un archivo las categorías. Para ello, haremos tres funciones:

La primera recibirá un parámetro (array), que será la lista de archivos que debe descomprimir y analizar.
Abrirá un archivo global de categorías, iterará el array que le llega como parámetro para analizarlo, y finalmente cerrará el archivo.

function setSitemapFromList($list){
$m=0;
$f = fopen("categorias.dat", "w");
for($i=0;$i<count($list);$i++)
$m=setSitemap($list[$i],$f,$m);
fclose($f);
}

Explicamos un poco la función: creamos un índice $m, y un acceso a archivo $f.
Luego, iteramos sobre $list, y por cada archivo a procesar (setSitemap()), recogeremos el nuevo resultado de $m (ya veremos para qué sirve). Finalmente cerramos $f.

Vamos con la segunda función; setSitemap():

function setSitemap($toFill, $f, $i){
return setCategoriesFromList(getMarkList(getContent($toFill, true), 'loc', true), $f, $i);

Como vemos, es un simple puente, que hace lo siguiente:
  • Obtiene el contenido del archivo ($toFill), indicando que es comprimido.
  • Obtiene, del resultado anterior, todos los "loc", indicando que parta el contenido por "<"
  • Llama a la tercera función, setCategoriesFromList(), usando el resultado anterior, el fichero $f y el entero $i.
Ahora veamos la tercera función, setCategoriesFromList():

function setCategoriesFromList($list, $f, $m){
$clean = clean($list);
for($i=0;$i<count($clean);$i++)
if(($gCat = getCat($clean[$i])) != "")
fwrite($f, "[".$i."] = ".$gCat."\n");
return ($m+$i);
}

Esta función recibe una lista, un fichero y un enteros.

Primero limpia la lista (clean()), luego la itera, y si la línea actual contiene una categoría (llamando a getCat() sabemos si hay categoría porque devolverá algo diferente a "") la escribimos en el fichero, con el formato de siempre:

[N] = Categoría

Finalmente devolvemos el índice actual, para preservar la cuenta en todo momento (que será el nuevo valor de $m en la primera función).

De esta manera, para obtener el fichero de categorías, debemos llamar a la siguiente función:

setSitemapFromList(getIndexList("BIG_INDEX.dat"));

Esta entrada y la anterior os pueden servir para tratar ficheros con estructuras simples.

En la siguiente entrada, veremos cómo conseguir el archivo de categorías, y cómo crearlo a partir de un programa hecho con vb.NET

El código útil abarcará los siguientes dos temas:
  • Envío de peticiones HTTP (con GET, con POST, y simples)
  • Envío de peticiones FTP
Una vez visto esto, tendremos un .exe capaz de cargar las categorías de paginaweb.es. Lo siguiente será generar la lista de categorías en modo visual, y dejar que el usuario seleccione las que quiera. A partir de esa lista generaremos un nuevo archivo, que será la lista de categorías en la que hemos de buscar las publicaciones a responder.

El cómo conseguir las publicaciones lo veremos junto a cómo enviar el correo electrónico (veremos primero cómo enviar el correo electrónico).

Cuando finalicemos esto, os contaré otra pequeña investigación que hice, y cómo ligarla a la anterior. Además, veremos código útil que nos serviría para ahondar en la brecha de paginaweb.es

Pero iremos con la calma.

Saludos, y 

¡Hasta la próxima!