Seguimos con la traducción de algunos capítulos del libro The Programming Historian, de William J. Turkel, Adam Crymble y Alan MacEachern.
Ahora vamos con el capítulo 5.
Del HTML a una lista de palabras
Quitando el formato HTML
A menudo estamos interesados en conservar el contenido textual de una fuente online para procesarla, pero para ello tenemos que deshacernos de las etiquetas y metadata HTML. Vamos a empezar haciéndolo de una manera rápida y desprolija. En el lenguaje HTML que vimos hasta ahora hay unos tipos básicos de etiquetas. Para quitar todas ellas parece seguro ignorar todo lo que esté entre un par de símbolos menor y mayor (< como apertura y > como clausura).
Nuestro algoritmo sera algo así:
1. Comenzar con una cadena (string) vacía (de nombre html para guardar nuestro texto en ella.
2. Mirar cada carácter en la cadena html, uno a uno.
3. Si el carácter es un signo menor (<) estamos entrando a una etiqueta por lo tanto ignoramos el caracter.
4. Si el carácter es un signo mayor (>) estamos abandonando una etiqueta.
5. Si estamos dentro de la etiqueta, ignoramos el carácter, de otro modo lo agregamos a la cadena de texto.
Un algoritmo es un procedimiento especificado en detalle que puede ser implementado por una computadora. Lo implementaremos ahora.
Más sobre cadenas en Python
Ya vimos que hay dos maneras de delimitar cadenas: usando comillas simples o dobles.
mensaje1 = 'hola mundo' mensaje2 = "hola mundo"
Python posee un tercer tipo de cadena que admite múltiples líneas. Nos será útil más adelante.
mensaje3 = """hola hola hola mundo"""
Python incluye muchos comandos para manipular cadenas de textos. Si quiere experimentar con esos comandos, puede escribir y ejecutar pequeños programas como hacemos nosotros, o bien puede abrirlos en la interfase de Python.
Usted puede concatenar cadenas utilizando el operador de adición (+). Observe que debe ser explícito acerca de la cantidad y la ubicación de los espacios en blanco que desea en la cadena. También puede crear múltiples copias de cadenas usando el operador de multiplicación (*).
mensaje4 = 'hola' + ' ' + 'mundo' print mensaje4 -> hola mundo mensaje5a = 'hola ' * 3 mensaje5b = 'mundo' print mensaje5a + mensaje5b -> hola hola hola mundo
¿Y si quiere agregar sucesivamente material al final de cada cadena? Hay un operador especial para eso.
mensaje6 = 'hola' mensaje6 += ' ' mensaje6 += 'mundo' print mensaje6 -> hola mundo
Podemos determinar el número de caracteres de un cadena utilizando len. Observe que los espacios en blanco se cuentan como caracteres.
mensaje7 = 'hola' + ' ' + 'mundo' print len(mensaje7) -> 10
Finalmente, en algunas situaciones necesitará incluir comillas de distinto tipo en una cadena, y no querrá que el intérprete Python se haga una idea equivocada y finalice la cadena cuando se cruce con uno de esos caracteres. En Python, usted puede poner una barra inversa delante de un comilla para que indique que con ella no termina la cadena. Esas marcas son conocidas como secuencias de escape.
print '\"' -> " print 'El programa imprimió \"hola mundo\"' -> El programa imprimió "hola mundo"
Otras dos secuencias de escape permiten imprimir tabulaciones y saltos de línea:
print 'hola<strong>\t</strong>hola<strong>\t</strong>hola<strong>\n</strong>mundo' ->hola hola hola mundo
Para volver a nuestro algoritmo, primero tenemos que crear una cadena vacía para poner el texto allí.
texto = ''
Ok, eso fue fácil. Sabemos como agregar caracteres en esa cadena cuando los necesitemos:
texto += char
Looping
Ahora necesitamos un modo de mirar cada carácter en la cadena html. Como muchos otros lenguajes de programación, Python incluye varios mecanismos de
for char in html:
# aquí hacemos algo con charBranching [condicionales]
Lo siguiente que necesitamos es un modo de testear los contenidos de una cadena y elegir un curso de acción en base a esa prueba. Otra vez, como muchos lenguajes de programación, Python cuenta con mecanismos de branching. La que usaremos más abajo se llama sentencia condicional if. Le pedimos que mire si la cadena char que le pasamos contiene un signo menor.
if char == '<':
# hacemos algoUn modo más común del condicional if nos permite especificar qué hacer si el test es falso.
if char == '<':
# hacemos algo
else:
# hacemos algo distintoEn Python tenemos la opción de hacer tests supletorios luego del primero, usando el comando elif (que es una forma abreviada de else if).
if char == '<':
# hacemos algo
elif char == '>':
# hacemos otra cosa
else:
# hacemos algo completamente diferente.Para evitar confusiones, Python usa un signo igual simple (=) para asignar, que puede entender como establecer la igualdad de una cosa con cualquier otra. Con el fin de probar la igualdad usa el signo igual doble (==). A menudo los que recién comienzan a programar confunden ambos signos.
¿Cómo podemos saber cuándo estamos dentro de un tag del HTML? Podemos usar una variable numérica que llamaremos inside, la que será 1 (verdad) si estamos dentro de una ramificación y 0 (falso) si no lo estamos.
La rutina para quitar etiquetas de HTML
Para poner todo en un mismo paquete, la versión final de nuestra rutina se muestra más abajo. Copie ese código y péguelo en el Komodo. Guárdelo en un archivo llamado dh.py. Este archivo será el que contenga todo el código que nosotros queremos reutilizar. En otras palabras, dh.py es un módulo. (Más sobre esto puede hallarse en la página de discusión del wiki.)
# Dada una cadena que contiene HTML, remover todos los caracteres # que se encuentren entre un signo menor y uno mayor, incluyendo # esos signos. def stripTags(html): inside = 0 texto = '' for char in html: if char == '<': inside = 1 continue elif (inside == 1 and char == '>'): inside = 0 continue elif inside == 1: continue else: texto += char return texto
Como puede leerse en el código, nos enteramos que necesitamos una orden más para hacer que el código funcione. El comando de Python continue le dice al intérprete que salte al inicio del bucle. Por lo que si el carácter es un signo menor quedará asentado que estamos dentro de una etiqueta y ahí mismo terminamos el procesamiento de ese carácter. Necesitamos, en ese punto, continuar con el siguiente en la cadena html antes que continuar procesando un carácter que ya clasificamos.
Listas en Python
Ahora que ya tenemos la habilidad de extraer texto crudo de las páginas web, lo que queremos es tener ese texto en una forma que sea fácil de procesar. Hasta aquí, cuando necesitábamos guardar información en nuestros programas en Python usábamos cadenas. Hubo un par de excepciones, de todos modos. En la rutina para quitar las etiquetas del HTML, también usamos un entero llamado inside que tomaba el valor 1 cuando procesábamos una etiqueta y 0 cuando no lo hacíamos (más información en la página de discusión del wiki).
inside = 1
Y, por otro lado, para leer o escribir en un archive nosotros usamos un manejador de archivo especial al que le pusimos f en el ejemplo de más arriba.
f = open('holamundo.txt','w')
f.write('hola mundo')
f.close()De todos modos, uno de los tipos más usados de objetos que Python provee es la lista, una colección ordenada de otros objetos (incluidas, potencialmente, otras listas). El hecho de que las listas pueden contener listas hace que sean ideales para almacenar estructuras de tipo árbol, algo que explicaremos pronto y sobre lo que volveremos repetidamente. Es muy simple convertir una cadena a una lista de caracteres o una lista de palabras, como muestra el siguiente programa. Lo copiamos al Komodo, lo guardamos como cadena-a-lista.py y lo ejecutamos. Compare las dos listas que serán impresas en el panel de salida del Komodo.
# cadena-a-lista.py
# algunas cadenas
s1 = 'hola mundo'
s2 = 'Que tal mundo'
# lista de caracteres
charlist = []
for char in s1:
charlist.append(char)
print charlist
# lista de 'palabras'
wordlist = s2.split()
print wordlistLa primera rutina usa un bucle for para tomar un caracter por vez de la cadena s1 y agregarlo appends al final de la lista llamada charlist. La segunda rutina hace uso del método split para quebrar la cadena s2 allí donde tiene espacios en blanco (espacios, tabulaciones, retorno de párrafo y caracteres similares). A decir verdad, es una simplificación llamar a los objetos de la segunda lista “palabras”. Trate cambiando s2 en el programa de arriba por ‘Qué tal mundo!’ y ejecútelo nuevamente. ¿Qué pasa con el signo de admiración?
Dado que aprendió tanto, usted puede ahora abrir una URL, bajar la página web a una cadena, quitarle las marcas HTML y luego dividir el texto en una lista de palabras. Pruebe ejecutando este programa:
# html-a-lista-1.py import urllib2 import dh url = 'http://niche.uwo.ca/programming-historian/dcb/dcb-34298.html' response = urllib2.urlopen(url) html = response.read() texto = dh.stripTags(html) wordlist = texto.split() print wordlist[0:120]
Debería haber obtenido lo siguiente:
['Dictionary', 'of', 'Canadian', 'Biography', 'DOLLARD', 'DES', 'ORMEAUX', '(called', 'Daulat', 'in', 'his', 'death', 'certificate', 'and', 'Daulac', 'by', 'some', 'historians),', 'ADAM,', 'soldier,', '\x93garrison', 'commander', 'of', 'the', 'fort', 'of', 'Ville-Marie', '[Montreal]\x94;', 'b.', '1635,', 'killed', 'by', 'the', 'Iroquois', 'at', 'the', 'Long', 'Sault', 'in', 'May 1660.', '\xa0\xa0\xa0\xa0\xa0', 'Nothing', 'is', 'known', 'of', 'Dollard\x92s', 'activities', 'prior', 'to', 'his', 'arrival', 'in', 'Canada', 'except', 'that', '\x93he', 'had', 'held', 'some', 'commands', 'in', 'the', 'armies', 'of', 'France.\x94', 'Having', 'come', 'to', 'Montreal', 'as', 'a', 'volunteer,', 'very', 'probably', 'in', '1658,', 'he', 'continued', 'his', 'military', 'career', 'there.', 'In', '1659', 'and', '1660', 'he', 'was', 'described', 'as', 'an', '\x93officer\x94', 'or', '\x93garrison', 'commander', 'of', 'the', 'fort', 'of', 'Ville-Marie,\x94', 'a', 'title', 'that', 'he', 'shared', 'with', 'Pierre', 'Picot\xe9', 'de', 'Belestre.', 'We', 'do', 'not', 'however', 'know', 'what', 'his', 'particular', 'responsibility', 'was.']
Pero con tener una lista de palabras no lograremos mucho. Tendremos que hacer que nuestros programas puedan procesarla.