Web scraping básico con Python: requests y BeautifulSoup



El web scraping permite extraer datos de sitios web de forma automática. Como sysadmin lo usarás para monitorizar páginas de estado, extraer información de paneles sin API, o automatizar la descarga de reportes. Con requests y BeautifulSoup puedes hacerlo en pocas líneas.

Instalación

pip install requests beautifulsoup4 lxml --break-system-packages

Hacer peticiones HTTP con requests

import requests

response = requests.get("https://httpbin.org/get")
print(response.status_code)     # 200
print(response.headers)
print(response.text)            # HTML o texto
print(response.json())          # si la respuesta es JSON


headers = {
    "User-Agent": "Mozilla/5.0 (compatible; MiBot/1.0)",
    "Accept-Language": "es-ES,es;q=0.9"
}
response = requests.get("https://ejemplo.com", headers=headers, timeout=10)
response.raise_for_status()     # lanza excepción si el status no es 2xx


session = requests.Session()
session.headers.update(headers)


login_data = {"username": "admin", "password": "secreto"}
session.post("https://panel.ejemplo.com/login", data=login_data)


response = session.get("https://panel.ejemplo.com/dashboard")

Parsear HTML con BeautifulSoup

from bs4 import BeautifulSoup
import requests

html = requests.get("https://news.ycombinator.com").text
soup = BeautifulSoup(html, "lxml")


titulos = soup.find_all("span", class_="titleline")
for t in titulos[:5]:
    enlace = t.find("a")
    print(enlace.text, "→", enlace.get("href"))

contenido = soup.find(id="hnmain")

filas = soup.select("tr.athing")
for fila in filas[:3]:
    print(fila.select_one(".titleline a").text)


imagenes = soup.find_all("img")
urls = [img.get("src") for img in imagenes if img.get("src")]


primer_parrafo = soup.find("p")
siguiente = primer_parrafo.find_next_sibling("p")
padre = primer_parrafo.parent

Ejemplo real: monitorizar el estado de un servicio web

import requests
from bs4 import BeautifulSoup
import smtplib
from email.mime.text import MIMEText
from datetime import datetime

SERVICIOS = [
    ("GitHub", "https://www.githubstatus.com"),
    ("Cloudflare", "https://www.cloudflarestatus.com"),
]

def verificar_estado(nombre, url):
    try:
        r = requests.get(url, timeout=10)
        soup = BeautifulSoup(r.text, "lxml")

        # Buscar el indicador de estado (varía por página)
        estado = soup.select_one(".page-status .status")
        if estado:
            return estado.text.strip()
        return "Desconocido"
    except requests.RequestException as e:
        return f"Error: {e}"

for nombre, url in SERVICIOS:
    estado = verificar_estado(nombre, url)
    simbolo = "✓" if "operational" in estado.lower() else "⚠"
    print(f"{simbolo} {nombre}: {estado}")

Ejemplo real: extraer tabla de datos

import requests
from bs4 import BeautifulSoup
import csv


def scrape_tabla(url, tabla_id=None, clase=None):
    response = requests.get(url, timeout=10)
    soup = BeautifulSoup(response.text, "lxml")

    if tabla_id:
        tabla = soup.find("table", id=tabla_id)
    elif clase:
        tabla = soup.find("table", class_=clase)
    else:
        tabla = soup.find("table")

    if not tabla:
        return []

    filas = []
    for tr in tabla.find_all("tr"):
        celdas = [td.get_text(strip=True) for td in tr.find_all(["th", "td"])]
        if celdas:
            filas.append(celdas)

    return filas

datos = scrape_tabla("https://intranet.miempresa.local/servidores")
with open("servidores.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(datos)

Manejar JavaScript (Selenium para webs dinámicas)

BeautifulSoup solo ve el HTML estático. Para webs con contenido generado por JavaScript necesitas Selenium o Playwright:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

options = webdriver.ChromeOptions()
options.add_argument("--headless")    # sin ventana gráfica
options.add_argument("--no-sandbox")

driver = webdriver.Chrome(options=options)
driver.get("https://panel.ejemplo.com/login")


WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "username")))

driver.find_element(By.ID, "username").send_keys("admin")
driver.find_element(By.ID, "password").send_keys("secreto")
driver.find_element(By.ID, "submit").click()


WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "dashboard")))
datos = driver.find_element(By.CLASS_NAME, "dashboard").text
driver.quit()

Buenas prácticas y ética

  • Respeta el archivo robots.txt del sitio.
  • Añade un delay entre peticiones para no sobrecargar el servidor (time.sleep(1)).
  • Identifícate con un User-Agent que incluya tu información de contacto.
  • Verifica si el sitio ofrece API antes de hacer scraping; siempre es preferible.
  • Nunca hagas scraping de datos personales sin permiso explícito.

Conclusión

Requests + BeautifulSoup es el dúo perfecto para scraping de HTML estático. Para la mayoría de necesidades de monitorización y extracción de datos internos, estas dos bibliotecas son más que suficientes. Cuando el sitio usa JavaScript, Playwright es la alternativa moderna a Selenium con una API más limpia.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *