Diferencia entre revisiones de «LSWC scraping the web/taller scraping lwsc 2011»

De WikiEducator
Saltar a: navegación, buscar
 
(21 revisiones intermedias por el mismo usuario no mostrado)
Línea 1: Línea 1:
{{MiTitulo| Taller de Web Scraping LWSC 2011}}
+
{{/nav}}
 +
 
 +
== Planteamiento del taller de scraping ==
 +
En el taller vamos a usar [http://www.python.org Python] y [http://www.xml3k.org/Amara Amara] para hacer una práctica de web scraping. El nivel del taller es de iniciación.
  
 
{{Objetivo|
 
{{Objetivo|
TOCdepth=2|
 
 
* Introducción a las técnicas básicas de scraping usando [http://www.python.org Python] y [http://www.xml3k.org/Amara Amara]
 
* Introducción a las técnicas básicas de scraping usando [http://www.python.org Python] y [http://www.xml3k.org/Amara Amara]
 
* Herramientas de ayuda (firebug, curl, ...)
 
* Herramientas de ayuda (firebug, curl, ...)
 
}}
 
}}
  
 +
== Configuración ==
 +
Python y Amara son multiplataforma. En el taller vamos a indicar cómo instalar el entorno en linux.
 
{{Actividad|
 
{{Actividad|
TOCdepth=2|
+
* Instalación de python.
Title=Configuración del entorno|
+
$ sudo apt-get install python2.7 python2.7-dev python-pip  idle-python2.7
 
* Instalación de amara
 
* Instalación de amara
 
  $ sudo pip install http://files.akara.info/00-amara-latest.tar.bz2
 
  $ sudo pip install http://files.akara.info/00-amara-latest.tar.bz2
Línea 17: Línea 21:
 
}}
 
}}
  
 +
== 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.
  
 
{{Actividad|
 
{{Actividad|
TOCdepth=2|
+
Escribe este ejemplo para leer un documento. Modifica '''URL''' para que tenga una dirección real. ¿Qué vemos en la pantalla?
Title=Lectura básica de un documento|
+
 
<source lang="python" line="GESHI_NORMAL_LINE_NUMBERS" >
 
<source lang="python" line="GESHI_NORMAL_LINE_NUMBERS" >
 
from amara.bindery import html
 
from amara.bindery import html
URL = 'http://mypage.com'
+
URL = 'http://pagina.destino.com'
  
 
doc = html.parse(URL)
 
doc = html.parse(URL)
 +
print doc
 
</source>
 
</source>
[{Tip|'''doc''' tiene toda la información del documento}}
+
{{Tip|'''doc''' tiene toda la información del documento}}
 
}}
 
}}
 +
 +
== 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|
TOCdepth=3
+
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.
Title=Ejemplo base|
+
'''Navegando al estilo Python'''
Listado de cursos del INAEM: http://plan.aragon.es/MapaRec.nsf/fmrListado?OpenForm
+
* ¿Cuál es el título del documento?
 +
print doc.html.head.title
 +
* ¿Cuáles son las ligas recogidas en el documento?
 +
for liga doc.html.body.h2:
 +
    print liga
 +
* ¿Cuáles son los equipos de la Liga BBVA?
 +
for equipo in doc.html.body.ul.li:
 +
    print equipo
 
}}
 
}}
  
 +
== 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
 +
{{Tip| Para el taller usaremos una copia local, para no saturar el servidor ;-) }}
 +
 +
=== Listado de cursos===
 +
Observando el código fuente, vemos que los nombres de los cursos van en una celda con '''class="textoApl1"''':
 +
<source lang="html4strict">
 +
<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>
 +
...
 +
</source>
 +
La expresión xpath correspondiente es:
 +
u'//table/tbody//tr'
 +
 +
<source lang="python">
 +
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
 +
</source>
 +
 +
¿Quieres más datos?
 +
<source lang="python">
 +
print c.td.a.href  # imprime la url (relativa) del curso
 +
print c.td[4]  # quinta columna: ciudad del curso
 +
</source>
 +
¿Cursos que se celebran en Huesca?
 +
<source lang="python">
 +
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
 +
</source>
 +
 +
=== 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.
 +
 +
[[Archivo:Copiar xpath.png]]
 +
 +
== 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.
 +
<source lang="python">
 +
for c in lista_cursos[1:]: 
 +
    nombre_curso = U(c.td.a)
 +
    c.td.a.xml_children[0].xml_value = nombre_curso.capitalize()
 +
</source>
 +
 +
Prueba a volver a mostrar el documento que habías analizado:
 +
doc.xml_write()
 +
 +
{{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



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.


Icon objectives.jpg

Objetivo

  • Introducción a las técnicas básicas de scraping usando Python y Amara
  • Herramientas de ayuda (firebug, curl, ...)


Configuración

Python y Amara son multiplataforma. En el taller vamos a indicar cómo instalar el entorno en linux.


Icon activity.jpg

Actividad

  • Instalación de python.
$ sudo apt-get install python2.7 python2.7-dev python-pip  idle-python2.7 
  • Instalación de amara
$ sudo pip install http://files.akara.info/00-amara-latest.tar.bz2
Icon present.gif
Tip: Es necesario tener instalado un compilador de C y la cabeceras de python (en debian/ubuntu hay que instalar python-dev)


  • Instalar firebug
https://addons.mozilla.org/es-es/firefox/addon/firebug/





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.



Icon activity.jpg

Actividad

Escribe este ejemplo para leer un documento. Modifica URL para que tenga una dirección real. ¿Qué vemos en la pantalla?

  1. from amara.bindery import html
  2. URL = 'http://pagina.destino.com'
  3.  
  4. doc = html.parse(URL)
  5. print doc
Icon present.gif
Tip: doc tiene toda la información del documento






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



Icon casestudy.gif

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

  • ¿Cuál es el título del documento?
print doc.html.head.title
  • ¿Cuáles son las ligas recogidas en el documento?
for liga doc.html.body.h2:
    print liga
  • ¿Cuáles son los equipos de la Liga BBVA?
for equipo in doc.html.body.ul.li:
    print equipo




Ejemplo 2: el horario de la LSWC

Icon objectives.jpg

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:

<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

Icon present.gif
Tip: Para el taller usaremos una copia local, para no saturar el servidor ;-)


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.

Copiar xpath.png

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()
Icon present.gif
Tip: 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

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()

Más ejemplos y más complejos