Etiqueta: WebScrapping

  • Cómo recolectar correos electrónicos de una página web usando PHP

    Cómo recolectar correos electrónicos de una página web usando PHP

    Es una necesidad bastante usual la de recoger información disponible en Internet.

    Algunos sitios permiten hacerlo en forma amigable exponiendo algún tipo de API, que puede ser consumida conectándose a un webservice.

    En otros casos, lo mejor que puede hacerse es algo de WebScrapping.

    Claro que es una técnica muy poco fiable y bastante costosa en términos computacionales, pero… si no queda otra…

    En este caso, de lo que se trata es de extraer las direcciones de correo electrónico presentes en una página cualquiera, por ejemplo:

    Se trata de un proceso de dos pasos

    Cómo obtener los contenidos de una página usando PHP

    El primer paso es obtener el contenido de la página. En realidad, lo que nos interesa es el HTML de la página, las imágenes, hojas de estilo y demás no es necesario en este momento.

    Una forma muy sencilla de conseguirlo es usar la función file_get_contents:

    <?php
    
    $html = file_get_contents('http://pagina-objetivo.com');

    Cómo identificar correos electrónicos dentro de un texto

    El segundo problema a resolver es, una vez que obtuvimos todo ese texto HTML… ¿cómo podemos determinar qué direcciones de correo electrónico hay dentro?

    Un modo muy simple es utilizar una expresión regular.

    Afortunadamente, las direcciones de correo electrónico siguen un patrón bastante estructurado: todas tienen una @ en el medio y, al menos, un . a la derecha.

    Podríamos usar una expresión mucho más compleja, pero comencemos por esta.

    Nuestro aliado en esta ocasión será la función preg_match_all:

    <?php
    
    $html = file_get_contents('http://pagina-objetivo.com');
    preg_match_all('/[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i', $html, $matches);

    Con esto nos quedamos con todos los candidatos a correos electrónicos.

    Luego, para mayor seguridad podemos eliminar aquellos que no pasen la verificación propia de PHP:

    <?php
    
    $html = file_get_contents('http://pagina-objetivo.com');
    preg_match_all('/[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i', $html, $matches);
    
    $emails = array_unique(array_filter($matches, function(string $str) { return filter_var($str, FILTER_VALIDATE_EMAIL);}));

    Con esto obtendremos un arreglo que contiene todas las direcciones de correo presentes en una página.

  • Webscrapping con PHP

    Webscrapping con PHP

    Me encontré recientemente con este problema desarrollando un sistema para un cliente y creo (¡y espero!) que mi experiencia pueda ayudar a otros.

    El desafío era el siguiente: nuestro cliente es una empresa que se dedica a la administración de activos financieros. Como parte de su operatoria, requieren la consolidación de información que actualmente está dispersa en una serie de planillas Excel.

    Parte de esa información se refiere a movimientos de acciones y bonos. Una de las tareas que se realizaba manualmente era el cálculo de cuánto dinero se había movido al realizar una compra o venta de alguno de estos instrumentos (Simple: cantidad de títulos por precio del título al día de la transacción).

    La complejidad de este cálculo reside en cómo conseguir el precio que tenía el activo al día en que se realizó la transacción. Antes de la intervención de Leeway esta tarea estaba a cargo de una empleada de la compañía (Entre muchas otras, tenía que buscar en el sitio de Yahoo Finance u otro similar y completar ese dato).

    Desarrollamos una aplicación que fuera capaz de consultar esa información y realizar ese cálculo en forma automatizada.

    No es realmente complicado hacerlo cuando se cuenta con una API bien hecha y documentada (Incluso mejor si tenemos a mano un SDK para PHP).

    Lo complicado del tema fue sacar la información de bonos, para la cual no encontramos ninguna fuente pública que tuviera buena información (¿Conocés alguna?, te invito a que dejes un comentario 🙂 ), con lo cual… no quedó opción más que arremangarse y hacer algo de web scrapping (Todo sea por ahorrarle unas horas de rastreo todos los meses a un cliente).

    Así que ahí nos metimos, cURL y SimpleHTMLDom en mano, a remover la maleza y a ver qué encontrábamos.

    Y la verdad… lo que encontramos no fue nada bonito. La primera misión fue entender todo el camino que un usuario humano tenía que recorrer para llegar a la información que nosotros queríamos obtener.

    Una vez que tuvimos esa información, nuestro primer intento fue apuntarle a la última URL con un simple GET y escarbar el resultado… no señor, ¿tan fácil iba a ser el tema?

    Lo primero que notamos era que la URL final tenía poco en común con la inicial (y sin acceso a la base de datos que andaba por detrás… difícil hacer la conversión…). Bien, retrocedamos dos casilleros.

    Nuevamente, bajemos el contenido de la primera URL y busquemos el link que necesitamos (¡Gracias S.C. Chen y compañía por ponerle sintaxis tipo jQuery al SimpleHTMLDom!).

    ¡Primera prueba superada! Tenemos una nueva página que está un pasito más cerca. Veámosla un poco más de cerca… ¡ahá! ¡Se trata de un formulario que va por POST! Ningún problema, nuestro amigo FireBug nos mostrará el camino. Ah, pero esto es muy simple… una pequeña llamada Ajax y voilà.

    Otra vez… ¡no tan rápido! Faltan los parámetros invisibles que se generan del lado del servidor… jejeje (Léase con risa de programador malicioso). Ok, volvamos a revisar ese HTML. Bien, acá están esos input hidden, no hay problema, los agregamos y listo.

    ¿Cómo que 404? ¡Si estoy viendo la información! ¿qué te pasa cURL?

    ¿Cómo? ¿Que el valor de un campo no es un literal si no una expresión?… qué ganas de complicarle la vida al prójimo… bueno… usemos el eval de php. Esta parte sí te la puedo mostrar:

    foreach ( $dom->find('.ajax-token') as $token ) {
      if ( $token->attr['name'] == '__atcrv' ) {
        $atcrv = eval('return '.$token->attr['value'].';');
      }
    }<div class="open_grepper_editor" title="Edit &amp; Save To Grepper">

    Y ahora, ¿qué otra sorpresita hay por ahí? ¿La respuesta (si da 200) viene gzipeada? 0 problema: gzdecode se encarga y por fín, tenemos a mano la tablita con los precios del bono buscado para el día buscado. Muchas gracias, buenas noches.


    La historia fue para poner en contexto, pasando en limpio (para generalizar un poco también):

    1. Todo lo que un navegador hace, cURL puede hacerlo también (Iba a decir, todo lo que una persona hace, pero después me vino a la mente el reCaptcha).
    2. Este mecanismo no es ni de lejos ideal. Basta con un pequeño cambio en el maquetado del sitio para que todo se rompa (Pero bueno… si no disponemos de una buena API, no creo que haya otro mucho mejor)
    3. No hay que temerle a un poco de ingeniería inversa 🙂
    4. La solapa Net de FireBug da un montón de información súper útil.

    ¿Me olvidé de algo importante?