Diferencia entre revisiones de «Curso Python DGA 2011/acceso a datos/slides»
De WikiEducator
(19 revisiones intermedias por el mismo usuario no mostrado) | |||
Línea 10: | Línea 10: | ||
<div class="slide"> | <div class="slide"> | ||
=== Introducción === | === Introducción === | ||
− | Una de las tareas más frecuentes que tenemos es el tratamiento de información. Muchas veces esa información está bien estructurada y almacenada en herramientas estándar (bases de datos relacionales), pero otras veces los datos están en hojas de cálculo, páginas web y formatos menos estructurados. | + | * Una de las tareas más frecuentes que tenemos es el tratamiento de información. |
+ | * Muchas veces esa información está bien estructurada y almacenada en herramientas estándar (bases de datos relacionales), | ||
+ | * pero otras veces los datos están en hojas de cálculo, páginas web y formatos menos estructurados. | ||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
Línea 141: | Línea 143: | ||
=== Estructura === | === Estructura === | ||
La DB API usa dos conceptos para realizar los procesos: | La DB API usa dos conceptos para realizar los procesos: | ||
− | * Objeto Conexión | + | * '''Objeto Conexión''' |
** conexión con la base de datos | ** conexión con la base de datos | ||
** Transacciones | ** Transacciones | ||
− | * Objeto Cursor | + | * '''Objeto Cursor''' |
** Ejecuta las sentencias | ** Ejecuta las sentencias | ||
** Accede a los resultados | ** Accede a los resultados | ||
Línea 166: | Línea 168: | ||
=== Transacciones === | === Transacciones === | ||
* DB API 2.0 soporta transacciones (si el motor las soporta) desde el objeto conexión. | * DB API 2.0 soporta transacciones (si el motor las soporta) desde el objeto conexión. | ||
− | * conexión: commit / rollback | + | * conexión: commit() / rollback() |
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
=== Introspección del esquema === | === Introspección del esquema === | ||
* Busca el tipo de las columnas de una tabla: | * Busca el tipo de las columnas de una tabla: | ||
− | + | <ul><li> | |
− | <source lang="python"> | + | Método sencillo: |
+ | <source lang="python"> | ||
cursor.execute(‘select * from testtable where 1=0’) | cursor.execute(‘select * from testtable where 1=0’) | ||
# mira el atributo cursor.description | # mira el atributo cursor.description | ||
</source> | </source> | ||
− | + | </li><li> | |
+ | Método avanzado: | ||
<source lang="python"> | <source lang="python"> | ||
cursor.columns(table='testtable') | cursor.columns(table='testtable') | ||
rows = cursor.fetchall() | rows = cursor.fetchall() | ||
</source> | </source> | ||
+ | </li></ul> | ||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
=== Muy importante: Paso de parámetros === | === Muy importante: Paso de parámetros === | ||
− | * No hay | + | * '''No hay que hacer nunca sustitución de cadenas de caracteres para evitar inyección de código.''' |
− | * | + | </div> |
+ | <div class="slide"> | ||
+ | === Paso de parámetros === | ||
+ | * '''paramstyle''': define cómo se pasan los parámetros. | ||
* Todos los módulos admiten al menos uno de: | * Todos los módulos admiten al menos uno de: | ||
** 'qmark': Signo de interrogación, ej. '...WHERE name=?' | ** 'qmark': Signo de interrogación, ej. '...WHERE name=?' | ||
Línea 194: | Línea 202: | ||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
− | === Ejemplo con sqlite === | + | === Ejemplo con sqlite (I)=== |
<source lang="python"> | <source lang="python"> | ||
# Fuente: http://mundogeek.net/archivos/2008/06/25/bases-de-datos-en-python | # Fuente: http://mundogeek.net/archivos/2008/06/25/bases-de-datos-en-python | ||
Línea 216: | Línea 224: | ||
bbdd.commit() | bbdd.commit() | ||
− | + | </source> | |
+ | </div> | ||
+ | <div class="slide"> | ||
+ | === Ejemplo con sqlite (cont.) === | ||
+ | <source lang="python"> | ||
# 3.3 select | # 3.3 select | ||
cursor.execute("""select * from empleados | cursor.execute("""select * from empleados | ||
Línea 241: | Línea 253: | ||
cursor = db.cursor() | cursor = db.cursor() | ||
− | # create the query | + | # create and exe the query |
query = "SELECT * FROM foo" | query = "SELECT * FROM foo" | ||
− | |||
− | |||
cursor.execute(query) | cursor.execute(query) | ||
Línea 254: | Línea 264: | ||
</source></div> | </source></div> | ||
<div class="slide"> | <div class="slide"> | ||
+ | |||
=== Ampliación MySQL === | === Ampliación MySQL === | ||
[[../MySQL python| Acceso a MySQL con Python]] | [[../MySQL python| Acceso a MySQL con Python]] | ||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
− | === Persistencia de objetos ===</div> | + | === Persistencia de objetos === |
+ | </div> | ||
<div class="slide"> | <div class="slide"> | ||
+ | |||
=== pickle=== | === pickle=== | ||
Pickle: convierte un objeto python en secuencia de bytes | Pickle: convierte un objeto python en secuencia de bytes | ||
Línea 347: | Línea 360: | ||
=== Instalar === | === Instalar === | ||
$ sudo easy_install elixir # pip install elixir | $ sudo easy_install elixir # pip install elixir | ||
+ | |||
+ | Podemos descargar el fuente de: http://elixir.ematia.de/trac/wiki/Download | ||
+ | $ svn checkout http://elixir.ematia.de/svn/elixir/trunk/ elixir | ||
+ | |||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
+ | |||
=== Un modelo sencillo === | === Un modelo sencillo === | ||
La clase '''Entity''' define clases, tablas y mapper en un sólo paso. | La clase '''Entity''' define clases, tablas y mapper en un sólo paso. | ||
Línea 371: | Línea 389: | ||
<div class="slide"> | <div class="slide"> | ||
=== Creación de la bases de datos === | === Creación de la bases de datos === | ||
− | |||
<source lang="python"> | <source lang="python"> | ||
>>> from modelo import * | >>> from modelo import * | ||
− | >>> setup_all() # crea | + | >>> setup_all() # crea el objeto Tabla SQLAlchemy y el objeto Mapper para la clase Película. |
>>> create_all() # crea las tablas SQL correspondientes a la Tabla SQLAlchemy | >>> create_all() # crea las tablas SQL correspondientes a la Tabla SQLAlchemy | ||
</source> | </source> | ||
− | |||
<source lang="sql"> | <source lang="sql"> | ||
Línea 388: | Línea 404: | ||
) | ) | ||
</source> | </source> | ||
− | + | </div> | |
− | Por defecto la tabla se llama '''<nombre_del_modulo>_<nombre_de_la_clase>'''. | + | <div class="slide"> |
− | Si ningún campo tiene el parámetro '''primary_key=True''', crea un atributo '''id'''. | + | === Creación de la bases de datos II=== |
− | + | * Por defecto la tabla se llama '''<nombre_del_modulo>_<nombre_de_la_clase>'''. | |
+ | * Si ningún campo tiene el parámetro '''primary_key=True''', crea un atributo '''id'''. | ||
<source lang="python"> | <source lang="python"> | ||
>>> Pelicula(titulo=u"Blade Runner", year=1982) | >>> Pelicula(titulo=u"Blade Runner", year=1982) | ||
Línea 397: | Línea 414: | ||
>>> session.commit() | >>> session.commit() | ||
</source> | </source> | ||
− | + | </div> | |
+ | <div class="slide"> | ||
+ | === Búsquedas === | ||
<source lang="python"> | <source lang="python"> | ||
>>> Pelicula.query.all() | >>> Pelicula.query.all() | ||
Línea 439: | Línea 458: | ||
return '<Director "%s">' % self.nombre | return '<Director "%s">' % self.nombre | ||
</source> | </source> | ||
− | + | </div> | |
+ | <div class="slide"> | ||
+ | === Relaciones II === | ||
<source lang="python"> | <source lang="python"> | ||
>>> from model import * | >>> from model import * | ||
Línea 461: | Línea 482: | ||
) | ) | ||
</source> | </source> | ||
− | + | </div> | |
+ | <div class="slide"> | ||
+ | === Relaciones III=== | ||
<source lang="python"> | <source lang="python"> | ||
# directores | # directores | ||
Línea 479: | Línea 502: | ||
>>> session.commit() | >>> session.commit() | ||
</source> | </source> | ||
− | |||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
Línea 492: | Línea 514: | ||
>>> Pelicula.query.filter(Pelicula.director.has(Director.nombre.endswith(u'Scott'))).all() | >>> Pelicula.query.filter(Pelicula.director.has(Director.nombre.endswith(u'Scott'))).all() | ||
[<Pelicula "Alien" (1979)>, <Pelicula "Blade Runner" (1982)>] | [<Pelicula "Alien" (1979)>, <Pelicula "Blade Runner" (1982)>] | ||
− | + | </source> | |
− | + | </div> | |
− | + | <div class="slide"> | |
+ | === Búsquedas generativas === | ||
+ | (usando partes de otras búsquedas) | ||
+ | <source lang="python"> | ||
>>> d = Director.get_by(nombre=u'Ridley Scott') # Class.get_by(xxx) es un atajo para Class.query.filter_by(xxx).first() | >>> d = Director.get_by(nombre=u'Ridley Scott') # Class.get_by(xxx) es un atajo para Class.query.filter_by(xxx).first() | ||
>>> q = Pelicula.query.filter_by(director=d) | >>> q = Pelicula.query.filter_by(director=d) | ||
Línea 506: | Línea 531: | ||
<div class="slide"> | <div class="slide"> | ||
===Relaciones muchos a muchos=== | ===Relaciones muchos a muchos=== | ||
− | |||
<source lang="python"> | <source lang="python"> | ||
− | |||
− | |||
class Genero(Entity): | class Genero(Entity): | ||
nombre = Field(Unicode(15), primary_key=True) | nombre = Field(Unicode(15), primary_key=True) | ||
peliculas = ManyToMany('Pelicula') | peliculas = ManyToMany('Pelicula') | ||
− | |||
def __repr__(self): | def __repr__(self): | ||
return '<Genero "%s">' % self.nombre | return '<Genero "%s">' % self.nombre | ||
Línea 527: | Línea 548: | ||
return '<Pelicula "%s" (%d)>' % (self.titulo, self.year) | return '<Pelicula "%s" (%d)>' % (self.titulo, self.year) | ||
</source> | </source> | ||
− | + | </div> | |
+ | <div class="slide"> | ||
+ | ===Relaciones muchos a muchos (cont.)=== | ||
<source lang="python"> | <source lang="python"> | ||
>>> from model import * | >>> from model import * | ||
Línea 541: | Línea 564: | ||
[<Pelicula "Alien" (1979)>] | [<Pelicula "Alien" (1979)>] | ||
</source> | </source> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
===Herencia=== | ===Herencia=== | ||
− | |||
Si pensamos en introducir actores y directores: | Si pensamos en introducir actores y directores: | ||
− | |||
<source lang="python"> | <source lang="python"> | ||
class Persona(Entity): | class Persona(Entity): | ||
Línea 572: | Línea 574: | ||
def __repr__(self): | def __repr__(self): | ||
return '<Persona "%s">' % self.nombre | return '<Persona "%s">' % self.nombre | ||
− | + | </source> | |
+ | </div> | ||
+ | <div class="slide"> | ||
+ | ===Herencia (cont.)=== | ||
+ | <source lang="python"> | ||
class Actor(Persona): | class Actor(Persona): | ||
using_options(inheritance='multi') | using_options(inheritance='multi') | ||
peliculas = ManyToMany('Pelicula') | peliculas = ManyToMany('Pelicula') | ||
− | |||
def __repr__(self): | def __repr__(self): | ||
return '<Actor "%s">' % self.nombre | return '<Actor "%s">' % self.nombre | ||
Línea 583: | Línea 588: | ||
using_options(inheritance='multi') | using_options(inheritance='multi') | ||
peliculas = OneToMany('Pelicula') | peliculas = OneToMany('Pelicula') | ||
− | |||
def __repr__(self): | def __repr__(self): | ||
return '<Director "%s">' % self.nombre | return '<Director "%s">' % self.nombre | ||
</source> | </source> | ||
− | + | </div> | |
+ | <div class="slide"> | ||
+ | ===Herencia (cont.)=== | ||
<source lang="python"> | <source lang="python"> | ||
>>> rscott = Director(nombre=u"Ridley Scott") | >>> rscott = Director(nombre=u"Ridley Scott") | ||
Línea 595: | Línea 601: | ||
>>> sweaver = Actor(nombre=u"Sigourney Weaver") | >>> sweaver = Actor(nombre=u"Sigourney Weaver") | ||
>>> session.commit() | >>> session.commit() | ||
− | + | </source> | |
+ | </div> | ||
+ | <div class="slide"> | ||
+ | ===Herencia=== | ||
+ | <source lang="python"> | ||
>>> Persona.query.all() | >>> Persona.query.all() | ||
[<Director "Ridley Scott">, <Director "George Lucas">, | [<Director "Ridley Scott">, <Director "George Lucas">, | ||
Línea 627: | Línea 637: | ||
know, I’ll use regular expressions.” Now they have two | know, I’ll use regular expressions.” Now they have two | ||
problems. | problems. | ||
− | – Jamie Zawinski | + | – Jamie Zawinski |
<source lang="python"> | <source lang="python"> | ||
from urllib import urlopen | from urllib import urlopen | ||
Línea 633: | Línea 643: | ||
doc = urlopen(URL).read() | doc = urlopen(URL).read() | ||
</source> | </source> | ||
+ | </div> | ||
+ | <div class="slide"> | ||
+ | === Alternativas (cont.)=== | ||
* Usar parsers de html/xml. Estos parsers tiene que poder leer '''tagsoup''' porque se encontrarán con código no válido: | * Usar parsers de html/xml. Estos parsers tiene que poder leer '''tagsoup''' porque se encontrarán con código no válido: | ||
** BeautifulSoup, lxml, '''amara'''. | ** BeautifulSoup, lxml, '''amara'''. | ||
+ | |||
+ | Nosotros usaremos Amara: http://wiki.xml3k.org/Amara ([http://wiki.xml3k.org/Amara/Tutorial Tutorial]) | ||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
=== Amara === | === Amara === | ||
− | * | + | * Interfaz rápida con API más cercana al xml |
− | + | ||
− | + | ||
− | + | ||
<source lang="python"> | <source lang="python"> | ||
import amara | import amara | ||
Línea 647: | Línea 659: | ||
doc = amara.parse(URL) | doc = amara.parse(URL) | ||
</source> | </source> | ||
− | + | * API más pythonica: | |
− | + | ||
<source lang="python"> | <source lang="python"> | ||
from amara import bindery | from amara import bindery | ||
Línea 654: | Línea 665: | ||
doc = bindery.parse(URL) | doc = bindery.parse(URL) | ||
</source> | </source> | ||
+ | </div> | ||
+ | <div class="slide"> | ||
+ | === Tagsoup=== | ||
+ | * Para usar html no válido: | ||
<source lang="python"> | <source lang="python"> | ||
− | |||
from amara.bindery import html | from amara.bindery import html | ||
URL = '....' | URL = '....' | ||
doc = html.parse(URL) | doc = html.parse(URL) | ||
</source> | </source> | ||
− | + | </div> | |
<div class="slide"> | <div class="slide"> | ||
+ | |||
=== Instalar === | === Instalar === | ||
Para instalar la última versión: | Para instalar la última versión: | ||
$ sudo pip install http://files.akara.info/00-amara-latest.tar.bz2 | $ sudo pip install http://files.akara.info/00-amara-latest.tar.bz2 | ||
− | Es necesario tener instalado un compilador de C y la cabeceras de python (en debian/ubuntu hay que instalar '''python-dev''' | + | También puedes descargar el código del repositorio: |
+ | $ git clone https://github.com/zepheira/amara.git | ||
+ | |||
+ | Es necesario tener instalado un compilador de C y la cabeceras de python (en debian/ubuntu hay que instalar '''python-dev''') Para windows hay versiones precompiladas. | ||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
− | === Ejemplos habituales de uso === | + | === Ejemplos habituales de uso === |
− | + | ||
===Búsqueda por expresiones XPATH=== | ===Búsqueda por expresiones XPATH=== | ||
{{Tip | Hay herramientas como [http://getfirebug.com/ firebug] que permite copiar el XPATH de un elemento.}}</div> | {{Tip | Hay herramientas como [http://getfirebug.com/ firebug] que permite copiar el XPATH de un elemento.}}</div> | ||
<div class="slide"> | <div class="slide"> | ||
− | === | + | ===Ej.: Búsqueda de las imágenes de un artículo=== |
<source lang="python"> | <source lang="python"> | ||
>>> from amara.bindery import html | >>> from amara.bindery import html | ||
Línea 682: | Línea 699: | ||
65 | 65 | ||
>>> primera_imagen = imagenes[0] | >>> primera_imagen = imagenes[0] | ||
+ | </source> | ||
+ | </div> | ||
+ | <div class="slide"> | ||
+ | ===Ej.: Búsqueda de las imágenes (cont.) === | ||
+ | <source lang="python"> | ||
>>> print primera_imagen.xml_encode() | >>> print primera_imagen.xml_encode() | ||
<img src="/MODULOS/global/publico/interfaces/img/logo-Heraldo.png" alt="Últimas noticias de Aragón, Zaragoza, Huesca y Teruel del periódico digital Heraldo.es"/> | <img src="/MODULOS/global/publico/interfaces/img/logo-Heraldo.png" alt="Últimas noticias de Aragón, Zaragoza, Huesca y Teruel del periódico digital Heraldo.es"/> | ||
Línea 693: | Línea 715: | ||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
− | + | ===Búsqueda de las entradas de una revista=== | |
Barrapunto publica sus entradas como | Barrapunto publica sus entradas como | ||
<source lang="html4strict"> | <source lang="html4strict"> | ||
Línea 711: | Línea 733: | ||
</div> | </div> | ||
</source> | </source> | ||
+ | </div> | ||
+ | <div class="slide"> | ||
+ | ===Búsqueda de las entradas de una revista (cont.)=== | ||
Para extraer los nombres de los artículos de la primera página: | Para extraer los nombres de los artículos de la primera página: | ||
− | |||
<source lang="python"> | <source lang="python"> | ||
>>> from amara.bindery import html | >>> from amara.bindery import html | ||
Línea 729: | Línea 753: | ||
... | ... | ||
</source> | </source> | ||
− | + | </div> | |
− | '''Más ejemplos''' en http://wiki.xml3k.org/Amara/Recipes</div> | + | <div class="slide"> |
+ | ===Búsqueda de las entradas de una revista (cont.) === | ||
+ | '''Más ejemplos''' en http://wiki.xml3k.org/Amara/Recipes | ||
+ | </div> | ||
<div class="slide"> | <div class="slide"> | ||
− | + | ===Expresiones XPATH útiles=== | |
<source lang="python"> | <source lang="python"> | ||
# Nodo que contenga una cadena de texto: | # Nodo que contenga una cadena de texto: | ||
Línea 738: | Línea 765: | ||
expresion = u'.//text()[contains(., "%s")]' % cadena.decode('utf-8') | expresion = u'.//text()[contains(., "%s")]' % cadena.decode('utf-8') | ||
# Nodos o atributos que contengan una cadena: | # Nodos o atributos que contengan una cadena: | ||
− | expresion | + | expresion = u'.//@*[contains(., "%s")]' |
</source> | </source> | ||
− | |||
</div> | </div> | ||
<div class="slide"> | <div class="slide"> | ||
+ | |||
===Inyección de marcado === | ===Inyección de marcado === | ||
Se puede transformar un documento para añadirle o quitarle información. | Se puede transformar un documento para añadirle o quitarle información. |