`
sobre dicho documento.
Cuando informes de algún error en esta documentación, por favor,
indica la traducción que estás leyendo.
===============
Inicio rápido
===============
Este es un documento HTML que usaré como ejemplo a lo largo de este
documento. Es parte de una historia de `Alicia en el país de las maravillas`::
html_doc = """The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well.
...
"""
Al procesar el documento de "Las tres hermanas" en Beautiful Soup, se nos
devuelve un objeto :py:class:`BeautifulSoup`, que representa el
documento como una estructura de datos anidada::
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.prettify())
#
#
#
# The Dormouse's story
#
#
#
#
#
# The Dormouse's story
#
#
#
# Once upon a time there were three little sisters; and their names were
#
# Elsie
#
# ,
#
# Lacie
#
# and
#
# Tillie
#
# ; and they lived at the bottom of a well.
#
#
# ...
#
#
#
Estas son algunas de las maneras sencillas para navegar
por la estructura de datos::
soup.title
# The Dormouse's story
soup.title.name
# u'title'
soup.title.string
# u'The Dormouse's story'
soup.title.parent.name
# u'head'
soup.p
# The Dormouse's story
soup.p['class']
# u'title'
soup.a
# Elsie
soup.find_all('a')
# [Elsie,
# Lacie,
# Tillie]
soup.find(id="link3")
# Tillie
Una tarea frecuente es extraer todas las URL encontradas en las etiquetas
de una página::
for link in soup.find_all('a'):
print(link.get('href'))
# http://example.com/elsie
# http://example.com/lacie
# http://example.com/tillie
Otra tarea habitual es extraer todo el texto de una página::
print(soup.get_text())
# The Dormouse's story
#
# The Dormouse's story
#
# Once upon a time there were three little sisters; and their names were
# Elsie,
# Lacie and
# Tillie;
# and they lived at the bottom of a well.
#
# ...
¿Esto se parece a lo que necesitas? Si es así, sigue leyendo.
=========================
Instalar Beautiful Soup
=========================
Si usas una versión reciente de Debian o Ubuntu Linux, puedes instalar
Beautiful Soup con el gestor de paquetes del sistema:
:kbd:`$ apt-get install python3-bs4`
Beautiful Soup 4 está publicado en Pypi, así que si no puedes instalarlo
con el gestor de paquetes, puedes instalarlo con ``easy_install`` o
``pip``. El nombre del paquete es ``beautifulsoup4``. Asegúrate de que
usas la versión correcta de ``pip`` o ``easy_install`` para tu versión
de Python (podrían llamarse ``pip3`` y ``easy_install3``, respectivamente):
:kbd:`$ easy_install beautifulsoup4`
:kbd:`$ pip install beautifulsoup4`
(El paquete :py:class:`BeautifulSoup` ``no`` es el que quieres. Ese es
el lanzamiento anterior `Beautiful Soup 3`_. Muchos *software* utilizan
BS3, así que aún está disponible, pero si estás escribiendo nuevo código,
deberías instalar ``beautifulsoup4``).
Si no tienes ``easy_install`` o ``pip`` instalados, puedes
`descargar el código de Beautiful Soup 4 comprimido en un tarball
`_ e
instalarlo con ``setup.py``:
:kbd:`$ python setup.py install`
Si aún así todo falla, la licencia de Beautiful Soup te permite
empaquetar la librería completa con tu aplicación. Puedes descargar
el *tarball*, copiar su directorio ``bs4`` en tu base de código y
usar Beautiful Soup sin instalarlo en absoluto.
Yo empleo Python 3.10 para desarrollar Beautiful Soup, aunque debería
funcionar con otras versiones recientes.
.. _parser-installation:
Instalar un analizador
======================
Beautiful Soup soporta el analizador de HTML incluido en la librería
estándar de Python, aunque también soporta varios analizadores de
Python de terceros. Uno de ellos es el `analizador de lxml `_.
Dependiendo de tu instalación, puedes instalar lxml con uno de los
siguientes comandos:
:kbd:`$ apt-get install python-lxml`
:kbd:`$ easy_install lxml`
:kbd:`$ pip install lxml`
Otra alternativa es usar el analizador de Python de
`html5lib `_,
el cual analiza HTML de la misma manera en la que lo haría
un navegador web. Dependiendo de tu instalación, puedes instalar
html5lib con uno de los siguientes comandos:
:kbd:`$ apt-get install python-html5lib`
:kbd:`$ easy_install html5lib`
:kbd:`$ pip install html5lib`
Esta tabla resume las ventajas e inconvenientes de cada librería de los analizadores:
+-----------------------+--------------------------------------------+-----------------------------------+-----------------------------+
| Analizador | Uso típico | Ventajas | Desventajas |
+-----------------------+--------------------------------------------+-----------------------------------+-----------------------------+
| html.parser de Python | ``BeautifulSoup(markup, "html.parser")`` | * Ya incluido | * No tan rápido como lxml, |
| | | * Rapidez decente | menos tolerante que |
| | | * Tolerante (en Python 3.2) | html5lib. |
+-----------------------+--------------------------------------------+-----------------------------------+-----------------------------+
| Analizador HTML de | ``BeautifulSoup(markup, "lxml")`` | * Muy rápido | * Dependencia externa de C |
| lxml | | * Tolerante | |
+-----------------------+--------------------------------------------+-----------------------------------+-----------------------------+
| Analizador XML de | ``BeautifulSoup(markup, "lxml-xml")`` | * Muy rápido | * Dependencia externa de C |
| lxml | ``BeautifulSoup(markup, "xml")`` | * El único analizador XML | |
| | | actualmente soportado | |
+-----------------------+--------------------------------------------+-----------------------------------+-----------------------------+
| html5lib | ``BeautifulSoup(markup, "html5lib")`` | * Extremadamente tolerante | * Muy lento |
| | | * Analiza las páginas de la misma | * Dependencia externa de |
| | | manera que un navegador web | Python |
| | | * Crea HTML5 válido | |
+-----------------------+--------------------------------------------+-----------------------------------+-----------------------------+
Si puedes, te recomiendo que instales y uses lxml para mayor velocidad.
Ten en cuenta que si un documento es inválido, analizadores diferentes
generarán árboles de Beautiful Soup diferentes para él. Mira
`Diferencias entre analizadores`_ para más detalle.
==================
Haciendo la sopa
==================
Para analizar un documento pásalo al constructor de :py:class:`BeautifulSoup`.
Puedes pasar una cadena de caracteres o abrir un manejador de archivos::
from bs4 import BeautifulSoup
with open("index.html") as fp:
soup = BeautifulSoup(fp, 'html.parser')
soup = BeautifulSoup("a web page", 'html.parser')
Primero, el documento se convierte a Unicode, y las entidades HTML se
convierten a caracteres Unicode::
print(BeautifulSoup("Sacré bleu!", "html.parser"))
# Sacré bleu!
Entonces Beautiful Soup analiza el documento usando el mejor analizador
disponible. Usará un analizador HTML a no ser que se especifique que se
use un analizador XML (ver `Analizar XML`_).
==================
Tipos de objetos
==================
Beautiful Soup transforma un complejo documento HTML en un complejo árbol de objetos
de Python. Pero tan solo tendrás que lidiar con cuatro `tipos` de objetos: :py:class:`Tag`,
:py:class:`NavigableString`, :py:class:`BeautifulSoup` y :py:class:`Comment`.
.. py:class:: Tag
Un objeto :py:class:`Tag` corresponde a una etiqueta XML o HTML en el documento
original.
::
soup = BeautifulSoup('Extremely bold', 'html.parser')
tag = soup.b
type(tag)
#
Las etiquetas tienen muchos atributos y métodos, y cubriré la mayoría de ellos en
`Navegar por el árbol`_ y `Buscar en el árbol`_. Por ahora, las características
más importantes de una etiqueta son su nombre y sus atributos.
.. py:attribute:: name
Toda etiqueta tiene un nombre::
tag.name
# 'b'
Si cambias el nombre de una etiqueta, el cambio se verá reflejado en
cualquier especificación generada por Beautiful Soup a partir de entonces::
tag.name = "blockquote"
tag
# Extremely bold
.. py:attribute:: attrs
Una etiqueta HTML o XML puede tener cualquier cantidad de atributos.
La etiqueta ```` tiene un atributo "id" cuyo valor
es "boldest". Puedes acceder a los atributos de una etiqueta
usándola como un diccionario::
tag = BeautifulSoup('bold', 'html.parser').b
tag['id']
# 'boldest'
Puedes acceder a los atributos del diccionario directamente con ``.attrs``::
tag.attrs
# {'id': 'boldest'}
Puedes añadir, quitar y modificar los atributos de una etiqueta. De nuevo, esto
se realiza usando la etiqueta como un diccionario::
tag['id'] = 'verybold'
tag['another-attribute'] = 1
tag
#
del tag['id']
del tag['another-attribute']
tag
# bold
tag['id']
# KeyError: 'id'
tag.get('id')
# None
.. _multivalue:
Atributos multivaluados
-----------------------
HTML 4 define algunos atributos que pueden tomar múltiples valores. HTML 5
elimina un par de ellos, pero define unos cuantos más. El atributo multivaluado
más común es ``class`` (esto es, una etiqueta puede tener más de una clase de CSS).
Otros incluyen ``rel``, ``rev``, ``accept-charset``, ``headers`` y ``accesskey``.
Por defecto, Beautiful Soup transforma los valores de un atributo multivaluado en
una lista::
css_soup = BeautifulSoup('', 'html.parser')
css_soup.p['class']
# ['body']
css_soup = BeautifulSoup('', 'html.parser')
css_soup.p['class']
# ['body', 'strikeout']
Si un atributo `parece` que tiene más de un valor, pero no es un atributo
multivaluado definido como tal por ninguna versión del estándar de HTML,
Beautiful Soup no modificará el atributo::
id_soup = BeautifulSoup('', 'html.parser')
id_soup.p['id']
# 'my id'
Cuando transformas una etiqueta en una cadena de caracteres, muchos atributos
se combinan::
rel_soup = BeautifulSoup('Back to the homepage
', 'html.parser')
rel_soup.a['rel']
# ['index', 'first']
rel_soup.a['rel'] = ['index', 'contents']
print(rel_soup.p)
# Back to the homepage
Puedes forzar que todos los atributos sean analizados como cadenas
de caracteres pasando ``multi_valued_attributes=None`` como argumento
clave en el constructor de :py:class:`BeautifulSoup`::
no_list_soup = BeautifulSoup('', 'html.parser', multi_valued_attributes=None)
no_list_soup.p['class']
# 'body strikeout'
Puedes usar ``get_attribute_list`` para obtener un valor que siempre sea una lista,
sin importar si es un atributo multivaluado::
id_soup.p.get_attribute_list('id')
# ["my id"]
Si analizas un documento como XML, no hay atributos multivaluados::
xml_soup = BeautifulSoup('', 'xml')
xml_soup.p['class']
# 'body strikeout'
Una vez más, puedes configurar esto usando el argumento ``multi_valued_attributes`` ::
class_is_multi= { '*' : 'class'}
xml_soup = BeautifulSoup('', 'xml', multi_valued_attributes=class_is_multi)
xml_soup.p['class']
# ['body', 'strikeout']
Probablemente no tengas que hacer esto, pero si lo necesitas, usa los
parámetros por defecto como guía. Implementan las reglas descritas en la
especificación de HTML::
from bs4.builder import builder_registry
builder_registry.lookup('html').DEFAULT_CDATA_LIST_ATTRIBUTES
.. py:class:: NavigableString
-----------------------------
Un *string* corresponde a un trozo de texto en una etiqueta. Beautiful Soup usa la clase
:py:class:`NavigableString` para contener estos trozos de texto::
soup = BeautifulSoup('Extremely bold', 'html.parser')
tag = soup.b
tag.string
# 'Extremely bold'
type(tag.string)
#
Un :py:class:`NavigableString` es como una cadena de caracteres de Python Unicode,
exceptuando que también soporta algunas de las características descritas en
`Navegar por el árbol`_ y `Buscar en el árbol`_. Puedes convertir un objeto
:py:class:`NavigableString` a una cadena de caracteres Unicode usando ``str``::
unicode_string = str(tag.string)
unicode_string
# 'Extremely bold'
type(unicode_string)
#
No puedes editar dicha cadena, pero puedes reemplazar una cadena por otra, usando
:ref:`replace_with()`::
tag.string.replace_with("No longer bold")
tag
# No longer bold
:py:class:`NavigableString` soporta la mayoría de las características descritas en
`Navegar por el árbol`_ y `Buscar en el árbol`_, pero no todas.
En particular, como una cadena no puede contener nada (la manera en la que
una etiqueta contiene una cadena de caracteres u otra etiqueta), *strings* no
admiten los atributos `.contents`` o ``.string``, o el método ``find()``.
Si quieres usar un :py:class:`NavigableString` fuera de Beautiful Soup,
deberías llamar ``unicode()`` sobre él para convertirlo en una cadena de caracteres
de Python Unicode. Si no, tu cadena arrastrará una referencia a todo el árbol analizado
de Beautiful Soup, incluso cuando hayas acabado de utilizar Beautiful Soup. Esto es un
gran malgasto de memoria.
.. py:class:: BeautifulSoup
---------------------------
El objeto :py:class:`BeautifulSoup` representa el documento analizado
en su conjunto. Para la mayoría de propósitos, puedes usarlo como un objeto
:py:class:`Tag`. Esto significa que soporta la mayoría de métodos descritos
en `Navegar por el árbol`_ and `Buscar en el árbol`_.
Puedes también pasar un objeto :py:class:`BeautifulSoup` en cualquiera de
los métodos definidos en `Modificar el árbol`_, como si fuese un :py:class:`Tag`.
Esto te permite hacer cosas como combinar dos documentos analizados::
doc = BeautifulSoup("INSERT FOOTER HEREHere's the footer", "xml")
doc.find(text="INSERT FOOTER HERE").replace_with(footer)
# 'INSERT FOOTER HERE'
print(doc)
#
#
Como un objeto :py:class:`BeautifulSoup` no corresponde realmente con una
etiqueta HTML o XML, no tiene nombre ni atributos. Aún así, es útil
comprobar su ``.name``, así que se le ha dado el ``.name`` especial
"[document]"::
soup.name
# '[document]'
Cadenas especiales
==================
:py:class:`Tag`, :py:class:`NavigableString` y
:py:class:`BeautifulSoup` cubren la mayoría de todo lo que verás en
un archivo HTML o XML, aunque aún quedan algunos remanentes. El principal
que probablemente encuentres es el :py:class:`Comment`.
.. py:class:: Comment
::
markup = ""
soup = BeautifulSoup(markup, 'html.parser')
comment = soup.b.string
type(comment)
#
El objeto :py:class:`Comment` es solo un tipo especial de :py:class:`NavigableString`::
comment
# 'Hey, buddy. Want to buy a used parser'
Pero cuando aparece como parte de un documento HTML, un :py:class:`Comment`
se muestra con un formato especial::
print(soup.b.prettify())
#
#
#
Para documentos HTML
--------------------
Beautiful Soup define algunas subclases de :py:class:`NavigableString`
para contener cadenas de caracteres encontradas dentro de etiquetas
HTML específicas. Esto hace más fácil tomar el cuerpo principal de la
página, ignorando cadenas que probablemente representen directivas de
programación encontradas dentro de la página. `(Estas clases son nuevas
en Beautiful Soup 4.9.0, y el analizador html5lib no las usa)`.
.. py:class:: Stylesheet
Una subclase de :py:class:`NavigableString` que representa hojas de estilo
CSS embebidas; esto es, cualquier cadena en una etiqueta
``