Diferencia entre revisiones de «LSWC scraping the web/taller scraping lwsc 2011»
(→Lectura de una página html) |
|||
(6 revisiones intermedias por el mismo usuario no mostrado) | |||
Línea 27: | Línea 27: | ||
amara.bindery.html.parse | amara.bindery.html.parse | ||
− | Para usar documentos html usaremos el tercero, que además soporta documentos no válidos (tagsoup). El objeto que devuelve el parser es el que usamos para leer y | + | Para usar documentos html usaremos el tercero, que además soporta documentos no válidos (tagsoup). El objeto que devuelve el parser es el que usamos para leer y modificar un documento web. |
{{Actividad| | {{Actividad| | ||
Línea 41: | Línea 41: | ||
}} | }} | ||
− | Vamos a practicar con un ejemplo muy sencillo, el [[/ejemplo liga | ejemplo de la liga]] | + | == Ejemplo 1: equipos de la liga == |
+ | Vamos a practicar con un ejemplo muy sencillo, el [[/ejemplo liga | ejemplo de la liga]]. Puedes ver una [[/liga traza completa | traza más completa]] | ||
{{Ejemplo| | {{Ejemplo| | ||
Línea 56: | Línea 57: | ||
}} | }} | ||
− | == | + | == Ejemplo 2: el horario de la LSWC == |
+ | {{Objetivo| | ||
+ | Hacer búsquedas en el horario: | ||
+ | * ¿a qué hora hay charlas de ... Python?, | ||
+ | * ¿A qué hora hay una charla que trate de ...? | ||
+ | }} | ||
+ | |||
+ | Las charlas están marcadas de la siguiente forma: | ||
+ | <source lang="html4strict"> | ||
+ | <td rowspan="4" title="09:30 - 10:30" class="python"> | ||
+ | <a href="/programa/ponentes/taller-de-construccion-de-un-interface-swing-con-jython-y-windows-builder.html">Taller interface swing con jython y Windows Builder Pro</a> | ||
+ | </td> | ||
+ | |||
+ | <td rowspan="4" title="10:30 - 11:30" class="python">Taller: OpenERP y su integración con Django</td> | ||
+ | </source> | ||
+ | |||
+ | '''¡¡No siempre están equiquetadas igual!!''' | ||
+ | Aquí tienes el ejemplo de la traza: [[/horario lwsc | horario LSWC]] | ||
+ | |||
+ | == Ejemplo 3: análisis de los cursos de formación para el empleo== | ||
Vamos a usar ahora un ejemplo real: el listado de cursos de formación para el empleo del INAEM recogido en http://plan.aragon.es/MapaRec.nsf/fmrListado?OpenForm | Vamos a usar ahora un ejemplo real: el listado de cursos de formación para el empleo del INAEM recogido en http://plan.aragon.es/MapaRec.nsf/fmrListado?OpenForm | ||
{{Tip| Para el taller usaremos una copia local, para no saturar el servidor ;-) }} | {{Tip| Para el taller usaremos una copia local, para no saturar el servidor ;-) }} | ||
Línea 110: | Línea 130: | ||
[[Archivo:Copiar xpath.png]] | [[Archivo:Copiar xpath.png]] | ||
+ | |||
+ | == Ejemplo 4: transformación de un portal legado == | ||
=== Transformando el documento === | === Transformando el documento === | ||
Línea 125: | Línea 147: | ||
{{Tip| [https://github.com/zepheira/amara/blob/master/demo/inject_markup.py Aquí] tienes un ejemplo más complejo de inyección de marcado.}} | {{Tip| [https://github.com/zepheira/amara/blob/master/demo/inject_markup.py Aquí] tienes un ejemplo más complejo de inyección de marcado.}} | ||
+ | |||
+ | |||
+ | == Algunos trucos == | ||
+ | === Uso de cabeceras: User-Agent y gzip === | ||
+ | A veces el servidor exige unas condiciones: | ||
+ | Notes to library developers | ||
+ | Please use GZip compression when making API calls (Accept-Encoding: gzip). | ||
+ | Bots eat up a lot of bandwidth, which is not free. | ||
+ | You must set a descriptive User Agent header (User-Agent: User name/email) | ||
+ | Please include your username and wiki or email address. | ||
+ | http://www.mediawiki.org/wiki/API:Client_code#API_Access_Libraries | ||
+ | |||
+ | <source lang="python"> | ||
+ | import urllib2 | ||
+ | import gzip # para descompresión del doc. | ||
+ | import StringIO | ||
+ | |||
+ | from amara.bindery import html | ||
+ | |||
+ | opener = urllib2.build_opener() | ||
+ | opener.addheaders=[('User-agent', 'lmorillas'), ('Accept-Encoding', 'gzip')] | ||
+ | |||
+ | fin = opener.open('http://en.wikipedia.org/w/index.php?title=Albert_Einstein&action=render') | ||
+ | |||
+ | doc = fin.read() # Este documento está comprimido | ||
+ | |||
+ | data = StringIO.StringIO(doc) | ||
+ | |||
+ | data_unzip = gzip.GzipFile(fileobj = data) # documento descomprimido | ||
+ | |||
+ | doc = html.parse(data_unzipped) | ||
+ | |||
+ | doc.xml_write() | ||
+ | </source> | ||
+ | |||
+ | === Más ejemplos y más complejos === | ||
+ | * https://github.com/zepheira/amara/tree/master/demo |
Última revisión de 22:22 10 nov 2011
Contenido
Planteamiento del taller de scraping
En el taller vamos a usar Python y Amara para hacer una práctica de web scraping. El nivel del taller es de iniciación.
Configuración
Python y Amara son multiplataforma. En el taller vamos a indicar cómo instalar el entorno en linux.
$ sudo apt-get install python2.7 python2.7-dev python-pip idle-python2.7
$ sudo pip install http://files.akara.info/00-amara-latest.tar.bz2 Tip: Es necesario tener instalado un compilador de C y la cabeceras de python (en debian/ubuntu hay que instalar python-dev)
|
Lectura de una página html
El proceso básico consiste en utilizar un parser para analizar el documento. Amara tiene 3 parsers:
amara.parse amara.bindery.parse amara.bindery.html.parse
Para usar documentos html usaremos el tercero, que además soporta documentos no válidos (tagsoup). El objeto que devuelve el parser es el que usamos para leer y modificar un documento web.
Ejemplo 1: equipos de la liga
Vamos a practicar con un ejemplo muy sencillo, el ejemplo de la liga. Puedes ver una traza más completa
Ejemplo | |
Lo primero que hay que hacer es ver la estructura del documento. Desde el navegador, accede al código fuente de la página web. Navegando al estilo Python
print doc.html.head.title
for liga doc.html.body.h2: print liga
for equipo in doc.html.body.ul.li: print equipo
|
Ejemplo 2: el horario de la LSWC
Hacer búsquedas en el horario:
|
Las charlas están marcadas de la siguiente forma:
<td rowspan="4" title="09:30 - 10:30" class="python"> <a href="/programa/ponentes/taller-de-construccion-de-un-interface-swing-con-jython-y-windows-builder.html">Taller interface swing con jython y Windows Builder Pro</a> </td> <td rowspan="4" title="10:30 - 11:30" class="python">Taller: OpenERP y su integración con Django</td>
¡¡No siempre están equiquetadas igual!! Aquí tienes el ejemplo de la traza: horario LSWC
Ejemplo 3: análisis de los cursos de formación para el empleo
Vamos a usar ahora un ejemplo real: el listado de cursos de formación para el empleo del INAEM recogido en http://plan.aragon.es/MapaRec.nsf/fmrListado?OpenForm
Listado de cursos
Observando el código fuente, vemos que los nombres de los cursos van en una celda con class="textoApl1":
<tr> <td class="textoApl1" style="background-color: rgb(248, 247, 247);"> <a class="enlaceApl1 pequena1 negrita" alt="Pulse para acceder al contenido del curso" title="Pulse para acceder al contenido del curso" href="/MapaRec.nsf/(ID)/B9666A70A2B596C7C125783A0029923D?OpenDocument">ACTIVIDADES ADMINISTRAT...RELACIÓN CON EL CLIENTE</a> </td> ...
La expresión xpath correspondiente es:
u'//table/tbody//tr'
from amara.bindery.html import parse doc = parse('lista_cursos_inaem.html') CURSOS = u'//table/tbody//tr' lista_cursos = doc.xml_select(CURSOS) for c in lista_cursos[1:]: # la primera fila eran cabeceras!!! print c.td
¿Quieres más datos?
print c.td.a.href # imprime la url (relativa) del curso print c.td[4] # quinta columna: ciudad del curso
¿Cursos que se celebran en Huesca?
from amara.lib import U # U devuelve el texto de un elemento for c in lista_cursos[1:]: # primera fila cabeceras if U(c.td[4]) == u'Huesca': print c.td
Búsquedas genéricas
Si buscamos un nodo que tenga un texto:
s = "texto buscado" expr = u'.//text()[contains(., "%s")]'%s.decode('utf-8')
Si buscamos un contenido que puede estar como valor de un atributo:
expr = u'.//@*[contains(., "%s")]'%s.decode('utf-8')
Si no sabes XPATH, firebug te puede ayudar. Firebug copia la XPATH a cualquier elemento.
Ejemplo 4: transformación de un portal legado
Transformando el documento
Podemos transformar el contenido de una página al vuelo para volver a servirla mejorada (inyección semántica, limpieza de marcado, adaptación del contenido al destinatario ...)
Cambiamos el nombre del curso en la misma página: Ponemos en mayúscula sólo la primera letra.
for c in lista_cursos[1:]: nombre_curso = U(c.td.a) c.td.a.xml_children[0].xml_value = nombre_curso.capitalize()
Prueba a volver a mostrar el documento que habías analizado:
doc.xml_write()
Algunos trucos
Uso de cabeceras: User-Agent y gzip
A veces el servidor exige unas condiciones:
Notes to library developers Please use GZip compression when making API calls (Accept-Encoding: gzip). Bots eat up a lot of bandwidth, which is not free. You must set a descriptive User Agent header (User-Agent: User name/email) Please include your username and wiki or email address.
http://www.mediawiki.org/wiki/API:Client_code#API_Access_Libraries
import urllib2 import gzip # para descompresión del doc. import StringIO from amara.bindery import html opener = urllib2.build_opener() opener.addheaders=[('User-agent', 'lmorillas'), ('Accept-Encoding', 'gzip')] fin = opener.open('http://en.wikipedia.org/w/index.php?title=Albert_Einstein&action=render') doc = fin.read() # Este documento está comprimido data = StringIO.StringIO(doc) data_unzip = gzip.GzipFile(fileobj = data) # documento descomprimido doc = html.parse(data_unzipped) doc.xml_write()