Python Web Scraping - BeautifulSoup

Python Web Scraping se refiere al proceso de extraer automáticamente información de Internet mediante la escritura de programas en Python.

El flujo básico de un web scraper generalmente incluye enviar solicitudes HTTP para obtener el contenido de la página web, analizar la página y extraer datos, y luego almacenar los datos.

El rico ecosistema de Python lo convierte en un lenguaje popular para desarrollar web scrapers, especialmente debido a su potente soporte de librerías.

En general, el proceso de un web scraper se puede dividir en los siguientes pasos:

  • Enviar solicitudes HTTP: el web scraper obtiene la página HTML del sitio web objetivo mediante solicitudes HTTP; las librerías comunes incluyenrequests。
  • Analizar el contenido HTML: después de obtener la página HTML, el web scraper necesita analizar el contenido y extraer datos; las librerías comunes sonBeautifulSoup、lxml、Scrapyetc.
  • Extraer datos: extraer los datos necesarios localizando elementos HTML (como etiquetas, atributos, nombres de clase, etc.).
  • Almacenar datos: almacenar los datos extraídos en bases de datos, archivos CSV, archivos JSON u otros formatos para su uso o análisis posterior.

Este capítulo presenta principalmente BeautifulSoup, que es una librería de Python para analizar documentos HTML y XML, capaz de extraer datos de páginas web, y se usa comúnmente para web scraping y minería de datos.


BeautifulSoup

BeautifulSoup es una librería de Python para extraer datos de páginas web, especialmente adecuada para analizar archivos HTML y XML.

BeautifulSoup puede extraer y manipular el contenido de las páginas web proporcionando una API simple, siendo muy adecuado para tareas de web scraping y extracción de datos.

Instalar BeautifulSoup

Para usar BeautifulSoup, es necesario instalar beautifulsoup4 y lxml o html.parser (un analizador de HTML).

Podemos usar pip para instalar estas dependencias:

pip install beautifulsoup4
pip install lxml  # 推荐使用 lxml 作为解析器(速度更快)

Si no tienes lxml, puedes usar el html.parser integrado de Python como analizador.

Uso básico

BeautifulSoup se utiliza para analizar datos HTML o XML, y proporciona varios métodos para navegar, buscar y modificar el árbol de análisis.

Las operaciones comunes de BeautifulSoup incluyen buscar etiquetas, obtener atributos de etiquetas, extraer texto, etc.

Para usar BeautifulSoup, primero debes importar BeautifulSoup y cargar la página HTML en un objeto BeautifulSoup.

Normalmente, primero usarás una librería de web scraping (comorequests) para obtener el contenido de la página web:

Ejemplo

from bs4 import BeautifulSoup
import requests

# Usar requests para obtener el contenido de la página web
url = 'https://cn.bing.com/' # Obtener el contenido de la página web del motor de búsqueda Bing
response = requests.get(url)

# Usar BeautifulSoup para analizar la página web
soup = BeautifulSoup(response.text, 'lxml')  # Usar el analizador lxml
# Analizar el contenido de la página web con el analizador html.parser
# soup = BeautifulSoup(response.text, 'html.parser')

Obtener el título de la página web:

Ejemplo

from bs4 import BeautifulSoup
import requests

# Especifica el sitio web del que deseas obtener el título
url = 'https://cn.bing.com/' # Obtener el contenido de la página web del motor de búsqueda Bing

# Enviar una solicitud HTTP para obtener el contenido de la página web
response = requests.get(url)
# Problema de caracteres chinos ilegibles
response.encoding = 'utf-8'
# Asegurarse de que la solicitud sea exitosa
if response.status_code == 200:
    # Usar BeautifulSoup para analizar el contenido de la página web
    soup = BeautifulSoup(response.text, 'lxml')
   
    # Buscar la etiqueta <title>
    title_tag = soup.find('title')
   
    # Imprimir el texto del título
    if title_tag:
        print(title_tag.get_text())
    else:
        print("No se encontró la etiqueta <title>")
else:
    print("Solicitud fallida, código de estado:", response.status_code)

Al ejecutar el código anterior, el título de salida es:

搜索 - Microsoft 必应

Problema de caracteres chinos ilegibles

Al usar la librería requests para obtener páginas web en chino, es posible encontrar problemas de codificación que impiden que el contenido chino se muestre correctamente. Para garantizar que las páginas web chinas se obtengan y se muestren correctamente, normalmente es necesario gestionar la codificación de caracteres de la página web.

Detección automática de codificación: requests normalmente infiere la codificación de la página web según el Content-Type en los encabezados de respuesta, pero a veces puede ser inexacto; en ese caso, se puede usar chardet para detectar la codificación automáticamente.

Ejemplo

import requests

url = 'https://cn.bing.com/'
response = requests.get(url)

# Usar chardet para detectar la codificación automáticamente
import chardet
encoding = chardet.detect(response.content)['encoding']
print(encoding)
response.encoding = encoding

Al ejecutar el código anterior, la salida es:

utf-8

Si conoces la codificación de la página web (por ejemplo, utf-8 o gbk), puedes configurarla directamente.response.encoding:

response.encoding = 'utf-8'  # 或者 'gbk',根据实际情况选择

Buscar etiquetas

BeautifulSoup proporciona varios métodos para buscar etiquetas en la página web; los más comunes incluyenfind()yfind_all()。

  • find()Devuelve la primera etiqueta que coincida
  • find_all()Devuelve todas las etiquetas que coincidan

Ejemplo

from bs4 import BeautifulSoup
import requests

# Especifica el sitio web del que deseas obtener el título
url = 'https://www.baidu.com/' # Obtener el contenido de la página web del motor de búsqueda Bing

# Enviar una solicitud HTTP para obtener el contenido de la página web
response = requests.get(url)
# Problema de caracteres chinos ilegibles
response.encoding = 'utf-8'

soup = BeautifulSoup(response.text, 'lxml')

# Buscar la primera etiqueta <a>
first_link = soup.find('a')
print(first_link)
print("----------------------------")

# Obtener el atributo href de la primera etiqueta <a>
first_link_url = first_link.get('href')
print(first_link_url)
print("----------------------------")

# Buscar todas las etiquetas <a>
all_links = soup.find_all('a')
print(all_links)

El resultado de salida es similar a:

<a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>
----------------------------
http://news.baidu.com
----------------------------
[<a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>, <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123</a>, <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图</a>, <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频</a>,

Obtener el texto de una etiqueta

Puedes, medianteget_text()los atributos, acceder a las etiquetas padre e hija de una etiqueta:

Ejemplo

from bs4 import BeautifulSoup
import requests

# Especifica el sitio web del que deseas obtener el título
url = 'https://www.baidu.com/' # Obtener el contenido de la página web del motor de búsqueda Bing

# Enviar una solicitud HTTP para obtener el contenido de la página web
response = requests.get(url)
# Problema de caracteres chinos ilegibles
response.encoding = 'utf-8'

soup = BeautifulSoup(response.text, 'lxml')

# Obtener el contenido de texto de la primera etiqueta <p>
paragraph_text = soup.find('p').get_text()

# Obtener todo el contenido de texto de la página
all_text = soup.get_text()
print(all_text)

El resultado de salida es similar a:

Busca en Baidu y lo sabrás.

Buscar etiquetas hijas y padres

Puedes, medianteparentychildrenlos atributos, acceder a las etiquetas padre e hija:
# 获取当前标签的父标签
parent_tag = first_link.parent

# 获取当前标签的所有子标签
children = first_link.children

Ejemplo

from bs4 import BeautifulSoup
import requests

# Especifica el sitio web del que deseas obtener el título
url = 'https://www.baidu.com/' # Obtener el contenido de la página web del motor de búsqueda Bing

# Enviar una solicitud HTTP para obtener el contenido de la página web
response = requests.get(url)
# Problema de caracteres chinos ilegibles
response.encoding = 'utf-8'

soup = BeautifulSoup(response.text, 'lxml')

# Buscar la primera etiqueta <a>
first_link = soup.find('a')
print(first_link)
print("----------------------------")

# Obtener la etiqueta padre de la etiqueta actual
parent_tag = first_link.parent
print(parent_tag.get_text())

El resultado de salida es similar a:

<a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>
----------------------------
 新闻 hao123 地图 视频 贴吧  登录   更多产品 

Buscar etiquetas con atributos específicos

Puedes buscar etiquetas con atributos específicos pasando atributos.

Por ejemplo, para buscar todas las etiquetas div con la clase example-class:

# 查找所有 class="example-class" 的 <div> 标签
divs_with_class = soup.find_all('div', class_='example-class')

# 查找具有 id="unique-id" 的 <p> 标签
unique_paragraph = soup.find('p', id='unique-id')

Obtener el botón de búsqueda con id su:

Ejemplo

from bs4 import BeautifulSoup
import requests

# Especifica el sitio web del que deseas obtener el título
url = 'https://www.baidu.com/' # Obtener el contenido de la página web del motor de búsqueda Bing

# Enviar una solicitud HTTP para obtener el contenido de la página web
response = requests.get(url)
# Problema de caracteres chinos ilegibles
response.encoding = 'utf-8'

soup = BeautifulSoup(response.text, 'lxml')

# Buscar la etiqueta <input> con id="unique-id"
unique_input = soup.find('input', id='su')

input_value = unique_input['value'] # Obtener el valor del campo de entrada input

print(input_value)

El resultado de salida es:

Buscar en Baidu.

Uso avanzado

Selectores CSS

BeautifulSoup también admite la búsqueda de etiquetas mediante selectores CSS.

El método select() permite buscar etiquetas usando una sintaxis de selector similar a jQuery:

# 使用 CSS 选择器查找所有 class 为 'example' 的 <div> 标签
example_divs = soup.select('div.example')

# 查找所有 <a> 标签中的 href 属性
links = soup.select('a[href]')

Manejo de etiquetas anidadas

BeautifulSoup admite estructuras HTML profundamente anidadas; puedes manejar estas estructuras buscando etiquetas hijas de forma recursiva:

# 查找嵌套的 <div> 标签
nested_divs = soup.find_all('div', class_='nested')
for div in nested_divs:
    print(div.get_text())

Modificar el contenido de la página web

BeautifulSoup te permite modificar el contenido HTML.

Podemos modificar los atributos de las etiquetas, el texto o eliminar etiquetas:

Ejemplo

# Modificar el atributo href de la primera etiqueta <a>
first_link['href'] = 'http://new-url.com'

# Modificar el contenido de texto de la primera etiqueta <p>
first_paragraph = soup.find('p')
first_paragraph.string = 'Updated content'

# Eliminar una etiqueta
first_paragraph.decompose()

Convertir a cadena

Puedes convertir el objeto BeautifulSoup analizado de vuelta a una cadena HTML:

# 转换为字符串
html_str = str(soup)

Atributos y métodos de BeautifulSoup

A continuación se presentan los atributos y métodos más utilizados en BeautifulSoup:

Métodos/AtributosDescripciónEjemplo
BeautifulSoup()Se utiliza para analizar documentos HTML o XML y devolver un objeto BeautifulSoup.soup = BeautifulSoup(html_doc, 'html.parser')
.prettify()Formatea y embellece el contenido del documento, generando una cadena estructurada.print(soup.prettify())
.find()Buscar la primera etiqueta que coincida.tag = soup.find('a')
.find_all()Buscar todas las etiquetas que coincidan y devolver una lista.tags = soup.find_all('a')
.find_all_next()Buscar todas las etiquetas que cumplan las condiciones después de la etiqueta actual.tags = soup.find('div').find_all_next('p')
.find_all_previous()Buscar todas las etiquetas que cumplan las condiciones antes de la etiqueta actual.tags = soup.find('div').find_all_previous('p')
.find_parent()Devolver la etiqueta padre de la etiqueta actual.parent = tag.find_parent()
.find_all_parents()Buscar todas las etiquetas padre de la etiqueta actual.parents = tag.find_all_parents()
.find_next_sibling()Buscar la siguiente etiqueta hermana de la etiqueta actual.next_sibling = tag.find_next_sibling()
.find_previous_sibling()Buscar la etiqueta hermana anterior de la etiqueta actual.prev_sibling = tag.find_previous_sibling()
.parentObtener la etiqueta padre de la etiqueta actual.parent = tag.parent
.next_siblingObtener la siguiente etiqueta hermana de la etiqueta actual.next_sibling = tag.next_sibling
.previous_siblingObtener la etiqueta hermana anterior de la etiqueta actual.prev_sibling = tag.previous_sibling
.get_text()Extraer el contenido de texto dentro de la etiqueta, ignorando todas las etiquetas HTML.text = tag.get_text()
.attrsDevolver todos los atributos de la etiqueta, representados como un diccionario.href = tag.attrs['href']
.stringObtener el contenido de cadena dentro de la etiqueta.string_content = tag.string
.nameDevolver el nombre de la etiqueta.tag_name = tag.name
.contentsDevolver todos los elementos hijos de la etiqueta, en forma de lista.children = tag.contents
.descendantsDevolver todos los elementos descendientes de la etiqueta, en forma de generador.for child in tag.descendants: print(child)
.parentObtener la etiqueta padre de la etiqueta actual.parent = tag.parent
.previous_elementObtener el elemento anterior de la etiqueta actual (sin incluir texto).prev_elem = tag.previous_element
.next_elementObtener el elemento siguiente de la etiqueta actual (sin incluir texto).next_elem = tag.next_element
.decompose()Eliminar la etiqueta actual y su contenido del árbol.tag.decompose()
.unwrap()Quitar la etiqueta en sí, conservando solo su contenido hijo.tag.unwrap()
.insert()Insertar una nueva etiqueta o texto dentro de la etiqueta.tag.insert(0, new_tag)
.insert_before()Insertar una nueva etiqueta antes de la etiqueta actual.tag.insert_before(new_tag)
.insert_after()Insertar una nueva etiqueta después de la etiqueta actual.tag.insert_after(new_tag)
.extract()Eliminar la etiqueta y devolverla.extracted_tag = tag.extract()
.replace_with()Reemplazar la etiqueta actual y su contenido.tag.replace_with(new_tag)
.has_attr()Verificar si la etiqueta tiene el atributo especificado.if tag.has_attr('href'):
.get()Obtener el valor del atributo especificado.href = tag.get('href')
.clear()Vaciar todo el contenido de la etiqueta.tag.clear()
.encode()Codificar el contenido de la etiqueta como flujo de bytes.encoded = tag.encode()
.is_empty_elementVerificar si la etiqueta es un elemento vacío (por ejemplo,<br>、<img>etc.).if tag.is_empty_element:
.is_ancestor_of()Verificar si la etiqueta actual es un elemento ancestro de la etiqueta especificada.if tag.is_ancestor_of(another_tag):
.is_descendant_of()Verificar si la etiqueta actual es un elemento descendiente de la etiqueta especificada.if tag.is_descendant_of(another_tag):

Otros atributos

Métodos/AtributosDescripciónEjemplo
.styleObtener el estilo en línea de la etiqueta.style = tag['style']
.idObtener de la etiquetaidel atributo.id = tag['id']
.class_Obtener de la etiquetaclassel atributo.class_name = tag['class']
.stringObtener el contenido de cadena dentro de la etiqueta, ignorando otras etiquetas.content = tag.string
.parentObtener el elemento padre de la etiqueta.parent = tag.parent

Otros

Métodos/AtributosDescripciónEjemplo
find_all(string)Buscar etiquetas coincidentes usando una cadena.tag = soup.find_all('div', class_='container')
find_all(id)Buscar especificadaidde la etiqueta.tag = soup.find_all(id='main')
find_all(attrs)Buscar etiquetas con el atributo especificado.tag = soup.find_all(attrs={"href": "http://example.com"})
Otras extensiones