Web Scraping: caso práctico con PHP y cURL

Web Scraping

Después de tantos años gestionando webs de competiciones deportivas, mucha gente me ha preguntado cómo lo hago para mantener tanta información actualizada. La respuesta es muy sencilla: ¡no hago nada!. O mejor dicho, ahora no hago nada, porque ya lo dejé hecho antes. El web scraping de guerrilla es mi solución. 

Nota: Los proyectos en los que utilizaba esta estrategia no están ya disponibles. Aquí puedes obtener más información sobre ellos si lo deseas. No obstante, el caso práctico sigue siendo totalmente funcional. Si alguien tiene especial interés en obtener más información o parte del código fuente, podéis dejarme un comentario o escribirme a [email protected]

Quien no haya oído nunca hablar del web scraping, que eche un vistazo a esto. En resumen, es una técnica que permite extraer información automáticamente de otros sitios web para su posterior procesado y explotación. Aunque aún no es un campo muy conocido, Internet está plagado de robots peinando constantemente la web, siendo los más conocidos, los robots utilizados por los buscadores para indexar las webs.

¿Por qué he recurrido al web scraping en mis proyectos deportivos? Fundamentalmente, porque es la solución más natural para enfrentarse al procesamiento masivo de datos públicos y estructurados. Actualmente mis proyectos deportivos (futsalia.es, el extinto Mundo Salafutbolvalladolid.es y otros) aglutinan la considerable cantidad de 105.522 partidos, de 5.415 equipos repartidos en 555 competiciones de fútbol y fútbol sala. ¿Os imagináis hacer todo el trabajo de actualización de forma manual? Sería una locura.

Es cierto que no he conseguido (aún) automatizar el 100% del proceso, dado que no siempre es fácil encontrar fuentes óptimas de información estructurada para extraer y procesar automáticamente, pero desde luego el tiempo invertido en desarrollar el sistema ha merecido la pena con creces.

Varias veces me han preguntado si esto es legal. Desde luego, lo que hago no es ilegal, al fin y al cabo me remito a recuperar información pública en Internet y que no tiene propiedad intelectual. ¿De dónde saco la información? Fundamentalmente de cualquier web que publique esta información de forma suficientemente rápida y fiable, y además lo haga de forma estructurada y consistente en el tiempo. Me voy a permitir la licencia de no publicar cuáles 🙂

Extracción

A la hora de automatizar una tarea de este tipo, lo primero que tenemos que hacer es determinar qué información necesitamos, de dónde la vamos a sacar y cómo la vamos a procesar. En mi caso, dispongo en mi base de datos de todos los calendarios de cada competición, y hay dos procesos fundamentales que necesito completar cada semana: obtener los horarios de los partidos,  y posteriormente obtener los resultados de los partidos.

Sabiendo qué semana se juega un determinado partido, puedo automáticamente determinar cuándo necesita el sistema buscar su horario. Y posteriormente, una vez conocido el día y hora a la que se juega dicho partido, simplemente hay que calcular la hora de finalización, para posteriormente lanzarse a buscar el resultado.

Es de vital importancia establecer las reglas de correspondencia entre nuestros datos y los del sitio web del que queremos extraer la información. Es decir, para cada una de las webs de donde extraigo la información, necesito vincular el identificador (o cualquier otro elemento unívoco) con el que se identifica cada competición y equipo con el identificador propio en mi base de datos. Sin esta correspondencia, la información extraída me sería inútil.

El robot por lo tanto tiene que saber autónomamente cuándo hay competiciones pendientes de actualización, y posteriormente para cada una de ellas generar de forma automática la url de consulta, y lanzarse a extraer los datos. La generación de la url de consulta depende, como es lógico, de cada sitio web. Hay muchas formas de hacer la extracción de los datos, yo en concreto utilizo cURL desde PHP.

Como se puede ver, la extracción en sí no es complicada. Es cierto que dependiendo de cómo esté construido el sitio web en cuestión y sus defensas, hay que recurrir a sistemas más complejos, pero esto debería servir para la inmensa mayoría de los sitios web que existen hoy en día.

Procesamiento

Aunque pueda parecer lo contrario, la parte más sencilla es la extracción. Una vez obtenido todo el html de la página a la cual hemos realizado la petición (como haría cualquier navegador), hay que extraer la información que necesitamos, eliminando todo lo restante. Aquí cada maestrillo tiene su librillo, en mi caso lo que me gusta hacer es primeramente eliminar todo el CSS, Javascript y demás código que no me aporta información (utilizo preg_replace), para quedarme con un html limpio.

Una vez hecho esto, hay que separar el grano de la paja. Iterando sobre el código aplicando sucesivas veces la función explode de PHP, troceo la información hasta quedarme únicamente con la información que me interesa. Código del equipo local y visitante, fecha, hora y goles del partido. En este punto ya tengo toda la información que necesito, y solamente queda incluirla en la base de datos.

Como sé para cada competición y equipo los identificadores que estos tienen en la web de la cual he extraído la información, simplemente realizo una búsqueda secuencial. Uno a uno se comprueban los partidos de esa competición de los cuales necesito algo, con los datos obtenidos con la técnica de web scraping, y cuando encuentro información que no tengo y necesito, la añado.

Y ya está, se integra este proceso en una o varias tareas cron, y esperas que tus robots trabajen por ti.

Riesgos

Que el proceso sea automático, no implica que no haya que estar pendiente. Los dos mayores inconvenientes que me he encontrado son el cambio de la estructura del html de las webs de donde extraigo la información, y el bloqueo de la IP de mi servidor en algunos sitios web.

En el caso de que haya algún cambio en el marcado de la página, por muy pequeño que sea, con bastante seguridad tocará modificar o rehacer el procedimiento de procesamiento de los datos. En el momento que el robot no encuentre la estructura que espera, no será capaz de obtener la información final.

Si te bloquean la IP, el proceso es un poco más complicado. A continuación os cuento cómo lo he solucionado:

Proxies: mejorando nuestro web scraping con PHP y cURL

Tienes tu robot montado, empieza a recuperar información para tu base de datos de forma normal y, de repente, todo deja de funcionar. Si utilizas normalmente web scraping, es algo que tarde o temprano suele ocurrir, y normalmente la causa es un cambio en el html del sitio web del cual obtienes datos. Cuando esto ocurre, la solución es muy sencilla, basta con reescribir nuestro robot para que sea capaz de entender el nuevo código.

Pero llegará un día en el que te encontrarás con que el html no ha cambiado, y que la causa de que no sigas recolectando información es que tu dirección IP ha sido bloqueada. Normalmente esto ocurre por dos razones: el sitio o sitios web de donde obtienes la información mediante el scraping se ha dado cuenta de ello, y te bloquea para evitar que lo hagas; o bien simplemente porque han detectado un número demasiado elevado de peticiones desde una misma dirección IP, considerando que es un comportamiento sospechoso.

Reconozco que la primera vez que me encontré con este problema, supuso un importante desafío, ya que algunos de mis proyectos, como por ejemplo futbolvalladolid.es, no tiene sentido a día de hoy si no se autogestiona por completo de forma automática. La primera solución fue mover el robot a un nuevo servidor, con un dirección IP distinta, pero poco tiempo después se repitió el bloqueo.

Proxies

Afortunadamente, después de un rato buscando soluciones, llegué a este artículo de Jacob Ward, que planteaba una solución duradera utilizando proxies. Un proxy, simplificando, es simplemente un servidor que hace de intermediario entre las peticiones de un cliente al servidor de destino. Y esto, fundamentalmente, permite que se enmascare la dirección IP original y esta no pueda ser detectada por el servidor de destino, evitando su bloqueo.

Por Internet encontraréis decenas de sitios con listados públicos de proxies, como freeproxylists.net o samair.ru/proxy, pero es recomendable recurrir a algún servicio de pago como myprivateproxy.net para encontrar proxies de mayor calidad.

Obviamente, para aumentar la eficacia, necesitamos disponer de un volumen de proxies muy grande, de modo que podamos elegir aleatoriamente uno cada vez que hagamos una petición. Esto tiene dos ventajas fundamentales. La primera es que cuantos más proxies distintos utilices, más difícil será para los administradores de las webs detectar que hay direcciones IP realizando demasiadas peticiones. Por otra parte, cuantos más proxies tengas, menos posibilidades tienes de perder peticiones (por sobrecarga del proxy, por lentitud, o bien porque esté caído).

Si se compara con el artículo anterior, podéis ver que el uso de un proxy con cURL en PHP no aumenta apenas la complejidad del código:

Donde $proxy es la dirección del proxy (incluyendo el puerto), y $timeout el tiempo máximo de espera de respuesta, que en mi caso suelo fijar 5 segundos. Además, capturo los códigos de error de las peticiones, lo que me permite posteriormente analizar la calidad de cada uno de los proxies.

Actualmente trabajo con una lista de unos cien proxies, y reviso semanalmente su nivel de desempeño. A través del html que obtienen y de los códigos de error, puedo saber si sus peticiones han sido satisfactorias, han fallado (bien porque no han devuelto todos los datos antes del timeout, o bien porque el proxy no estaba disponible) o si la dirección IP del proxy ha sido bloqueada.

Una vez revisados estos logs, mantengo aquellos con un buen nivel de desempeño, aumento la frecuencia de uso de los proxies que tienen una tasa de funcionamiento muy alta, y elimino los que fallan demasiado a menudo o han sido bloqueados, sustituyéndolos por otros nuevos.

Así que ya sabes, si haces web scraping, pon un proxy en tu vida.

Uso de cookies

Este sitio web, como todos, utiliza cookies. Si continúas navegando por la web estás dando tu consentimiento para la aceptación de la política de cookies de este sitio web. ACEPTAR

Aviso de cookies