Web Scraping

Iniciado por arthusu, Enero 28, 2015, 03:19:21 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Definiendo Web scraping

El internet hoy en dia es un gran deposito de informacion, la porcion mas grande puede ser accedida a traves de la World Wide Web. Siendo el navegador web la herramienta estandar para acceder a este tipo de informacion.

Aunque el navegador es una gran herramienta, que se limita a los usuarios. Con un gran conjunto de informacion disponible en linea, seria util que las computadoras pudieran grabar toda esa informacion de forma automatica de alguna manera. Esto podria ser util para reutilizacion de datos, el analisis o creacion de No tienes permitido ver los links. Registrarse o Entrar a mi cuenta.   

Muchos de los sitios web de hoy en dia proporcionan el acceso a su contenido con la ayuda de una No tienes permitido ver los links. Registrarse o Entrar a mi cuenta, ya sea a traves de protocolos No tienes permitido ver los links. Registrarse o Entrar a mi cuentao No tienes permitido ver los links. Registrarse o Entrar a mi cuenta. Los usuarios pueden recuperar el contenido del sitio mediante la API y reutilizarlo como deseen (por supuesto, siempre respetando los terminos y condiciones del sitio). Por desgracia la mayoria de los sitios webs no proporcionan una API y la unica manera de conseguir los datos es a traves de web scraping, tambien conocido como spidering o screen scraping.

Web scraping es un metodo que implica la recuperacion automatica por programas, de los datos semi-estructurados de paginas webs. Comunmente hoy una pagina web esta construida en un lenguaje de marcas tales como HTML o XHTML. Como se muestra a continuacion:



Código: html5
<html>
<head>
<title>Hola HTML</title>
</head>
<body>
<p>Hola Mundo!</p>
</body>
</html>





Como se puede ver en el codigo anterior, la informacion aqui es la cadena "Hola Mundo!", mientras que la mayor parte del contenido de la pagina es el formato HTML , lo que hace que el navegador del usuario muestre "Hola Mundo!", si usted necesita conseguir esa parte del contenido "Hola Mundo!" mediante un programa informatico, lo que tendra que hacer es descargar la pagina y analizar el contenido de manera adecuada, lo que elimina todo el codigo HTML superfluo para llegar al contenido de texto puro. Por supuesto el ejemplo de marcado dado anteriormente es muy simple. En realidad las paginas webs son mucho mas complejas, que alberga diversos elementos HTML en una variedad de combinaciones.
Algunos de los HTML pueden estar mal formados, las etiquetas que faltan pueden estar anidadas incorrectamente. Los navegadores modernos suelen ignorar estos problemas y tratar de corregir las inconsistencias antes de mostrar una pagina. Sin embargo cuando usted esta escribiendo un web scraping usted tiene que tomar todos estos factores en consideracion. Esto puede hacer que el analisis de una pagina web sea una tarea dificil. Afortunadamente, hay varias bibliotecas y herramientas disponibles para que dicha mision sea mas facil para nosotros.

Razones para utilizar Web Scraping

Ahora que hemos definido lo que es web scraping y tenia una idea superficial sobre ello, tenemos que responder a la pregunta ¿por que? ¿Por que molestarme en realizar web scraping? llegarse a responder esta pregunta le resultara muy util para ampliar sus habilidades de web scraping en varios dominios.

Agregar y buscar un tipo especifico de datos.

Aunque diferentes sitios web ofrecen diferentes tipos de datos, la mayoria de ellos estan conectados semanticamente de alguna manera. Por ejemplo, si usted esta interesado en blogs relacionados con la ciencia, y tiene alrededor de 100 blog que se alimenta leyendo, seria dificil recorrer todos ellos regularmente, y encontrar articulos que usted realmente desea. Sin embargo usted puede programar un script de web scraping que alimente en una sola pagina todos los blogs y haciendo busquedas de interes en particular, transfiriendo asi la monotonia de datos filtrados en una sola maquina.

Obtener acceso automatizado a recursos web

Si es necesario comprobar regularmente el precio de algun producto en una tienda de comercio electronico (ecommerce) para ver si algun descuento esta disponible, se podria visitar el sitio regularmente para ver si se encuentra algo. Sin embargo, eso seria lento y tedioso. Una mejor manera seria escribir un pequeño programa de web scraping que visite regularmente el sitio y obtenga los precios y que envie un correo si encuentra algun descuento. Tambien podria comprobar las imagenes y descargarlas en su ordenador.

Combinar la informacion y presentarla en formato alternativo

Este metodo es uno de los usos mas comunes de web scraping, tambien conocido como "mashups", le permite reunir diferente tipo de informacion de varios sitios y combinarlos de una manera interesante que seria muy util para un usuario final.

Legalidad de web scraping

Despues de haber visto algunos usos de web scraping, tenemos que mirar un tema importante con relacion a la legalidad de web scraping. La legalidad de web scraping es una pregunta bastante compleja, debido a gran parte a las leyes de derecho de autor y de propiedad intelectual. Desafortunadamente, no hay una respuesta corta y facil que quede completamente clara, sobre todo por que estas leyes pueden variar en cada pais. Sin embargo hay varios puntos fundamentales para la examinacion de una pagina web potencial a realizar web scraping.

En primer lugar, los sitios web contienen a menudo documentos con terminos de servicio (TOS), terminos o condiciones de uso, o acuerdos de usuarios. Estos se localizan generalmente a lo largo del pie de pagina o la seccion de ayuda.

Estos tipos de documentos son mas comunes en sitios webs grandes o empresas reconocidas. Leer y enteder esto para saber los terminos sobre web scraping automatizado con scripts. Si usted esta realizando web scraping con el unico proposito de utilizar propiedad intelectual de otra persona en su propio sitio web, usted esta violando claramente los derechos de autor, esto es una obviedad. Si usted esta realizando web scraping a sitios de sus competidores y utilizando en su propio sitio lo cual es claramente ilegal. Ademas, incluso si usted no esta utilizando el web scraping para la reunion ilegal de datos, pero su script carga el servidor con varias solicitudes, perjudicando de este modo el servidor, esta usted violando los terminos del sitio. Asi que asegurese de que su script de web scraping no degrada de ninguna manera el servidor del sitio remoto.

Ahora con todos los problemas legales fuera del camino (pero aun a la vista), estamos listos para salir adelante con la parte de la codificacion.

     
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

HTTP: un resumen rapido

La primera tarea de una aplicacion de web scraping es la de recuperar documentos que contienen informacion para ser extraida. Para los sitios webs que constan de varias paginas o que requieren la mantencion de la sesion entre solicitudes o informacion de autenticacion, un cierto nivel de ingieneria inversa es a menudo necesaria para desarrollar una aplicacion web de web scraping correspondiente. Este tipo de exploracion requiere un buen conocimiento del protocolo de transferencia de hipertexto o HTTP el protocolo que da poder a la internet.


HTTP es un protocolo  de solicitud/respuesta concebido como un protocolo de comunicaciones entre clientes y servidor web.  Los clientes son programas o scripts que envian peticiones a los servidores, algunos ejemplo de clientes se incluyen los navegadores, tal como firefox, internet explorer o crawlers, como los utilizados por Yahoo!, Google y web scrapers.

Siempre que su navegador web obtiene un archivo (una pagina, una foto, etc) de un servidor web, lo hace utilizando HTTP. Esa solicitud que su ordenador envia al servidor web contiene todo tipo de informacion interesante. HTTP define metodos (aveces denominados verbs) para indicar la accion deseada a realizar por el servidor web en el recurso solicitado. Lo que este recurso representa, ya sea datos o datos preexistentes que son generados dinamicamente, depende de la aplicacion del servidor. A menudo, el recurso corresponde a un archivo o salida de un ejecutable que reside en el servidor. Una peticion HTTP consta de un metodo de peticion, un URL de solicitud, los campos de cabecera, y un cuerpo.

HTTP 1.1 define los siguientes metodos de peticion, de los cuales solo dos metodos de peticion seran de interes para nosotros - GET y POST.

GET: Recupera el recurso identificado por la URL de la solicitud.
HEAD: Devuelve las cabeceras identificadas por la URL de la solicitud.
POST: Envia los datos de longitud ilimitada en el servidor web.
PUT: Almacena un recurso bajo el URL de la solicitud.
DELETE: Elimina el recurso identificado por la URL de la solicitud.
OPTIONS: Devuelve los metodos HTTP que soporta el servidor.
TRACE: Devuelve los campos de cabecera que han sido enviados con la peticion TRACE.

Peticiones GET

Comencemos con una simple peticion HTTP GET, una pagina que devuelva el index ficticio del tal pagina:

el sitio es - No tienes permitido ver los links. Registrarse o Entrar a mi cuenta. La siguiente es la peticion que su navegador envia al servidor para recuperar el archivo index:

GET /index.html HTTP 1.1
Host: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta


Los componentes individuales de la solicitud anterior se detallan a continuacion:

GET es el metodo o la operacion que debe aplicarse sobre el recurso del servidor. Piense en ello como un verbo en una frase, una accion que desea realizar en algo.

index.html es el identificador uniforme de recursos o URI. Proporciona un punto unico de referencia para el recurso, el objeto o destino de la operacion.

HTTP 1.1 especifica la version del protocolo HTTP en uso por el cliente.

El metodo, URI, y la especificacion HTTP juntos conforman la linea de peticion.

En un unico encabezado Host y su valor asociado No tienes permitido ver los links. Registrarse o Entrar a mi cuenta siguen la linea de la peticion.

Mas pares de cabecera : Valor pueden seguir.

Basado en el recurso, el valor de la cabecera Host y el protocolo en uso No tienes permitido ver los links. Registrarse o Entrar a mi cuenta, Es la url completa del recurso solicitado resultante.

La mayoria de las solicitudes de las cabeceras de los sitios son sin embargo mas complejas que contienen muchos campos adicionales.

Puede utilizar herramientas como Firebug, Live HTTP Headers Plugin de firefox, para comprobar las cabeceras que son enviadas por el navegador y las respuestas correspondientes recibidas desde el servidor. Un encabezado que ha sido capturado con Live HTTP Headers para el sitio No tienes permitido ver los links. Registrarse o Entrar a mi cuenta se muestra a continuacion. Como se puede ver hay muchos campos adicionales junto con la accion GET.

GET /?p=us HTTP/1.1
Host: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: B=1mjov258ke&b=4&d=g2uJ6p15tCRXz. (truncated)
Cache-Control: max-age=0


Cadenas de consulta

Otra disposicion de URL es un mecanismo que se llama cadena de consulta (query string), que se utiliza para pasar parametros de la peticion a aplicaciones web. A continuacion se muestra una peticion GET que incluye una cadena de consulta se utiliza para solicitar una determinada pagina de No tienes permitido ver los links. Registrarse o Entrar a mi cuenta.

GET /index.php?page=3&limit=10
Host: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Hay algunos puntos notables que tenemos que tomar en cuenta en la URL:

Un signo de interrogacion indica el final de la ruta del recurso y de ahi comienza la cadena de consulta.

La cadena de consulta se compone de pares clave-valor, donde cada pareja esta separada por un signo (&).

Las claves y los valores estan separados por un signo igual. Las cadenas de consulta no son especificas de operaciones GET y pueden ser utilizadas en otras operaciones como POST, que miraremos a continuacion.


Peticiones POST

La adicion del metodo POST es quizas una de las mas grandes mejoras del HTTP especificadas hasta la fecha. Este metodo puede ser acreditado con la transicion de la web para una verdadera plataforma de desarrollo de aplicaciones web interactivas.

Cuando se utiliza un navegador web como cliente, esto mas a menudo se utiliza en formularios HTML. Si un formulario HTML especifica un metodo POST, el navegador enviara los datos de los campos del formulario en una peticion POST en lugar de una peticion GET.

Una gran diferencia entre una solicitud GET y una solicitud POST es que el POST incluye un cuerpo siguiendo los encabezados de solicitud para contener los datos que se presentaran. POST pretende añadir o modificar los datos expuestos por la aplicacion, un resultado posible es que se crea un nuevo recurso o se cambia un recurso existente.

Sintaxis de URL

La URL proporciona un medio de localizar cualquier recurso de internet, pero estos recursos pueden accederse por diferentes esquemas (Por ejemplo: HTTP, FTP, SMTP), y la sintaxis de la URL varia de esquema a esquema. A pesar de las diferencias entre varios esquemas, las URLS se adhieren a una sintaxis general de URL, y existe una superposicion significativa en el estilo y la sintaxis entre distintos esquemas de URL.

Todos los mensajes HTTP, con la posible excepcion del contenido del mensjae, utilizan el conjunto de caracteres ISO-8859-1 (ISO-Latin). Una solicitud HTTP puede incluir un encabezado de peticion Accept-Encoding que identifica codificaciones de caracteres alternativos que no sean aceptables para el contenido de la respuesta HTTP. La mayoria de los esquemas HTTP basan su sintaxis en este formato general de nueve partes:

<scheme>://<user>:<pass>@<host>:<port>/<path>?<query>#<frag>

La mayoria de las URLs no contienen todos estos componentes. Las tres partes mas importantes de una URL son scheme, host y path (esquema, servidor y ruta). Las distintas partes se explican a continuacion.

 

Código: text
+------------+-----------------------------------------------------------------------+----------------------+
| Componente | Descripcion                                                           | Valor por defecto    |
+------------+-----------------------------------------------------------------------+----------------------+
| scheme     | Protocolo utilizado para a obtener un recurso                         | Ninguno              |
+------------+-----------------------------------------------------------------------+----------------------+
| user       | El nombre de usuario, algunos scheme requieren                        | anonymous            |
|            | acceso anonimo para acceder a un recurso                              |                      |
+------------+-----------------------------------------------------------------------+----------------------+
| pass       | La contraseña se puede incluir despues del nombre                     | <correo electronico> |
|            | de usuario, separados por dos puntos(:)                               |                      |
+------------+-----------------------------------------------------------------------+----------------------+
| host       | El nombre del servidor o direccion IP que aloja                       | Ninguno              |
|            | el recurso                                                            |                      |
+------------+-----------------------------------------------------------------------+----------------------+
| port       | El numero de puerto en el que el servidor que aloja                   | Especifico al scheme |
|            | el recurso esta escuchando. Muchos schemes tienen                     |                      |
|            | numeros de puerto por defecto (el numero de puerto                    |                      |
|            | predeterminado para HTTP es el 80)                                    |                      |
+------------+-----------------------------------------------------------------------+----------------------+
| path       | El nombre local para el recurso en el servidor, separado              | Ninguno              |
|            | por los componentes de la URL anteriores por una                      |                      |
|            | barra (/), la sintaxis del componente path  es servidor y             |                      |
|            | especifico al scheme.                                                 |                      |
+------------+-----------------------------------------------------------------------+----------------------+
| query      | Utilizado por algunos schemes para pasar parametros a                 | Ninguno              |
|            | las aplicaciones activas (como base de datos, bulletin boards,        |                      |
|            | motores de busqueda y otros portales de internet). No hay             |                      |
|            | un formato comun para el contenido del componente de query.           |                      |
|            | Esta separado por el resto de la URL por el caracter "?".             |                      |
+------------+-----------------------------------------------------------------------+----------------------+
| frag       | Un nombre para una pieza o parte del recurso. El campo frag           |                      |
|            | no se pasa al servidor cuando hace referencia al objeto; es utilizado | Ninguno              |
|            | internamente por el cliente. Esta separado del resto de la URL        |                      |
|            | por el caracter "#".                                                  |                      |
+------------+-----------------------------------------------------------------------+----------------------+



Gestion de estados HTTP y Cookies

La tarea principal de un servidor web es satisfacer cada solicitud HTTP recibida del cliente. Todo lo que el servidor web considera al generar la respuesta HTTP esta incluido en la peticion. Si dos clientes web completamente diferentes envian solicitudes HTTP identicas al mismo servidor web, el servidor web usara el mismo metodo para generar la respuesta, que puede incluir la ejecucion de algunas aplicaciones del lado del servidor.

Los sitios webs modernos sin embargo tienen que proporcionar un toque personal a los clientes. Ellos quieren saber mas acerca de los usuarios sobre los otros extremos de las conexiones y poder hacer un seguimiento de los usuarios mientras navegan. Las transacciones HTTP son sin estado. Cada peticion/respuesta ocurre en el aislamiento. Muchos sitios web quieren construir un estado gradual a medida que interactuan con el sitio (por ejemplo, llenar un carrito de compras en linea). Para ello, los sitios webs necesitan una manera de distinguir una transaccion HTTP de otra.

Las cookies son la mejor manera de identificar los diferentes usuarios y permitir sesiones persistentes. Las cookies se desarrollaron por primera vez por Netscape pero ahora son compatibles con todos los navegadores principales. Puede clasificar las cookies generalmente en dos tipos: las cookies de sesion y las cookies persistentes. Una cookie de sesion es una cookie temporal que mantiene un registro de configuracion y las preferencias de como un usuario navega en el sitio. Una cookie de sesion se elimina cuando el usuario cierra el navegador. Las cookies persistentes pueden vivir mas tiempo; se almacenan en el disco y sobreviven despues de salir del navegador e incluso reiniciar el equipo. Las cookies persistentes a menudo se utilizan para mantener un perfil de configuracion o un nombre de usuario para un sitio que un usuario visita periodicamente. La unica diferencia entre las cookies de sesion y las cookies persistentes es cuando caducan.

Ahora que hemos tenido un breve resumen de HTTP, podemos proceder a lo que vamos - web scraping.! En la proxima parte.
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Herramientas para web scraping

Al igual que con cualquier otra tarea, comenzando con un buen conjunto de herramientas para comprender y utilizarlas de manera eficiente. Web scraping no es diferente. Web scraping se puede hacer ya sea mediante programacion con lenguajes de scripting como PHP, Ruby, o con ayuda de herramientas como wget o curl, aunque mas tarde no proporcionan la flexibilidad de un lenguaje que son herramientas utiles que vendran a mano. Cada uno puede ser utilizado de manera independiente o con la combinacion de realizar ciertas tareas de web scraping. Nuestro principal objetivo es utilizar PHP para recuperar paginas web y raspar la pagina para contenidos que nos interesan.

Trabajar con PHP para raspar los datos de las paginas webs no es una tarea facil y requiere de un buen conocimiento de las expresiones regulares. Sin embargo, existen algunas excelentes librerias, que seran muy faciles para analizar los datos de una pagina web sin ningun conocimiento de expresiones regulares. Tener un conocimiento practico de ello es parte esencial de un programador y puede ayudar enormemente con su trabajo de raspado (scraping).

En este apartado vamos a trabajar exclusivamente con la maravillosa herramienta SimpleHTMLDOM. Esta es una libreria muy pequeña y versatil, y es facil de integrar dentro de su aplicacion. Es un analizador HTML DOM escrito en PHP5+ que permite manipular HTML de manera muy facil. Es compatible con HTML invalido y permite encontrar las etiquetas en una pagina HTML con selectores como jQuery. Se puede descargar desde el siguiente enlace: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

La mejor manera para obtener una sensacion de utilizar la libreria es conseguir un par de ejemplos de como se ejecuta. Una vez que estamos a traves de estos ejemplos, vamos a ver mas profundamente la libreria. Aunque hay muchas librerias PHP por ahi que pueden ayudarte en su web scraping, centrandose unicamente en una sola libreria le ayudara conseguir el dominio de la libreria, depurando y escribiendo mas rapido sus scrapers. Ademas una vez que se llega a conocer una libreria se puede pasar facilmente a otras librerias. Simple HTML DOM ha existido desde hace bastante tiempo y es estable y altamente utilizado. Antes de empezar con simplehtmldom, miraremos otra herramienta muy importante cURL.

PHP cURL

Si quieres ver cURL en profundidad te invito a que visites las siguientes partes:

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Mientras PHP el mismo es capaz de descargar archivos remotos y paginas webs, la mayoria de las aplicaciones de raspado web requiren una funcionalidad adicional para manejar temas avanzados como envio de formularios, autenticacion, redireccion y asi sucesivamente. Estas funciones son dificiles de facilitar con funciones incorporadas de solo PHP. Afortunadamente para nosotros cada instalacion de PHP incluye una libreria llamada PHP/CURL, que se encargar automaticamente de estas caracteristicas avanzadas. La mayoria de los ejemplos que veras hacen uso de cURL para descargar archivos y paginas web.

A diferencia de las funciones de red incorporadas en PHP, cURL soporta multiples protocolos de transferencia - FTP, FTPS, HTTP, HTTPS, Gopher, Telnet y LDAP. De estos protocolos el mas importante para nuestro proposito es probablemente HTTP y HTTPS. Permite a nuestros web scrapers la descarga de paginas webs encriptadas que utilizan el protocolo SSL (Secure Sockets Layer).

Como se indico anteriormente, cURL permite la transferencia de datos atraves de una amplia variedad de protocolos. Es ampliamente utilizado como una forma de enviar contenido a traves de sitios web, incluyendo cosas como API. cURL no se restringe en lo que puede hacer, a partir de una peticion basica HTTP, carga de archivos en FTP o interaccion con un sitio seguro HTTPS.

Antes de que podamos hacer algo con una solicitud cURL, tenemos que crear antes una instancia de un recurso cURL llamando a la funcion curl_init(). Esta funcion toma un parametro que es la direccion URL a la que desea enviar la solicitud y devuelve un recurso cURL.

Código: php
$ch = curl_init('http://ejemplo.com');


Tambien podemos inicializar cURL de una manera ligeramente diferente:

Código: php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://ejemplo.com');


Tomemos un ejemplo sencillo:

Código: php
<?php
$ch = curl_init('http://ejemplo.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$res = curl_exec($ch);
curl_close($ch);
?>


Los detalles de cada linea se indican a continuacion:

* curl_init se llama y se pasa 'No tienes permitido ver los links. Registrarse o Entrar a mi cuenta' como una url para la solicitud
* curl_setopt es llamada a establecer la configuracion de ajuste representado por CURLOPT_RETURNTRANSFER estableciendo el valor en verdad (true). Esta configuracion hará que curl_exec devuelva el cuerpo de la respuesta HTTP en una cadena saliendo directamente al navegador o consola, siendo este ultimo el comportamiento predeterminado.
* curl_exec es llamado para ejecutar la solicitud y devolver el cuerpo de la respuesta que se almacena en la variable $res
* curl_close es llamado explicitamente para cerrar el identificador de sesion curl. Es importante que compruebe el codigo de retorno HTTP despues de una operacion cURL.

Puede utilizar la funcion 'curl_getinfo' para obtener la respuesta HTTP.

Código: php
$estado_http = curl_getinfo($ch);


La impresion de la variable $estado_http sera la siguiente informacion:

Código: text
Array
(
[url] => http://www.ejemplo.com
[content_type] => text/html; charset=UTF-8
[http_code] => 200
[header_size] => 157
[request_size] => 58
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 1
[namelookup_time] => 0.109
[connect_time] => 0.406
[pretransfer_time] => 0.406
[size_upload] => 0
[size_download] => 0
[speed_download] => 0
[speed_upload] => 0
[download_content_length] => 20714
[upload_content_length] => -1
[starttransfer_time] => 1
[redirect_time] => 0
)


Apartir de la matriz anterior solo estamos interesados en el codigo de estado, el cual podemos comprobar con lo siguiente:

Código: php
$http_status = curl_getinfo($ch);
if($http_status['http_code'] != 200) {
echo "echo error.";
// Hacer algo aqui


Tambien puede obtener el codigo de estado HTTP directamente atraves de la opcion 'CURLINFO_HTTP_CODE':

Código: php
$estado_http = curl_getinfo($s, CURLINFO_HTTP_CODE);


Simple HTML DOM: Una vision rapida

Comencemos con un ejemplo del mundo real para tener una idea de la libreria. El siguiente es un programa sencillo que busca en google la palabra clave 'flor' e imprime todos los enlaces de la pagina:

Código: php
<?php
/* Incluimos la libreria simplehtmldom */
require_once('simplehtmldom/simple_html_dom.php');
/* Obtenemos el contenido de la pagina por el buscador de Google */
$html = file_get_html('http://www.google.com/search?q=flor');
/* Buscamos todos los enlaces '<a>' de la pagina */
$links = $html->find('a');
/* Recorremos todos los links y los imprimimos */
foreach($links as $elemento)
{
echo $elemento->href . '<br>';
}
?>

El codigo se explica por si mismo. Inicialmente incluimos la libreria 'simplehtmldom' en su programa. Asegurese de la que la ruta de la libreria esta correcta.

Código: php
require_once('simplehtmldom/simple_html_dom.php');


A continuacion, utilizamos la funcion de la libreria file_get_html() para obtener el contenido en raw de la URL dada. La variable $html ahora contendra la estructura DOM completa de la pagina web recuperada.

Código: php
$html = file_get_html('http://www.google.com/search?q=flor');


Tenga en cuenta que la funcion file_get_html() utiliza file_get_contents() internamente de PHP. Asi que si file_get_contents() esta desactivada por su proveedor por razones de seguridad tendra que habilitarlo en su archivo de configuracion php.ini o en su lugar usar curl para obtener el contenido de la pagina inicial.

Una vez que se ejecuta la linea anterior, la variable $html contiene el objeto simplehtmldom que contiene el contenido HTML de la url dada. Una vez que tenemos nuestra pagina el DOM, estamos dispuestos a buscar con el metodo 'find' de la libreria. En este ejemplo estamos buscando el elemento de enlace <a>.

Código: php
$links = $html->find('a');


Esto devolvera una matriz de elementos del objeto <a>, que luego iteramos para imprimir el atributo href, el ejemplo anterior utilizando curl es el siguiente:

Código: php
<?php
/* Incluimos la libreria simplehtmldom */
require_once('simplehtmldom/simple_html_dom.php');
/* Obtenemos el contenido de la pagina desde el buscador Google */
$ch = curl_init('http://www.google.com/search?q=flor');
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$html_data = curl_exec($ch);
curl_close($ch);
/* Creamos un objeto DOM desde la pagina anteriormente dada. */
$html = str_get_html($html_data);
/* Buscamos todos los enlaces de la pagina '<a>'. */
$links = $html->find('a');
/* Los recorremos e imprimimos */
foreach($links as $elemento) {
echo $elemento->href . '<br>';
}
?>


En el ejemplo anterior hemos utilizado la siguiente linea para imprimir el atributo href:

Código: php
echo $elemento->href; 


Esto es el flujo de cualquier web scraping. Por supuesto, para proyectos complejos que habra demasiadas variaciones que habra que explorar, pero la logica basica y el flujo seguira siendo el mismo. Cada vez que buscas todos los elementos DOM, varios atributos se devuelven con la busqueda. En el ejemplo anterior hemos utilizado href, pero podria haber otros atributos que estan disponibles mediante el uso de los siguientes datos:

Código: php
print_r(array_keys($elemento->attr));


Esto deberia de devolver algo como lo siguiente:

Código: text
Array
(
[0] => href
[1] => rel
[2] => title
)

Asi que hay un atributo de title el cual puede ser impreso en lugar del atributo href.

Código: php
echo $elemento->title;


Cuando se especifica un atributo para imprimir, como el titulo en el ejemplo anterior, simplehtmldom busca primero en la lista de atributos para ver si hay algun atributo con ese nombre disponible, si lo encuentra entonces devuelve eso o lanza error. Muchas veces no son necesarios los atributos, pero el texto real dentro del elemento DOM, por ejemplo, el texto dentro de la etiqueta h3, para ello podemos utilizar el metodo 'plaintext' o 'innertext' el cual devuelve el contenido html en formato raw dentro del elemento especificado, mientras 'plaintext' devuelve el texto en plano sin formato html. Hay otro metodo 'outertext' que devuelve el texto exterior del nodo DOM junto a las etiquetas. El siguiente ejemplo muestra como los diferentes metodos son devueltos desde una cadena:

Código: php
$html = str_get_html("<div>Hola <b>Mundo!</b></div>");
$ret = $html->find("div", 0);
echo $ret->tag; // "div"
echo $ret->outertext; // "<div>Hola <b>Mundo!</b></div>"
echo $ret->innertext; // "Hola <b>Mundo!</b>"
echo $ret->plaintext; // "Hola Mundo!"


Supongamos que queremos listar todos los titulos de la busqueda de Google, en lugar de los enlaces podemos utilizar el codigo dado a continuacion, los titulos estan dentro de la etiqueta h3, por lo que tendran que buscar la misma, aviso lo facil es cambiar el elemento de busqueda el elemento de busqueda es el metodo 'find':

Código: php
/* Buscamos todos los titulos '<h3>' en los elementos de la pagina */
$titulos = $html->find('h3');
/* recorremos atraves de todos los titulos y los imprimimos */
foreach($titulos as $titulo) {
echo $titulo->plaintext . '<br>';
}

Hemos utilizado el metodo 'plaintext' aqui, en lugar del nombre de los atributos.

Guardando en caché paginas descargadas

Durante su inicio en proyectos de web scraping cuando se esta en aprendizaje, le ayudara enormemente si guarda una pagina en local a la cual le esta realizando web scraping. Esto ahorrara su ancho de banda y tambien de la pagina se que este raspando. Puede suceder que si se golpea el servidor remota de forma frecuente puede prohibir su IP. La mayoria de los datos de la pagina web no cambian con frecuencia, podemos ahorrarnos esto con las noticias o cosas relacionadas. Asi que una vez que haya guardado la pagina localmente y que se sienta comodo para probar el codigo de scraping en la copia local en lugar de ir de nuevo al servidor. Asi que si usted esta haciendo web scraping a la pagina principal de yahoo, se puede guardar un archivo local inicialmente y hacer uso de el mas adelante para rasparlo. Esto ayudara a mantener su trafico bajo y aumentar su velocidad es por que esta accediendo a una copia local, en lugar de la pagina web. A continuacion se muestra el codigo para guardar una copia local de la pagina:

Código: php
/* Obtenemos el contenido de la pagina principal de yahoo */
$homepage = file_get_contents('http://www.yahoo.com/');
/* Lo guardamos en un archivo local */
file_put_contents("cache/index.html", $homepage);


Si esta utilizando curl puede utilizar lo siguiente:

Código: php
/* obtenemos la pagina principal de yahoo */
$ch = curl_init('http://yahoo.com');
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$html_data = curl_exec($ch);
curl_close($ch);
file_put_contents("cache/index.html", $html_data);


Ahora que usted tiene la copia local de la pagina principal, se puede utilizar en el codigo de raspado:

Código: php
/* Incluimos la libreria simplehtmldom */
require_once('simplehtmldom/simple_html_dom.php');
/* Creamos un objeto DOM desde el archivo local */
$html = file_get_html('cache/index.html');
/* Buscamos todos los enlaces <a> de la pagina */
$links = $html->find('a');
/* Recorremos todos los enlaces y los imprimimos */
foreach($links as $element) {
echo $element->href . '<br>';
}


Depuracion cURL

Rara vez un codigo nuevo corre perfectamente la primera vez. cURL no es la excepcion. Muchas veces puede no funcionar correctamente o simplemente devolver un error. La funcion curl_getinfo permite ver peticion enviadas por cURL. Esto puede ser muy util para depurar las solicitudes, a continuacion se presenta un ejemplo de esta funcion en accion:

Código: php
$ch = curl_init('https://www.google.com/search?q=flor&start=0');
curl_setopt($ch, CURLOPT_TIMEOUT, 6);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
$html = curl_exec($ch);
$request = curl_getinfo($ch, CURLINFO_HEADER_OUT);
curl_close($ch);


La variable $request ahora mantiene el contenido de la peticion enviada por cURL. Puede ver si esto es correcto y modificar el codigo en consecuencia.


Nos vemos en la proxima parte.
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Explorando Simple HTML DOM

Simple HTML DOM en Detalle

Ahora que hemos visto un par de ejemplos, vamos a mirar en detalle esta maravillosa herramienta. Para todos los ejemplos a continuacion vamos a utilizar la plantilla siguiente: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

La razon por la cual no utilizamos sitios webs reales es que en esos sitios el html cambia con frecuencia, lo cual podria suceder que los ejemplos queden sin ser accedidos.

Construyendo un DOM inicial

Como vamos a trabajar con la plantilla proporcionada, primero debe instalarlo en su directorio local. Para los ejemplos asumiremos que tiene una URL local la cual se muestra a continuacion, aunque la suya puede variar, por lo que necesita ajustar los ejemplos en consecuencia.

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Trabajar con simplehtmldom requiere que primero creemos una estructura DOM con el HTML de la pagina que nos interesa. Todas las funciones proporcionadas por simplehtmldom trabajan con este objeto DOM y no con el texto plano de la pagina. Asi que el objeto DOM es lo que necesitamos para empezar. El objeto DOM puede ser creado de 3 maneras:

Código: php
/* Creamos un objeto DOM a partir de una cadena */
$html = str_get_html('<html><body>¡Hola!</body></html>');
/* Creamos un objeto DOM a partir de una URL */
$html = file_get_html('http://www.google.com/');
/* Creamos un objeto DOM a partir de un archivo HTML */
$html = file_get_html('index.html');


Todo lo anterior devuelve un objeto DOM que es almacenado en la variable $html. str_get_html() y file_get_html() son funciones de simplehtmldom. Como se dijo anteriormente, la funcion file_get_html() utiliza la funcion interna file_get_contents() de PHP. Asi que si esa funcion esta desactivada de su sistema por cualquier razon, tendra que utilizar cURL para obtener el contenido de la pagina inicial. Una vez obtenido el contenido usando cURL, puede utilizar la funcion str_get_html() para crear un objeto DOM. Si esta usted mas inclinado al estilo de programacion orientado a objetos puede hacer lo siguiente en lugar de crear el DOM.


Código: php
/* Creamos un objeto DOM */
$html = new simple_html_dom();
/* Leemos el HTML desde una cadena */
$html->load('<html><body>¡Hola!</body></html>');
/* Leemos el HTML desde una URL */
$html->load_file('http://www.google.com/');
// Leemos el HTML desde un archivo
$html->load_file('index.html');


Aunque no se menciona anteriormente, str_get_html y file_get_html pueden tomar parametros adicionales junto a la cadena HTML.

Código: php
str_get_html($str,
$lowercase=true, //Forza a minusculas para el nombre de etiquetas
$forceTagsClosed=true,
$target_charset = DEFAULT_TARGET_CHARSET,
$stripRN=true, // Strip NewLine characters
$defaultBRText=DEFAULT_BR_TEXT)


Todos los parametros tienen un valor por defecto, lo que va hacer o la mayoria de las aplicaciones. Sin embargo es necesario tener cuidado. El quinto parametro - $defaultBRText indica que todos los caracteres que tengan un salto de linea seran cortados como una cadena. Esto aveces puede causar problemas cuando se utiliza la cadena en otro lugar y se espera que la cadena tenga un salto de linea. Que por defecto esta establecido a "\r\n" (Windows). Si esta trabajando con saltos de linea de estilo *NIX, es necesario ponerlo en "\n".

El tercer parametro - $forceTagsClosed, tambien es importante. Forzar a las etiquetas se cierran significa que no confiamos en el HTML, esto puede ser util en casos de mal marcado HTML. Pero en validaciones de HTML es mejor establecer este valor como false (falso). La siguiente es la lista de parametros para la funcion file_get_html.


Código: php
file_get_html($url,
$use_include_path = false,
$context=null,
$offset = -1,
$maxLen=-1,
$lowercase = true,
$forceTagsClosed=true,
$target_charset = DEFAULT_TARGET_CHARSET,
$stripRN=true, $defaultBRText=DEFAULT_BR_TEXT)



Los ultimos parametros son los mismos que para la funcion str_get_html, mientras que los primeros 5 parametros son los mismos que para la funcion file_get_contents de PHP, como file_get_html llama a esta funcion internamente.

Agarrando el contenido del texto de una pagina

Código: php
/* Incluimos la libreria simplehtmldom */
require_once('simple_html_dom.php');
$template = 'http://localhost/scrape/template/index.html';
$html = file_get_html($template);
echo $html->plaintext;


Esto imprimira todo el contenido de la pagina sin las etiquetas html, es decir, el texto plano de la pagina. Una forma de acceso directo seria asi.

Código: php
/* Volcar el contenido (sin etiquetas) desde HTML*/
echo file_get_html($template)->plaintext;


Si usted necesita el contenido de texto sin formato de una pagina web externa en su lugar puede utilizar lo siguiente.
Código: php

/* Volcar el contenido (sin etiquetas) desde HTML*/
echo file_get_html('http://www.yahoo.com')->plaintext;


Esto puede ser util si usted necesita procesar algun texto en alguna pagina web completa, al igual que con que frecuencia se utilizan las palabras para crear una nube de etiquetas, encontrar palabras claves importantes, etc.

Encontrar elementos en una pagina

Todo el proceso de descarga de una pagina web es que podemos recuperar algo de contenido que nos interesa. El metodo find de simplehtmldom es el metodo principal para este fin. La funcion toma un selector como parametro y devuelve una matriz de elemento de objetos encontrados. Algunos ejemplos se muestran a continuacion.

Código: php
 /*busca la etiqueta 'a' en el html */
$ret = $html->find('a');

/* busca todas las etiquetas  <div>  que tengan el atributo id establecido */
$ret = $html->find('div[id]');


/* busca todas las etiquetas <div> que tengan el id=comment */
$ret = $html->find('div[id=comment]');

/* busca todos los elementos que tienen el id=comment */
$ret = $html->find('#comment');

/* busca todos los elementos que tienen la class=comment */
$ret = $html->find('.comment');

/* busca todos los elementos que tienen el atributo id establecido */
$ret = $html->find('*[id]');

/* busca a todos los elementos con anclas e imagenes */
$ret = $html->find('a, img');


Digamos por ejemplo que usted desea conseguir todos los titulos de los enlaces en la barra lateral de nuestra plantilla de ejemplo.




Usando firebug nos damos cuenta que los enlaces estan dentro de las etiquetas a u y la clase sidemenu. Asi que podemos pedir que el metodo find busque todos los enlaces que se encuentran debajo de la etiqueta ul con un nombre de clase dado.

Código: php
$links = $html->find('ul[class=sidemenu] a');
/* Recorremos todos los links y los imprimimos */
foreach($links as $link)
{
echo $link->plaintext . '<br>';
}


Podemos comprobar aun mas que esten disponibles otros atributos para su uso.

Código: php
print_r(array_keys($link->attr));


Esto devolvera lo siguiente en nuestro caso.

Código: text
Array
(
[0] => title
[1] => href
)


Asi que podemos imprimir los titulos en lugar de los enlaces.

Código: php
$links = $html->find('ul[class=sidemenu] a');
/* Recorremos todos los enlaces e imprimimos el 'title' */
foreach($links as $link)
{
echo $link->title . '<br>';
}


Simplehtmldom tambien tiene algunos metodos para trabajar con atributos. Si usted necesita ver todos los atributos que tiene un elemento puede utilizar el metodo getAllAttributes(). Asi que para el ejemplo anterior lo siguiente imprimira todos los atributos del elemento $link.

Código: php
print_r($link->getAllAttributes());


Imprimira:

Código: text
Array
(
[title] => side menu 5
[href] => #
)


Podemos comprobar si un elemento tiene un atributo en particular establecido y lo devuelve.

Código: php
if($link->hasAttribute('title'))
{
echo $link->getAttribute('title');
}


Otro ejemplo - Supongamos que queremos encontrar los enlaces del pie de pagina en nuestra pagina del template. Podemos ver usando Firebug que estan bajo un div con el id de nombre footerleft.



Una vez que sabemos el nombre del id podemos buscarlo con find.

Código: php
$links = $html->find('div[id=footerleft] a');


Tambien podriamos haber utilizado la siguiente linea al llegar a los links, pero el ul es redundante en este caso.

Código: php
$links = $html->find('div[id=footerleft] ul a');


No hay una manera unica de llegar al elemento que te interesa. Este es un truco que usted va aprender con el tiempo una vez que comprenda la libreria y tener alguna experiencia con el raspado web en su cinturon. Los anteriores son ejemplos descendientes de selectores, algunos mas los mostramos a continuacion.

Código: php
/* Buscamos todos los <li> en <ul> */
$ret = $html->find('ul li');

/* Buscamos etiquetas <div> anidadas */
$ret = $html->find('div div div');

/* Buscamos todos los <td> en una <table> con class=salary */
$ret = $html->find('table.salary td');

/* Buscamos todos las etiquetas <td> con el atributo align=center
en una etiqueta <table>  */
$ret = $html->find('table td[align=center]');


Iterar sobre los elementos anidados

Digamos que usted desea iterar sobre todos los elementos div en nuestra pagina del template que tienen la clase ct e imprimir el contenido. El codigo sera de la siguiente manera.

Código: php
$findDivs = $html->find('div[class=ct]');
foreach($findDivs as $findDiv)
{
echo $findDiv->plaintext . '<br />';
}


Por ejemplo, si desea buscar todas las etiquetas <p> dentro de las etiquetas <div>, lo que podemos escribir es lo siguiente.

Código: php
foreach($findDivs as $findDiv)
{
foreach($findDiv->find('p') as $p)
{
echo $p->plaintext . '<br />';
}
}


Por supuesto, esto es equivalente a lo siguiente. Que el estilo a utilizar dependera de su problema en particular. Lo anterior es mas flexible si quieres buscar varias etiquetas dentro de la etiqueta div y desea procesar mas etiquetas.

Código: php
$findDivs = $html->find('div p');
foreach($findDivs as $findDiv)
{
echo $findDiv->plaintext . '<br />';
}


Scraping tablas HTML

Las tablas HTML son uno de los elementos mas importantes de una pagina y puede requerir mucho esfuerzo para raspar utilizando expresiones regulares. Pero simplehtmldom hace que sea mas facil para iterar sobre todas las filas y columnas de las tablas. Nuestro template de muestra tiene 3 filas que utiliza el ejemplo de abajo.

Código: html5
<table id="mytable">
<thead>
<tr>
<th width="150">Column 1</th>
<th width="150">Column 2</th>
<th width="150">Column 3</th>
<th width="150">Column 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>C-1 R-1 Content</td>
<td>C-2 R-1 Content</td>
<td>C-3 R-1 Content</td>
<td>C-4 R-1 Content</td>
</tr>
<tr>


La mejor manera de obtener todo el contenido de la tabla es buscar e iterar sobre cada <tr>. A continuacion se muestra el codigo completo.

Código: php
$table = $html->find('table[id=mytable]');
foreach ($table as $t) {
foreach ($t->find('tr') as $tr) {
foreach ($tr->find('td') as $td) {
echo $td->innertext . " | " ;
}
echo "<br>";
}
}


Esta salida luce como lo siguiente:
 

Código: text
Col-1 Row-1 Content | Col-2 Row-1 Content | Col-3 Row-1 Content | Col-4 Row-1 Content | 
Col-1 Row-2 Content | Col-2 Row-2 Content | Col-3 Row-2 Content | Col-4 Row-2 Content |
Col-1 Row-3 Content | Col-2 Row-3 Content | Col-3 Row-3 Content | Col-4 Row-3 Content |


Tenga en cuenta que en el ejemplo anterior, si solo hay una tabla con el id 'mytable' usted puede tambien escribir el codigo de la siguiente manera. Observe como estamos indexando la variable $table.

Código: php
$table = $html->find('table[id=mytable]');
foreach ($table[0]->find('tr') as $tr) {
foreach ($tr->find('td') as $td) {
echo $td->innertext . " | ";
}
echo "<br>";
}

Si habia dos tablas y queriams el contenido de la segunda tabla el indice de la tabla sera diferente.

Código: php
foreach ($table[1]->find('tr') as $tr)


A veces puede haber la necesidad de almacenar los contenidos de la tabla en una matriz de dos dimensiones para su posterior procesamiento en lugar de hacer echo a la pantalla. Tal vez usted quiere guardarlo en una base de datos o exportarlo en un archivo CSV. Cualquiera que sea el caso, podemos agregar facilmente algunas lineas adicionales para guardar el contenido de la tabla en una matriz.

Código: php
$table_array = array();
$row = 0;
$col = 0;
$table = $html->find('table[id=mytable]');
foreach ($table as $t) {
foreach ($t->find('tr') as $tr) {
foreach ($tr->find('td') as $td) {
$table_array[$row][$col] = $td->plaintext;
$col++;
}
$row++;
}
}
print_r($table_array); // Prueba del array


Fijate como hemos usado un bucle for para iterar sobre las columnas de la tabla. Tambien puede acceder a columnas o filas individuales utilizando un indice en lugar de utilizar un bucle for.

Código: php
$td = $tr->find('td') ;
echo $td[1]; //mostrando la segunda columna de la tabla


DOM Padres e Hijos

Una estructura de la pagina HTML siempre es jerarquica. Todos los elementos DOM tienen padres e hijos. Una pequeña parte de nuestra plantilla es la siguiente.

Código: html5
<!DOCTYPE html>
<html>
<head>
<title>Web Scraping Template</title>
</head>
<body>
<div id="container" class="clearfix">
<div id="menucont">
<ul>
<li><a title="" href="#" class="active">Home</a></li>
<li><a title="" href="#">About Us</a></li>
<li><a title="" href="#">Blog</a></li>
<li><a title="" href="#">Contact Us</a></li>
</ul>
</div>


Usted puede notar que la etiqueta div con el id "container" es hijo de la etiqueta body. Si utilizamos el siguiente codigo en nuestro template este devolvera el padre del div container como la etiqueta body. De esta manera se puede recorrer el arbol DOM usando el metodo parent.

Código: php
$findDivs = $html->find('div[id=container]');
foreach($findDivs as $findDiv)
{
echo $findDiv->parent()->tag; // Imprime el 'body'
}


Digamos que queremos encontrar todas las etiquetas p, pero solo aquellos que son hijos inmediatos de un elemento div con una clase llamada ct. Podriamos utilizar el metodo parent aqui. El fragmento de codigo HTML se da a continuacion,

Código: html5
<div class="ct">
<p>Div 1. pellentesque</p>
</div>


El codigo para analizar lo anterior se muestra a continuacion.

Código: php
$findDivs = $html->find('p');
foreach($findDivs as $findDiv)
{
if ($findDiv->parent()->class == 'ct' &&
$findDiv->parent()->tag == 'div')
{
echo $findDiv->plaintext;
}
}


Por supuesto que podriamos haber escrito de manera diferente especificando una consulta mas precisa con el metodo find.

Código: php
$findDivs = $html->find('div[class=ct] p');
foreach($findDivs as $findDiv) {
echo $findDiv->plaintext;
}


Entonces ¿por que necesitamos un metodo parent? Muchas veces la estructura HTML cambia con frecuencia y tenemos que ser mas flexibles en nuestros esfuerzos de analisis. El metodo parent permite la flexibilidad al escribir el codigo, por lo que cualquier estructura de la pagina los cambios se pueden acomodar facilmente. Otro metodo para completar el metodo parent es el metodo children(n). Esto devuelve el objeto secundario Nth si el indice esta establecido, de lo contrario devuelve una matriz de children. Asi por ejemplo, lo siguiente imprimira los nombres de las etiquetas de los hijos de la div con clase ct. Tenga en cuenta una vez mas que los ejemplos que estan aqui son tomados con el template proporcionado.


Código: php
$findDivs = $html->find('div[class=ct]');
foreach($findDivs as $findDiv) {
foreach($findDiv->children as $child) {
echo $child->tag;
}
}
// Imprime 'p'


Otros metodos para su uso junto con children se dan a continuacion.

Código: php
$e->first_child () //Devuelve el primer hijo del elemento, o null.
$e->last_child () //Devuelve el ultimo hijo del elemento, o null.
$e->next_sibling () //Devuelve el siguiente hermano del elemento o null.
$e->prev_sibling () //Devuelve el hermano anterior del elemento o null.



Metodos de encadenamiento

Los metodos de encadenamiento son una manera mas facil de perforar a profundidad la jerarquia de los elementos DOM. Lo siguiente por ejemplo, nos permitira acceder a los elementos children anidados de la etiqueta div a p.

Código: php
$findDivs = $html->find('div[id=container]');
foreach($findDivs as $findDiv) {
echo $findDiv->children(1)->children(0);
}


Esto imprimira lo siguiente.

CitarWeb Scraping Template

Descarga de imagenes

La descarga de imagenes es una de las tareas mas comunes de web scraping que un desarrollador se puede encontrar. Con la ayuda de simplehtmldom y curl podemos lograr esto facilmente. Tomemos nuestro template de muestra de nuevo. Esto tiene tres imagenes que deseamos descargar a nuestro directorio local. Usando firebug podemos ver que las imagenes estan en la etiqueta p dentro de un div con la clase images. El pantallazo de firebug se muestra a continuacion.



Desde firebug el diseño del DOM nosotros podemos conseguir las urls de la imagen con el siguiente codigo.

Código: php
$images = $html->find('div[class=images] img');
foreach($images as $image)
{
echo $image->src;
}


Tenga en cuenta que el src de la imagen tiene una ruta relativa, que ahora tenemos que convertir en absoluta para descargar las imagenes. Una vez que nosotros somos completamente capaces de obtener el src de las imagenes ahora nosotros podemos descagarlas usando la funcion de PHP file_get_contents().

Código: php
$images = $html->find('div[class=images] img');
/* Ajustamos esto para la ruta completa al template */
$url = 'http://localhost/scrape/template/';
foreach($images as $image)
{
/* Obtenemos el link de la imagen */
$image_src = $image->src;
/* El atributo src tambien contiene el nombre del directorio
'images/' , necesitamos deshacernos de el para obtener el nombre de la imagen en texto plano */
$file_name = str_replace('images/', '', $image_src);
/* Nosotros descargamos los datos de la imagen y lo guardamos como archivo */
$file = file_get_contents($url . $image_src);
$fp = fopen($file_name, 'w');
fwrite($fp,$file);
fclose($fp);
}


En el escenario anterior se realizaron busquedas de las imagenes y a la vez se descargaron las imagenes en nuestro directorio local. Sin embargo, muchas veces es mejor guardar simplemente las direcciones URL de imagen en un archivo y luego descargar las imagenes con otro script.

Código: php
$fp = fopen('image_urls.txt', 'w');
foreach($images as $image)
{
/* Obtenemos los links de la imagen */
$image_src = $image->src;
fwrite($fp,$url . $image_src . PHP_EOL);
}
fclose($fp);


En el ejemplo anterior hemos almacenado todas las urls de imagen en el archivo 'image_urls.txt'. Mas tarde podemos usar esto para descagar las imagenes con otro script, ya sea usando file_get_contents() o usando curl. Hemos visto un ejemplo usando file_get_contents(), a continuacion veremos un ejemplo usando curl.

Código: php
/* Leemos todo el archivo y lo ponemos dentro de un array */
$lines = file('image_urls.txt');
foreach ($lines as $line)
{
/* eliminamos cualquier caracter al final de la linea */
$url = trim($line);
/* obtenemos solo el nombre de la imagen desde la url */
$filename = basename($url);
$fp = fopen($filename, 'w');
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_TIMEOUT, 6);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_exec($ch);
fclose($fp);
}
curl_close($ch);


Normalmente cada vez que se ejecuta una sesion de curl, el contenido recuperado se muestra en consola o es almacenado en una variable. Sin embargo, existe una opcion de curl importante, CURLOPT_FILE que le permite especificar un recurso de archivo en el que el contenido recuperado se guarda en un archivo, como en nuestro ejemplo anterior. Guardar todas las urls de las imagenes en un solo archivo tiene otra ventaja importante. Puede utilizar una utilidad como WGET para recuperar las imagenes.

WGET

Wget es un programa de linea de comandos de gran alcance que recupera el contenido de los servidores web, y es parte del proyecto GNU. Su nombre se deriva de World Wide Web y get. Soporta la descarga a traves de HTTP, HTTPS y FTP. Entre otras cosas sus caracteristicas incluyen descargas recursivas, conversion de enlaces para ver paginas web HTML offline, soporte para servidores proxy, etc. Escrito en portable C, Wget puede ser facilmente instalado en cualquier sistema UNIX y ha sido portado a muchos entornos, incluyendo Microsoft Windows, MAC OS X. Si utiliza windows se puede bajar la version del siguiente link. No tienes permitido ver los links. Registrarse o Entrar a mi cuenta.

Una vez que haya instalado Wget, puede pedir las imagenes del archivo image_urls.txt. Wget ahora abrira el archivo image_urls.txt, devolvera y guardara las imagenes en un directorio especificado o en el directorio actual.

Citard:\wget>wget -i image_urls.txt

Wget es una herramienta potente y flexible que puede ayudarle en sus esfuerzos de web scraping. Contiene un monton de opciones que le ayudaran enormemente si usted lee la documentacion cuidadosamente y se familiariza con innumerables caracteristicas. El siguiente es el formato general de wget.

Citarwget [opcion]... [URL]...

Unos pocos ejemplos de wget se dan a continuacion.

Código: text
#Descarga la portada de ejemplo.com a un archivo
#Llamado index.html
wget http://www.ejemplo.com/

#Descarga todo el contenido de ejemplo.com
wget -r -l 0 http://www.ejemplo.com/

#Descarga la portada de ejemplo.com, junto
#Con las imagenes y hojas de estilos necesarios
#Para mostrar la pagina, y convertir las urls en interior
#Para referirse a los contenidos disponibles a nivel local
wget -p -k http://www.ejemplo.com/


Para mas informacion:
Citarwget --help
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Web scraping en paginas autenticadas

Sitios autenticados

Aunque la mayoria de los sitios webs no estan asegurados y puedan ser accedidos a traves de cURL, muchas de las mas importantes aseguran con diferentes maneras la autenticacion. Una de las maneras mas comunes para asegurar una pagina es utilizando autenticacion basica HTTP. Para acceder al contenido tenemos que autenticarnos por lo cual necesitamos utilizar la extension cURL de PHP. Pero antes de ello diremos unas pocas palabras acerca de la autenticacion basica HTTP.

Autenticacion basica HTTP

Autenticacion basica HTTP es una de las maneras mas faciles y mas rapidas para asegurar paginas web, ya que no requiere cookies, manejo de sesiones, o la creacion de paginas de inicio de sesion. En su lugar, la autenticacion basica HTTP utiliza encabezados HTTP estaticos, lo que significa que no hay handshakes (apretones de manos) necesarios entre clientes y servidores. Cada vez que se entre a la pagina el navegador solicitara una autenticacion basica HTTP. Los encabezados de la misma se muestran a continuacion.

CitarHTTP/1.1 401 Access Denied
WWW-Authenticate: Basic realm="Mi Servidor"
Content-Length: 0

Esta solicitud debera ser enviada mediante HTTP 401 Not Authorized el codigo de respuesta contiene un encabezado WWW-Authenticate HTTP. La mayoria de los navegadores mostrara un cuadro de dialogo de inicio de sesion cuando recibe esta respuesta, lo que permite al usuario introducir un nombre de usuario y contraseña. Un ejemplo se muestra a continuacion. Cuando el user agent como navegador quiere enviar las credenciales de autenticacion al servidor, puede utilizar el encabezado de autorizacion. La cabecera de autorizacion se construye como sigue:

* Nombre de usuario y la contrase se combinan en una cadena "usuario:contraseña".
* La cadena resultante literal se codifica usando base64.
* El metodo authorization y un espacio es decir "Basic" se pone a continuacion, antes de la cadena codificada.

Por ejemplo, si el navegador utiliza "Patrick" como nombre de usuario y "thementalist" como contraseña y quiere acceder a algunos archivos de forma segura en el servidor No tienes permitido ver los links. Registrarse o Entrar a mi cuenta, las cabeceras enviadas por el navegador seran como el siguiente.

CitarGET /archivosseguros/ HTTP/1.1
Host: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
Authorization: Basic UGF0cmljazp0aGVtZW50YWxpc3Q=

En PHP podemos codificar nombre de usuario y contraseña de cadena a base 64 utilizando el siguiente.

Código: php
echo base64_encode('Patrick:thementalist');


Autentificacion basica HTTP con cURL

Ahora que estamos familiarizados con la autentificacion basica vamos a ver como podemos enviar la informacion de autentificacion utilizando cURL. El siguiente ejemplo nos permite acceder a un sitio con autenticacion basica HTTP utilizando cURL. Esto utiliza la opcion CURL_USERPWD para pasar el nombre de usuario y la contraseña para el servidor remoto para la autenticacion. Tenga en cuenta que cURL maneja todos los problemas de codificacion en base64 por nosotros. Solo tenemos que especificar las opciones adecuadas para cURL y se encarga del resto por nosotros. Las principales opciones en este ejemplo son CURLOPT_HTTPAUTH, CURLAUTH_BASIC.
Código: php

<?php
$user = "admin";
$pass = "adminautenticacion";
$s = curl_init();
curl_setopt($s, CURLOPT_URL, 'http://ejemplo.com/index.php');
curl_setopt($s, CURLOPT_RETURNTRANSFER, true);
curl_setopt($s, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($s, CURLOPT_USERPWD, "$user:$pass");
curl_setopt($s, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
$salida= curl_exec($s);
curl_close($s);
echo $salida;
?>


Incluso el tipo favorito de autenticacion, la autenticacion basica sin embargo esta perdiendo terreno a otras tecnicas debido a su debil naturaleza. Por ejemplo, con la autenticacion basica, no hay manera de cerrar sesion sin cerrar su navegador. Tampoco hay manera de cambiar el aspecto de la forma de autenticacion debido a que el navegador lo crea y el usuario no tiene control sobre eso. Es importante destacar que la autenticacion basica no es muy segura, ya que el navegador envia las credenciales de acceso al servidor en texto sin cifrar.

Almacenamiento y envio de cookies

La mayoria de los servidores despues de la autenticacion devuelven algo de informacion de la cookie que se requiere para trabajar correctamente con el resto del sitio una vez estamos con exito mas alla de la autenticacion. Necesitamos almacenar estos datos de las cookies en algun lugar para que podamos utilizarlo mas tarde con otras paginas. cURL hace que este trabajo sea mas facil para nosotros con la opcion CURLOPT_COOKIEJAR. Esta opcion tiene un nombre de archivo en el que la informacion de las cookies del sitio se almacenan; la hemos llamado "cookie.txt" en nuestro ejemplo a continuacion. A continuacion, podemos utilizar los datos de las cookies tarde como se requiere para trabajar con otras paginas.

Código: php
<?php
$username = "admin";
$password = "adminautenticacion";
$s = curl_init();
curl_setopt($s, CURLOPT_URL, 'http://ejemplo.com/');
curl_setopt($s, CURLOPT_HEADER, 1);
curl_setopt($s, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($s, CURLOPT_USERPWD, "$username:$password");
curl_setopt($s, CURLOPT_COOKIEJAR, 'cookie.txt');
curl_setopt($s, CURLOPT_TIMEOUT, 30);
curl_setopt($s, CURLOPT_RETURNTRANSFER, TRUE);
$page_data = curl_exec($s);
echo $page_data;
?>


Mas tarde, cuando queramos acceder a una pagina que require las cookies creadas anteriormente durante la autenticacion, podemos utilizar la opcion de curl CURLOPT_COOKIEFILE para cargar los datos de las cookies, que luego se envian al servidor cuando se solicita una pagina. A continuacion se da un ejemplo de codigo.

Código: php
$s = curl_init();
curl_setopt($s, CURLOPT_URL, 'http://ejemplo.com/pagina2.php');
curl_setopt($s, CURLOPT_COOKIEFILE, 'cookie.txt');
curl_setopt($s, CURLOPT_TIMEOUT, 30);
curl_setopt($s, CURLOPT_RETURNTRANSFER, TRUE);
$page_data = curl_exec($s);


Ademas de la autenticacion HTTP basica, tambien existe la autenticacion estandar usuario/contraseña en un formulario, utilizado en la mayoria de los sitios. Los datos de autenticacion son normalmente enviados por metodo HTTP POST. En el siguiente ejemplo veremos como usar cURL para trabajar con este tipo de autenticaciones.

Autenticacion de sesion

A diferencia de la autenticacion basica, en las credenciales de acceso se envian cada vez que se solicita una pagina, la autenticacion de sesion valida a los usuarios una vez y crea un valor de sesion que representa la autenticacion. El formulario de inicio de sesion estandar usuario/contraseña utilizan autenticacion de sesion. Una vez que el usuario se autentica correctamente una sesion se crea con la ayuda de las cookies y los valores de sesion se pasan a cada peticion de la pagina posterior para indicar que el usuario es autenticado. Hay dos metodos basicos para el empleo de sesion de autenticacion con cookies y con cadenas de consulta. En el siguiente ejemplo vamos a ver como podemos usar cURL para iniciar sesion en un sitio de administracion de WordPress utilizando autenticacion de sesion.

Ingresando a un sitio de administracion WordPress con cURL

Actualmente WordPress es uno de los CMS mas instalados en el mundo, y hay millones de sitios que ejecutan esta plataforma versatil. El siguiente ejemplo muestra como se puede acceder a una seccion de administracion de WordPress remota utilizando cURL y agarrar cualquier contenido de interes. Por ejemplo si desea comprobar automaticamente los intervalos regulares del numero de comentarios publicados en su blog, puede utilizar el siguiente codigo para lograrlo. El siguiente ejemplo utiliza cURL para enviar los datos de acceso a su pagina de administracion de WordPress. Esto se logra mediante la opcion CURLOPT_POSTFIELDS junto con algunas otras opciones requeridas. Despues del inicio de sesion correcto WordPress devuelve un conjunto de cookies que se quedaran almacenadas en un archivo local utilizando CURLOPT_COOKIEJAR. Estos datos de cookies seran utilizados despues por nuestro codigo que solicito otras paginas de administracion.

Código: php
<?php
$blog_url = "http://www.wordpress-blog.com/";
$blog_name = "wordpress-blog.com";
$username = "admin";
$password = "your-admin-password";
$post_data = "log={$username}&pwd={$password}&wp-submit=Acceder&redirect_to=http%3A%2F%2F{$blog_name}%2Fwp-admin%2F&testcookie=1";
$s = curl_init();
curl_setopt($s, CURLOPT_URL, $blog_url . 'wp-login.php');
curl_setopt($s, CURLOPT_TIMEOUT, 30);
curl_setopt($s, CURLOPT_POST, true);
curl_setopt($s, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($s, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($s, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($s, CURLOPT_COOKIEJAR, "cookie.txt");
curl_setopt($s, CURLOPT_RETURNTRANSFER, TRUE);
$downloaded_page = curl_exec($s);
?>


Una vez correctamente en la cuenta para admin, entonces podemos conseguir cualquier pagina de administracion requerida y raspar el contenido. Esto, sin embargo, sera necesario que establezcan las opciones correctas con cURL. El siguiente es otro ejemplo que se permitira el acceso a la administracion de WordPress y permitira obtener el contenido de la pagina para el enlace 'Todas las entradas'.

Un cambio importante a señalar aqui es la opcion CURLOPT_COOKIEFILE. Esto le permite continuar con la sesion cURL iniciada despues de inicio de sesion. cURL utiliza datos de la cookie de este archivo al mismo tiempo que solicita la nueva pagina.

Código: php
/* Ahora que nosotros tenemos iniciada la sesion correctamente, podemos pedir la nueva pagina */
curl_setopt($s, CURLOPT_URL, $blog_url . 'wp-admin/edit.php');
curl_setopt($s, CURLOPT_COOKIEFILE, "cookie.txt");
$html = curl_exec($s);
/* $html ahora contiene los datos de la pagina 'edit.php' */
echo $html;


Una vez que tenemos el contenido de la pagina, podemos utilizar simplehtmldom para encontrar el elemento DOM relevante y devolver el numero total de comentarios. El recuento de los comentarios pendientes se almacena en el siguiente elemento span en el DOM. Tenga en cuenta que esto podria variar dependiendo de la version de WordPress, asi que asegurese de que esta comprobando el elemento de DOM correcto.

Código: html5
<span class="pending-count">6</span>


Ahora usamos el metodo "find" para obtener el recuento de comentario. Esto se logra mediante la siguiente linea. Devolvemos el primer nodo que se ajuste a los parametros de busqueda. Esto se especifica mediante el segundo parametro para 'find', '0' en nuestro ejemplo.

Código: php
$data = @$html->find('span[class=pending-count]', 0);


Tambien podemos especificar el parametro de busqueda como a continuacion.

Código: php
$data = $html->find('span.pending-count', 0);


Una vez encontrado el elemento relevante devolvemos los datos adecuados del objeto.

Código: php
echo @$data->plaintext;


En el ejemplo anterior usted puede preguntarse como hemos llegado hasta los datos para la variable $post_data. La mejor manera es abrir el panel Firebug y enviar el formulario de WordPress. Ahora va conseguir todo el contenido solicitado en el panel de Firebug en la pestaña de red. Las urls mostradas en el panel seran tipicas de la administracion de WordPress despues del envio de formulario de inicio de sesion que se mostrara a continuacion. Se puede ver facilmente que los datos del formulario de inicio de sesion se han enviado en el archivo 'wp-login.php' utilizando el metodo POST. Tambien puede ver los detalles de POST y otras cabeceras haciendo clic en el enlace 'wp-login.php'. Una muestra parcial de lo capturado usando Firebug se mostrara a continuacion. El contenido de POST se muestra despues del campo 'Content-Length'. Solo tienes que copiar eso y reemplazar el nombre de usuario/contraseña correcta.

CitarPOST /wp-login.php HTTP/1.1
Host: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
Cookie: __utma=101293715.944295703.1353556559; Content-Type: application/x-www\
-form-urlencoded
Content-Length: 102
log=admin&pwd=test&wp-submit=Acceder&redirect_to=http%3A%2F%ejemplo.com%2Fwp-ad\
min%2F&testcookie=1
HTTP/1.1 200 OK
Date: Fri, 08 Mar 2013 04:32:18 GMT
Server: Apache
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
x-frame-options: SAMEORIGIN
Set-Cookie: wordpress_test_cookie=WP+Cookie+check; path=/
Last-Modified: Fri, 08 Mar 2013 04:32:19 GMT
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Con estos dos tipos de autenticacion debe ser capaz de obtener acceso a la mayoria de los sitios web. La otra arquitectura de autenticacion que necesitamos explorar es utilizar ajax para validar. Esto lo veremos mas adelante
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Scraping con expresiones regulares

Expresiones regulares: una introduccion rapida

Expresiones regulares o regex son la No tienes permitido ver los links. Registrarse o Entrar a mi cuenta del procesamiento de texto y son una parte importante del web scraping. Una vez que se sienta comodo con simplehtmldom es cuando puede embarcarse al uso de las expresiones regulares en sus proyectos de scraping. Este es un tema complejo y se supone que tiene un conocimiento rudimentario de ello. No lo cubrimos en detalle aqui, pero puede ver mas acerca de ello aqui: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
Una rapida vision general de las expresiones regulares sin embargo se presenta en este capitulo.

Las expresiones regulares son una herramienta increiblemente poderosa disponible en la mayoria de los lenguajes de programacion de hoy. Piense en expresiones regulares como un sofisticado sistema que coincide con un patron de texto. Se especifica un patron y luego usa las funciones incorporadas de PHP para aplicar el patron en una cadena de texto para ver si coinciden. PHP es compatible con dos tipos de normas de expresiones regulares,POSIX Extended and Perl-Compatible (PCRE). PCRE es actualmente el tipo preferido para utilizar con
PHP, tiende a ser mas rapido que la opcion POSIX, y utiliza la misma sintaxis de expresiones regulares que PERL. Puede identificar facilmente estas funciones ya que comienzan con el prefijo preg. Ejemplos de funciones de expresiones regulares PCRE en PHP son preg_replace(), preg_split(), preg_match(), y preg_match_all(). Vamos a describir solo las expresiones regulares PCRE en esta parte, y por simplicidad, tambien vamos a limitar nuestra discusion a las funciones mas utilizadas dentro de PHP. Echemos un vistazo a la coincidencia de cadenas en PHP primero. La funcion de PHP a utilizar para expresiones regulares es preg_match(). Considere la cadena:


"No tienes permitido ver los links. Registrarse o Entrar a mi cuenta"

Vamos a utilizar la cadena anterior para hacer coincidir con patrones. Aqui esta el primer ejemplo:

Código: php
$string = "las ideas verdes incoloras duermen furiosamente";
echo preg_match('/verdes/', $string) ; // retorna 1


Aqui estamos buscando el patron 'verdes' en cualquier lugar dentro de la cadena proporcionada. Cada vez que usted este buscando una cierta cadena o patron dentro de una cadena dada, hay que delimitar primero el patron. En general, usted hace esto con el caracter de barra diagonal (/), pero se puede utilizar cualquier otro caracter no alfanumerico. Por lo tanto si usted esta buscando un patron que concuerde con 'verdes' podria configurarlo con /verdes/ o #verdes#, siempre y cuando usted utilice los mismos caracteres en ambos extremos del patron y que no esta entre la cadena patron que esta buscando. Por ejemplo, los siguientes son equivalentes:

Código: php
preg_match('/Hola/', 'Hola Mundo!'); // devuelve 1
preg_match('#Hola#', 'Hola Mundo!'); // devuelve 1


El formato general de la funcion preg_match() es la siguiente.

Código: php
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] ) 

 
Podriamos haber logrado lo anterior con alguna de las funciones de cadena mas basicas que PHP proporciona, pero la funcion preg_match() tambien viene con algunos cuantificadores en el patron que aumentan el poder de las expresiones regulares, unos pocos seleccionados se muestran a continuacion.

Cuantificadores de patron

^ prueba para el patron al inicio de la cadena
$ prueba para el patron al final de la cadena
. coincide con cualquier caracter (comodin)
\ caracter de escape, se utiliza en la busqueda de otros cuantificadores como cadenas literales
[] rango de caracteres validos: [0-6] significa "entre e incluyendo 0-6"
{} Cuantos caracteres estan permitidos dentro de la regla del patron previamente definido

Con estos cuantificadores, podemos ser mucho mas especificos en lo que estamos buscando y donde lo estamos buscando para ello. He aqui algunos ejemplos:


Código: php
echo preg_match('/^verdes/', $string) ; // devuelve 0
echo preg_match('/^las ideas/', $string) ; // devuelve 1
echo preg_match('/^duermen/', $string) ; // devuelve 0
echo preg_match('/furiosamente$/', $string) ; // devuelve 1
echo preg_match('/verd.s/', $string) ; // devuelve 1


Si $matches es proporcionado, entonces se llena con resultados de la busqueda. $matches[0] contendra el texto que coincidio con el patron completo, $matches[1] tendra el texto que coincidio con el primer sub patron con parentesis capturado, y asi sucesivamente. Vamos a trabajar a traves de un ejemplo completo a continuacion.

Obteniendo la codificacion de caracteres de una pagina web

Cada pagina web tiene una codificacion especial para seleccionar que rangos de caracteres se muestran por el navegador. Hay varias maneras de especificar que codificacion de caracteres seran usadas en el documento. En primer lugar, el servidor web puede incluir la codificacion de caracteres o 'charset' en el protocolo de transferencia de hipertexto (HTTP) en la cabecera Content-Type, que normalmente tiene el siguiente aspecto:

CitarContent-Type: text/html; charset=ISO-8859-1

Para HTML, es posible incluir esta informacion en el interior del elemento head, cerca de la parte superior del documento:

Código: html5
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">


En el siguiente ejemplo breve obtendra la codificacion de caracteres para una pagina web en particular. Vamos a usar las  funciones PHP regex para buscar el contenido requerido. Aqui estamos mirando para el atributo 'charset' que incluye la informacion de codificacion.

Código: php
<?php
/* Obtenemos el contenido del sitio */
$html = file_get_contents('http://www.microsoft.com/');
// Buscamos el charset en el atributo meta
preg_match('/charset\=.*?(\'|\"|\s)/i', $html, $matches);
//Reemplazamos lo que no necesitamos a vacio
$matches = preg_replace('/(charset|\=|\'|\"|\s)/', '', $matches[0]);
echo strtoupper($matches);
?>


La parte importante del codigo es la expresion regular para buscar el patron 'charset'.

Código: php
'/charset\=.*?(\'|\"|\s)/i' 


La funcion preg_match se utiliza para encontrar la primera cadena que coincide para el patron anterior. Una vez que se encuentra, la funcion preg_replace solo mantiene el codigo del charset real y devuelve eso. Usted puede haber notado el caracter 'i' en la expresion regular despues del delimitador de cierre. Esto indica que la coincidencia debe ser case insensitive (insensible a mayusculas y minusculas). La funcion preg_match() solo coincide con la primera ocurrencia (coincidencia) encontrada y luego se detiene. La funcion preg_match_all() coincide sin embargo en varias ocasiones hasta que la ultima ocurrencia termina, hasta que no haya mas ocurrencias que encontrar. preg_match_all() funciona exactamente igual que preg_match(); sin embargo, la matriz resultante es multidimensional. Cada entrada de esta matriz es un arreglo de ocurrencias, ya que habria sido devuelta por preg_match().

Agarrando imagenes de una pagina web

Tomemos otro ejemplo. Una de las tareas comunes en web scraping es agarrar las imagenes de una pagina web. Esto se puede lograr facilmente usando el poder de la expresion regular. El siguiente codigo le permite obtener una lista de todas las imagenes de una pagina web junto con sus atributos - como src, height, width, alt etc. Para este ejemplo vamos a separar la parte de la expresion regular desde el script principal como es un poco largo y seria mejor si lo hacemos como una funcion. Llamamos a esta funcion 'parseTag'. La funcion 'parseTag' tambien extrae los atributos de la etiqueta de la imagen. La funcion completa es la siguiente.

Código: php
function parseTag($tag, $content)
{
$regex = "/<{$tag}\s+([^>]+)>/i";
preg_match_all($regex, $content, $matches);
$raw = array();
//Nosotros tambien queremos los atributos de la etiqueta
foreach($matches[1] as $str)
{
$regex = '/([a-z]([a-z0-9]*)?)=("|\')(.*?)("|\')/is';
preg_match_all($regex, $str, $pairs);
if(count($pairs[1]) > 0) {
$raw[] = array_combine($pairs[1],$pairs[4]);
}
}
return $raw;
}


El trabajo pesado del codigo anterior es la funcion preg_match_all(), que toma todas las etiquetas epecificadas en el parametro de una pagina web. A continuacion se muestra el codigo completo para agarrar los enlaces de la imagen de nuestra template usando la funcion 'parseTag'. Hemos utilizado cURL abajo, pero tambien podriamos utilizar file_get_html(). Ambas versiones se muestran aqui.

Código: php
<?php
/* version cURL */
$url = 'http://localhost/scrape/template/index.html';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 6);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$html = curl_exec($ch);
/* Esto grabara todos los datos de la etiqueta <img> */
$links = parseTag('img', $html);
print_r($links);
?>


La version file_get_html() se muestra a continuacion. Como se puede ver, es mas concisa que la version cURL. 

Código: php
<?php
/* version file_get_html  */
$url = 'http://localhost/scrape/template/index.html';
$html = file_get_html($url);
/* Esto grabara todos los datos de la etiqueta <img> */
$links = parseTag('img', $html);
print_r($links);
?>


Esto devolvera la siguiente matriz. Tenga en cuenta los atributos adicionales devueltos incluyendo los nombres de las clases de las imagenes. Esto puede ser util cuando solo se necesita encontrar imagenes con un nombre de clase determinada o etiqueta alt.

Código: text
Array
(
[0] => Array
(
[src] => images/flower1.jpg
[class] => imageb
[alt] => flower 1
)
[1] => Array
(
[src] => images/flower2.jpg
[class] => imageb
[alt] => flower 2
)
[2] => Array
(
[src] => images/flower3.jpg
[class] => imageb
[alt] => flower 3
)
)


Tambien puede imprimir el atributo src de cada imagen dentro de un bucle for.

Código: php
foreach($links as $link) {
echo $link['src'] . '<br>';
}


Como se puede ver las expresiones regulares junto a simplehtmldom pueden ayudar a raspar cualquier contenido web que usted desee. Por supuesto, las expresiones regulares son mas complejas que el uso de una libreria como simplehtmldom, pero ofrecen mas flexibilidad y potencia si se utiliza correctamente. Pero el principal inconveniente es la curva de aprendizaje que exige. Si se diseña incorrectamente las expresiones regulares puede ser una fuente de errores sutiles. Por estas razones, prefiero usar simplehtmldom siempre que sea posible y solo recurrir a expresiones regulares en casos raros.
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Scraping contenido Ajax

Javascript y el nacimiento de Ajax

Hace algunos años la mayoria del contenido de una pagina web, si se generaba dinamicamente o estaticamente en el servidor y luego era mostrado al cliente. Sin embargo, con la introduccion de ajax el contenido ahora podria ser llamado dinamicamente despues de que una pagina web ya habia sido cargada. Esto proporciono un nuevo reto para el web scraping.
Con auge de javascript muchos sitios webs ahora utilizan ajax para recuperar el contenido fresco de una pagina web. Sin embargo, esto puede ser problematico para nuestros esfuerzos de web scraping, ya que el contenido recuperado por javascript no se puede analizar facilmente por PHP cuando se inserta de forma dinamica en el DOM por el navegador. Solo usando cURL para obtener los contenidos de la pagina devolvera los datos sin contenido ajax. Entonces, ¿Que hacemos para obtener el contenido dinamico generado por javascript?
Una solucion es utilizar una herramienta de navegador como No tienes permitido ver los links. Registrarse o Entrar a mi cuenta. Sin embargo, Selenium es una herramienta de prueba y no puede ser facilmente integrado con PHP para recuperar el contenido dinamico. La otra solucion es utilizar 'phantom.js',  un programa de escritorio que les permite descargar paginas web y manipular el DOM usando javascript, lo que le permite recuperar el contenido ajax. phantomjs en realidad es un navegador sin la ventana normal, algo que se llama como un navegador sin cabeza. phantomjs utiliza javascript como lenguaje de scripting para procesar paginas descargadas. Vamos a mirar un poco phantomjs en este apartado, sin embargo nuestro objetivo principal es usar PHP para realizar web scraping.
Cada solicitud de pagina ajax implica una llamada a una direccion URL del servidor que devuelve de nuevo algunos datos al navegador. Estos datos son utilizados por el navegador para actualizar el contenido relevante en la pagina. La parte importante para saber realizar web scraping en el contenido es encontrar la peticion URL. Entonces, ¿Como podemos llegar a la solicitud correcta de la URL de ajax?

Firebug al rescate

Aqui es donde el complemento firebug sera muy util. En el panel de 'red' de la ventana de firebug puedes ver todas las peticiones GET/POST realizadas por la pagina web. Una muestra la pantalla de diversas solicitudes de Javascript se muestra a continuacion.




Como puede ver hay muchas peticiones GET asi como POST. Es su tarea localizar el URL correcto que se utiliza para recuperar el contenido ajax que le interesa. Por ejemplo, una peticion GET puede tomar la forma de la siguiente URL, donde el parametro de pagina variara con cada solicitud. La url de abajo podra ser para recuperar una lista de libros para la categoria "ciencia". Puede haber un total de 600 libros, por 20 libros que se muestren por peticion, lo que eleva el numero de paginas a 30 paginas.

CitarNo tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Por supuesto, esta URL puede ser un poco mas compleja de lo que se muestra aqui. Para para ejemplos bien vamos con el de arriba. Una vez que conocemos la URL correcta, entonces podemos usarla en nuestra solicitud cURL para llegar al contenido real. En el ejemplo que figura a continuacion tenemos que conseguir todo el contenido de ajax para las paginas 0-20. Con cada iteracion del bucle 'for' modificamos el parametro pagina y enviamos una nueva solicitud. No hemos añadido ninguna gestion de errores, pero usted debe comprobar si la solicitud devolvio cualquier dato valido o esta vacio.

Código: php
<?php
$ch = curl_init();
for($i=0; $<=20; $i++)
{
$url = "http://algun-sitio.com/respuesta/obtener-lista-libros.php?pagina={$i}&limit=10";
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 6);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$page_data = curl_exec($ch);
/* Hacemos algo con los datos */
}
?>

Para el contenido ajax con muchas paginas, como en el ejemplo anterior, por lo general prefieren almacenar en el cache el contenido recuperado en un archivo local, que hace mas facil la parte de raspar como comprobar repetidamente el codigo diferente en las paginas webs almacenadas en el cache. En pocas palabras, es la forma de recuperar el contenido de las paginas con peticiones ajax.
Algunas solicitudes de javascript aunque pueden usar POST en lugar de GET. Sin embargo, esto no cambia nada. Usted puede obtener los detalles adicionales de la solicitud POST del Firebug como antes. En el codigo que se muestra a continuacion podemos ver que javascript ha publicado a la url 'login.php' con dos parametros username y password. A veces esta solicitud podria ser mas compleja, con docenas de parametros. Sin embargo, la logica sigue siendo la misma. La respuesta enviada por el servidor por la solicitud esta disponible en la pestaña de "Respuesta". La respuesta puede ser enviada en HTML, texto plano, JSON o formato XML. La tarea de decodificar la respuesta en algunos formatos adecuados cae en el web scraper. El siguiente ejemplo muestra como utilizar cURL para enviar esta URL junto a sus dos parametros.

Código: php
<?php
$url = "http://ejemplo/login.php?login=1";
$username = "arthusu";
$password = "tu-password";
$post_data = "username={$username}&password={$password};
$s = curl_init();
curl_setopt($s, CURLOPT_URL, $url);
curl_setopt($s, CURLOPT_TIMEOUT, 30);
curl_setopt($s, CURLOPT_POST, true);
curl_setopt($s, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($s, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($s, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($s, CURLOPT_COOKIEJAR, "cookie.txt");
curl_setopt($s, CURLOPT_RETURNTRANSFER, TRUE);
$downloaded_page = curl_exec($s);
?>

Por supuesto Firebug no es la unica herramienta para observar el trafico de red. Google Chrome y Safari tienen excelentes herramientas de desarrollo integradas en el navegador para que usted se sienta comodo. La decodificacion de llamadas ajax y extraer las direcciones URL correctas es una habilidad que va adquirir con el tiempo. Es un arte, y requiere un poco de paciencia y ensayo y error para hacer las cosas bien, pero vale la pena el esfuerzo.

PhantomJS

Como se ha descrito anteriormente en este capitulo, PhantomJS es un Webkit scriptable sin cabeza con API de javascript. Tiene soporte rapido y de origen por varios estandares web: manipulacion DOM, selectores CSS, JSON, Canvas y SVG. Puede utilizar PhantomJS para capturar mediante programacion contenidos web, incluyendo SVG y Canvas, crear capturas de pantallas del sitio web, acceder y manipular paginas web con la API DOM estandar, o utilizarlo con las liberias junto a jQuery. Debido a que PhantomJS puede cargar y manipular una pagina web, es perfecto para llevar a cabo diversas automatizaciones de la pagina incluyendo web scraping, nuestro objetivo primordial. Puede descargar PhantomJS desde el siguiente enlace: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta. PhantomJS se corre desde la linea de comandos, asi que asegurese que la aplicacion se encuentre disponible con el PATH. Para probar si la aplicacion esta funcionando correctamente copie las siguientes lineas en un archivo de texto en blanco y guardelo como 'Prueba.js'.

Citarconsole.log('Hola, Mundo!');
phantom.exit();

Ahora puede ejecutar estos comandos haciendo pasar el archivo PhantomJs.

Citarc:\>phantomjs prueba.js

Esto imprimira la cadena Hola, Mundo! en la ventana de la consola. A continuacion se muestra un ejemplo sencillo para descargar una pagina web e imprimir el contenido en la consola.

Código: javascript
var page = require('webpage').create();
page.open('http://phantomjs.org', function(status) {
if ( status === "success" ) {
console.log(page.content);
}
phantom.exit();
});


PhantomJS es relativamente un buen tema con un gran numero de funciones y metodos que no podemos cubrir en este apartado. Este sitio web tiene una documentacion decente que se puede utilizar para codear. Tenga en cuenta que PhantomJS supone que tiene muy buen conocimiento de javascript.
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

El uso de proxies

El uso de proxies para web scraping

Uno de los problemas comunes de scraping en un sitio con gran tamaño de datos es que su ip puede ser baneada o bloqueada por solicitar demasiadas paginas en poco tiempo, ya sea de forma automatica o por el administrador del servidor. Una manera de salir de esto es utilizar servidores proxy.
Un servidor proxy es un servidor que actua como intermediario para solicitudes de clientes en busca de recursos de otros servidores. El uso de servidores proxy permite a sus peticiones pasar por muchas direcciones ip diferentes y por lo tanto aparece ser que viene de diferentes lugares.
Existen muchos tipos de servidores proxy - tanto gratuitos como de paga. Proxies de paga son los mejores para trabajos de web scraping donde te estan pagando (trabajos serios), para servidores proxy gratis puede ver una lista aqui: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta


Proxies con cURL

Suponiendo que haya reducido su lista de servidores proxy, tenemos que cargarla en una matriz para que podamos utilizarlo con cURL.

Código: php
$proxies = array();
$proxies[] = '187.28.84.130';
$proxies[] = '190.0.58.58';
/* Algunas proxies usan puerto */
$proxies[] = '199.15.248.179:8089';
/* Algunas proxy requieren usuario y contraseña */
$proxies[] = 'user:[email protected]';


Si hay varios servidores proxy que figuran en la matriz, lo mejor es elegir al azar cada vez.

Código: php
if (isset($proxies)) {
/* Seleccionamos una proxy aleatoria */
$proxy = $proxies[array_rand($proxies)];
}

Finalmente utilizamos el servidor proxy en nuestra sesion cURL.

Código: php
$ch = curl_init('http://ejemplo.com');
if (isset($proxy)) {
curl_setopt($ch, CURLOPT_PROXY, $proxy);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$res = curl_exec($ch);
curl_close($ch);


A continuacion se muestra el codigo completo.

Código: php
<?php
$proxies = array();
$proxies[] = '187.28.84.130';
$proxies[] = '190.0.58.58';
/* Algunas proxy usan numero de puerto */
$proxies[] = '199.15.248.179:8089';
/* Algunas proxy requieren usuario y contraseña */
$proxies[] = 'user:[email protected]';
if (isset($proxies))
{
/* Seleccionamos una proxy aleatoria */
$proxy = $proxies[array_rand($proxies)];
}
$ch = curl_init('http://ejemplo.com');
if (isset($proxy))
{
curl_setopt($ch, CURLOPT_PROXY, $proxy);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$res = curl_exec($ch);
curl_close($ch);
?>

Si la conexion falla, 'curl_exec' devuelve '0', asi que asegurese de comprobar con otro proxy. Sin embargo, puede suceder que ninguno de los servidores proxy son actualmente accesibles. Esto suele suceder cuando se utiliza proxy gratis. Asi que si usted tiene un proyecto de web scraping serio, me gustaria sugerirle el uso de proxies de paga que es fiable y que tenga soporte al cliente.


Por lo general encontramos que para los pequeños proyectos de web scraping no se requiere proxy si acabamos de escalonar las peticiones al servidor remoto, es decir, añadir un poco de retraso aleatorio entre sucesivas peticiones. Entonces para los pequeños proyectos, es mejor comprobar primero si las solicitudes trabajan bien y solo entonces utilizar un proxy.
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Programar tus web scrapers

Correr tu web scraping en funcionamiento de intervalos regulares

Hasta ahora hemos ejecutado todas las tareas de web scraping manualmente. Sin embargo, el poder real de los raspadores webs viene cuando se ejecutan en algunos intervalos predefinidos. Esto permite automatizar todas las tareas de raspado sin ninguna intervencion humana. Esto puede ser util cuando se necesita raspar uan pagina web, cuyo contenido cambia de forma regular - tales como los precios de productos, noticias, imagenes, etc.
Por lo general prefiero programar en un servidor remoto para realizar tareas de raspado - por lo general mi blog, o cualquier otro dominio que tengo mas que mi pagina local. Esto asegura que la programacion de mi scraper no se detendra debido a la caida del servidor local o algun corte de energia accidental.
Ademas el funcionamiento de un scraper en su maquina local significa que usted tendra que tener corriendo su PC las 24 horas del dia los 7 dias de la semana.

Casi todos los proveedores de hosting ofrecen una instalacion CRON que se puede usar para programar una secuencia de comandos que se ejecute en intervalos regulares en el servidor. Establecer una tarea CRON usualmente implica que el usuario defina una configuracion la programacion en el archivo crobtab del servidor. Este es el archivo principal en donde los diversos detalles de las tareas programadas se almacenan. Cada linea de un archivo crontab representa una trabajo, y se compone de una expresion CRON, seguido de un comando a ejecutar.

Una expresion CRON es una cadena formada por cinco o seis campos separados por espacios en blanco que representa el tiempo a establecer. Normalmente como un calendario para ejecutar algun comando o script. La expresion crontab se muestra a continuacion con los detalles de cada campo.

MIN HOUR DOM MON DOW CMD

Código: text
Nombre corto      Nombre del campo         Valores permitidos
MIN               Minutes (Minutos)               0-59
HOUR              Hours (Horas)                   0-23
DOM               Day of Month (Dia del mes)      1-31
MON               Month (Mes)                     1-12 o JAN-DEC
DOW               Day of Week (Dia de la semana)  0-6 o SUN-SAT
CMD               Command      Comando o script ha ser ejecutado   
     
                                               
Un ejemplo de una entrada crontab para ejecutar un script PHP se muestra a continuacion.

00 11,16 * * * php /home/sameer/www/html/scrape-content.php

En este ejemplo se ejecuta el script PHP especificado a las 11:00 y a las 16:00 todos los dias. El valor separado por comas en un campo especifica que el comando debe ejecutarse en todos los tiempos mencionados. He enumerado a continuacion algunos ejemplos crontab comunes. Observe el uso especial del especificador "*"

Código: text
* * * * * <command> #Corre cada minuto
30 * * * * <command> #Corre cada 30 minutos pasando la hora
45 6 * * * <command> #Corre a las 6:45 am cada dia
45 18 * * * <command> #Corre a las 6:45 pm cada dia
00 1 * * 0 <command> #Corre a la 1:00 am cada Domingo
00 1 * * 7 <command> #Corre a la 1:00 am cada Domingo
00 1 * * Sun <command> #Corre a la 1:00 am cada Domingo
30 8 1 * * <command> #Corre a las 8:30 am el primer dia de cada mes
00 0-23/2 02 07 * <command> #Corre cada 2 horas el 2 de Julio


Francamente, el establecimiento de una tarea cron puede ser confusa para muchos usuarios. Afortunadamente la mayoria de los proveedores de hosting ofrecen una manera facil de configurar tareas cron utilizando valores predefinidos. Un ejemplo del planificador CRON en cPanel se muestra a continuacion. Esto muestra la seccion de configuracion por defecto para añadir una nueva tarea cron.




Puede configurar rapidamente con los valores que se muestran arriba en la imagen, o simplemente modificar cada minuto, hora, dia del mes, mes, dia de la semana.

Asegurarse de que cron job esta activo

Software, por su propia naturaleza, es propenso al fracaso, y su script no es diferente. Usted necesita asegurarse de que cron job se ejecuta regularmente y no se ha detenido por algunos errores imprevistos. Yo suelo añadir unas lineas debajo del codigo del script de web scraping, que me envia un correo electronico cada vez que el codigo se ejecuta con exito. Esto asegura que si cron job falla por alguna razon voy a llegar a saber debido a la ausencia de un correo electronico. Tambien otra ventaja de utilizar un correo electronico es que se puede enviar otros detalles de la ultima ejecucion de cron con el correo electronico - como resumen de los datos de raspado, la duracion de ejecucion de la ultima ejecucion de cron, etc.
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta