| You are here: Inicio > Inmersión en Python > Procesamiento de HTML > Presentación de BaseHTMLProcessor.py | << >> | ||||
Inmersión en PythonPython de novato a experto |
|||||
SGMLParser no produce nada por sí mismo. Analiza, analiza y analiza, e invoca métodos por cada cosa interesante que encuentra, pero los métodos no hacen nada. SGMLParser es un consumidor de HTML: toma un HTML y lo divide en trozos pequeños y estructurados. Como vio en la sección anterior, puede derivar SGMLParser para definir clases que capturen etiquetas específicas y produzcan cosas útiles, como una lista de todos los enlaces en una página web. Ahora llevará esto un paso más allá, definiendo una clase que capture todo lo que SGMLParser le lance, reconstruyendo el documento HTML por completo. En términos técnicos, esta clase será un productor de HTML.
BaseHTMLProcessor deriva de SGMLParser y proporciona los 8 métodos de manipulación esenciales: unknown_starttag, unknown_endtag, handle_charref, handle_entityref, handle_comment, handle_pi, handle_decl y handle_data.
class BaseHTMLProcessor(SGMLParser): def reset(self):self.pieces = [] SGMLParser.reset(self) def unknown_starttag(self, tag, attrs):
strattrs = "".join([' %s="%s"' % (key, value) for key, value in attrs]) self.pieces.append("<%(tag)s%(strattrs)s>" % locals()) def unknown_endtag(self, tag):
self.pieces.append("</%(tag)s>" % locals()) def handle_charref(self, ref):
self.pieces.append("&#%(ref)s;" % locals()) def handle_entityref(self, ref):
self.pieces.append("&%(ref)s" % locals()) if htmlentitydefs.entitydefs.has_key(ref): self.pieces.append(";") def handle_data(self, text):
self.pieces.append(text) def handle_comment(self, text):
self.pieces.append("<!--%(text)s-->" % locals()) def handle_pi(self, text):
self.pieces.append("<?%(text)s>" % locals()) def handle_decl(self, text): self.pieces.append("<!%(text)s>" % locals())
| reset, invocado por SGMLParser.__init__, inicializa self.pieces como una lista vacía antes de llamar al método del ancestro. self.pieces es un atributo de datos que contendrá las partes del documento HTML que usted está construyendo. Cada método manejador reconstruirá el HTML analizado por SGMLParser añadiendo esa cadena a self.pieces. Observe que self.pieces es una lista. Podría tentarle definirla como una cadena y limitarse a concatenar cada parte. Esto funcionaría, pero Python es mucho más eficiente tratando listas.[6] | |
| Dado que BaseHTMLProcessor no define métodos para etiquetas específicas (como el método start_a de URLLister), SGMLParser invocará a unknown_starttag por cada etiqueta de inicio. Este método toma la etiqueta (tag) y la lista de pares nombre/valor de atributos (attrs), reconstruye el HTML original y lo añade a self.pieces. La cadena de formato que vemos es un poco extraña; la desentrañará (y también esa función locals de pinta tan extraña) más adelante en este capítulo. | |
| Reconstruir las etiquetas de final es mucho más simple; basta tomar el nombre de la etiqueta y encerrarlo entre </...>. | |
| Cuando SGMLParser encuentra una referencia a un carácter, llama a handle_charref pasándole la referencia sin delimitadores. Si el documento HTML contiene la referencia  , ref será 160. Reconstruir la referencia original completa implica sólo encapsular ref con los caracteres &#...;. | |
| Las referencias a entidades son similares a las referencias a caracteres, pero sin la marca #. Reconstruir la referencia original implica encapsular ref dentro de &...;. (En realidad, como un erudito lector me ha señalado, es ligeramente más complicado que esto. Sólo ciertas entidades estándar de HTML terminan en punto y coma; el resto de entidades similares no lo hacen. Por suerte para nosotros, el conjunto de entidades estándar de HTML está definido en un diccionario del módulo de Python llamado htmlentitydefs. De ahí la sentencia if adicional) | |
| Los bloques de texto se añaden a self.pieces sin alterar, simplemente. | |
| Los comentarios de HTML van escritos entre los caracteres <!--...-->. | |
| Las instrucciones de procesamiento van escritas entre los caracteres <?...> . |
| La especificación de HTML precisa que todo lo que no sea HTML (como JavaScript para el cliente) debe estar encerrado dentro de comentarios de HTML, pero no todas las páginas web lo hacen correctamente (y todos los navegadores modernos hacen la vista gorda en ese caso). BaseHTMLProcessor no es tan permisivo; si un script no está adecuadamente embebido, será analizado como si fuera HTML. Por ejemplo, si el script contiene símbolos "igual" o "menor que", SGMLParser puede entender de incorrectamente que ha encontrado etiquetas y atributos. SGMLParser siempre convierte los nombres de etiquetas y atributos a minúsculas, lo que puede inutilizar el script, y BaseHTMLProcessor siempre encierra los valores de atributos dentro de comillas dobles (incluso si el documento original utiliza comillas simples o ningún tipo de comillas), lo que hará inútil el script con certeza. Proteja siempre sus script dentro de comentarios de HTML. | |
def output(self):
"""Return processed HTML as a single string"""
return "".join(self.pieces) 
[6] La razón de que Python sea mejor con las listas que con las cadenas es que las listas son mutables pero las cadenas son inmutables. Esto significa que añadir a una lista simplemente agrega el elemento y actualiza el índice. Como las cadenas no se pueden modificar tras haber sido creadas, un código como s = s + nuevaparte creará una cadena complemente nueva partiendo de la concatenación de la original y de la nueva parte, para asignarla luego a la variable de la cadena original. Esto implica mucha gestión de memoria costosa, y la cantidad de esfuerzo empleado aumenta cuando la cadena va creciendo, de manera que hacer s = s + nuevaparte en un bucle es mortal. En términos técnicos, agregar n elementos a una lista es O(n), mientras que añadir n elementos a una cadena es O(n2).
<< Extracción de datos de documentos HTML |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
locals y globals >> |