El web scraping es el proceso de extraer datos de sitios web automáticamente. Es una técnica común que se utiliza para recopilar grandes cantidades de datos para análisis, aprendizaje automático y más. En Python, hay muchas bibliotecas excelentes que facilitan el web scraping. Una opción popular es HTTPX.
HTTPX es un cliente HTTP potente y moderno para Python. Fue creado por los desarrolladores detrás de Requests y se inspira en gran medida en Requests, al tiempo que agrega nuevas funciones como compatibilidad con HTTP/2.
En esta guía completa, exploraremos cómo raspar sitios web de manera efectiva en Python usando HTTPX.
Comenzando con HTTPX
Para comenzar, HTTPX se puede instalar mediante pip:
pip install httpx
Alternativamente, se puede utilizar Poesía:
poetry add httpx
Una vez instalado, HTTPX se puede importar y utilizar:
import httpx
response = httpx.get(‘https://example.com‘)
print(response.text)
Esto realizará una solicitud GET a example.com e imprimirá el HTML de la página de inicio.
HTTPX tiene una API simple y admite todos los verbos HTTP populares como GET, POST, PUT, DELETE, HEAD, OPTIONS, etc.
Algunas características clave incluyen:
- Soporte HTTP/1.1 y HTTP/2
- API asincrónica de estilo aiohttp
- Agrupación de conexiones y keepalive
- Soporte Proxy
- Configuración de tiempo de espera
- Persistencia de cookies
- API de estilo de solicitudes familiar
A continuación, veamos algunos patrones de uso comunes.
Realizar solicitudes con HTTPX
Para realizar una solicitud GET, el httpx.get()
se puede utilizar el método:
response = httpx.get(‘https://example.com‘)
De manera similar, los httpx.post()
, httpx.put()
, httpx.delete()
, etc. se pueden utilizar para otros verbos HTTP.
Los parámetros como encabezados, cookies, tiempo de espera, etc. se pueden pasar como argumentos de palabras clave:
response = httpx.get(
‘https://httpbin.org/headers‘,
headers = {
‘User-Agent‘: ‘MyBot 1.0‘
},
timeout = 10.0
)
La respuesta contiene propiedades como status_code
, headers
, text
, json()
, Etc.:
print(response.status_code)
print(response.headers)
print(response.text)
json = response.json()
También puede transmitir la respuesta de forma incremental iterando sobre el objeto de respuesta.
Usando un cliente HTTPX
Para la mayoría de las tareas de scraping, se recomienda utilizar un persistente httpx.Client
ejemplo.
El cliente maneja cosas como agrupación de conexiones, sesiones, cookies, etc. en múltiples solicitudes.
import httpx
client = httpx.Client()
response = client.get(‘https://example.com‘)
print(response.text)
response = client.get(‘https://httpbin.org/cookies/set?name=foo‘)
print(response.text)
response = client.get(‘https://httpbin.org/cookies‘)
print(response.json())
Aquí realizamos múltiples solicitudes utilizando el mismo cliente, que maneja la persistencia de las cookies automáticamente.
También puedes configurar opciones como encabezados, proxies, autenticación, etc. al crear el cliente:
client = httpx.Client(
headers = {
‘User-Agent‘: ‘MyBot 1.0‘,
‘Authorization‘: ‘Bearer xxx‘
},
proxies = ‘http://192.168.1.1:8181/‘,
auth = (‘username‘, ‘password‘)
)
Ahora veamos cómo realizar solicitudes de forma asincrónica.
Solicitudes asincrónicas con HTTPX
Para realizar solicitudes de forma asincrónica en Python, HTTPX proporciona una AsyncClient
:
import httpx
async with httpx.AsyncClient() as client:
response = await client.get(‘https://example.com‘)
Utilizamos async with
para inicializar el cliente, await
en la solicitud y cerrar automáticamente el cliente después.
Para extraer varias URL al mismo tiempo, podemos usar asyncio.gather()
:
import httpx
import asyncio
async def get_url(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
urls = [‘https://example.com‘, ‘https://httpbin.org‘, ‘https://python.org‘]
async def main():
responses = await asyncio.gather(*[get_url(url) for url in urls])
print(responses)
asyncio.run(main())
asyncio.gather()
espera simultáneamente múltiples corrutinas y devuelve resultados en el orden de los awaitables.
También hay otras opciones como asyncio.as_completed()
para procesarlos a medida que se completan:
tasks = [get_url(url) for url in urls]
async def main():
for result in asyncio.as_completed(tasks):
print(await result)
Async IO permite recuperar varias páginas a la vez, lo que resulta útil para acelerar el scraping.
A continuación, veamos cómo extraer datos de respuestas HTML y JSON.
Eliminación de respuestas HTML y JSON
Para el raspado de HTML, podemos usar un analizador como Beautiful Soup para extraer datos:
from bs4 import BeautifulSoup
response = httpx.get(‘https://en.wikipedia.org/wiki/Python_(programming_language)‘)
soup = BeautifulSoup(response.text, ‘html.parser‘)
for link in soup.select(‘.toctext‘):
print(link.text.strip())
Esto imprime el contenido de la tabla de contenidos de la página de Wikipedia.
Para respuestas JSON, HTTPX proporciona una función incorporada .json()
método:
response = httpx.get(‘https://api.github.com/repos/encode/httpx‘)
json = response.json()
print(json[‘description‘])
El json=
El parámetro también se puede utilizar para serializar datos JSON en solicitudes.
Junto con un analizador, podemos crear raspadores para extraer datos de API y sitios web.
Manejo de problemas y errores
Durante el scraping, a menudo surgen problemas como errores de conexión, tiempos de espera, límites de velocidad, etc.
HTTPX proporciona excepciones y herramientas para manejarlas adecuadamente.
Tiempos de espera
Para manejar respuestas lentas, puede especificar un personalizado timeout
parámetro. El valor predeterminado es 5 segundos.
response = httpx.get(‘https://example.com‘, timeout=10.0)
Si esto se excede, un httpx.TimeoutException
ocurre:
try:
response = httpx.get(‘https://example.com‘, timeout=0.001)
except httpx.TimeoutException:
print(‘Request timed out‘)
Es posible que se necesiten tiempos de espera más largos para ciertos sitios o páginas.
Errores HTTP
Para errores HTTP como 400, 500, etc., el response.raise_for_status()
se puede utilizar el método:
try:
response = httpx.get(‘https://httpbin.org/status/500‘)
response.raise_for_status()
except httpx.HTTPStatusError:
print(‘HTTP Error occurred‘)
Esto generará una excepción en cualquier código de estado 4xx o 5xx.
Reintentar solicitudes
Para agregar lógica de reintento, paquetes externos como tenacity
puede ser usado:
from tenacity import retry, stop_after_attempt
@retry(stop=stop_after_attempt(3))
def make_request():
response = httpx.get(‘https://example.com‘)
response.raise_for_status()
return response.json()
data = make_request()
Aquí reintentamos hasta 3 veces en cualquier excepción. También se puede definir una lógica de reintento más avanzada.
Límites de solicitudes paralelas
Al realizar muchas solicitudes en paralelo, es posible que encuentre límites de conexión.
El limits
El parámetro se puede utilizar para configurar opciones como conexiones máximas:
client = httpx.AsyncClient(
limits = httpx.Limits(max_connections=20)
)
Ajustar este parámetro en función del sitio de destino puede ayudar a evitar límites.
Al abordar estos problemas comunes, se pueden construir raspadores más resistentes.
Mejores prácticas de scraping
A continuación se ofrecen algunos consejos para crear raspadores web eficaces con HTTPX:
Utilice un cliente HTTPX – Los clientes proporcionan agrupación de conexiones, persistencia de cookies y otros beneficios.
Raspe cortésmente – Limite las tasas de solicitudes para evitar saturar los servidores. Utilice retrasos y limitaciones aleatorios.
Manejar errores – Utilice bloques try/except, comprobaciones de estado y reintentos para solucionar problemas.
Usar E/S asíncrona – Raspe las páginas al mismo tiempo para mejorar la velocidad. Pero limite la concurrencia para evitar prohibiciones.
Aleatorizar agentes de usuario – Gire cadenas aleatorias de agentes de usuario para que parezcan más humanos.
Usar servidores proxy – Rotar diferentes proxies/IP para distribuir solicitudes.
Caché y datos persistentes – Guarde los datos extraídos en archivos/bases de datos para evitar volver a extraerlos.
Siguiendo mejores prácticas como estas, se pueden construir raspadores más robustos y fáciles de mantener.
Técnicas avanzadas de raspado
Veamos algunas capacidades de raspado más avanzadas de HTTPX.
Raspado de páginas de autenticación
Para extraer páginas autenticadas, HTTPX admite múltiples tipos de autenticación, como autenticación básica, implícita y de portador:
client = httpx.Client(
auth = (‘username‘, ‘password‘)
)
response = client.get(‘https://api.example.com/users/me‘)
Las credenciales de autenticación se conservan automáticamente en todas las solicitudes.
Manejo de cookies
El cookies
El parámetro se puede utilizar para enviar cookies personalizadas:
client = httpx.Client(
cookies = {
‘sessionId‘: ‘xxxx‘
}
)
response = client.get(‘https://example.com/dashboard‘)
Y las cookies configuradas por el servidor persisten automáticamente en el cliente.
Respuestas de transmisión
Para respuestas grandes, puede transmitirlas de forma incremental iterando el objeto de respuesta:
response = client.get(‘https://example.com/bigfile‘)
for chunk in response:
process(chunk)
Esto evita tener que cargar la respuesta completa en la memoria.
Soporte de proxy
Para enrutar solicitudes a través de un servidor proxy, el proxies
Se puede utilizar el parámetro:
proxy = ‘http://192.168.0.1:8888‘
client = httpx.Client(proxies = {‘http‘: proxy, ‘https‘: proxy})
Rotar diferentes servidores proxy ayuda a distribuir solicitudes de diferentes IP.
Encabezados personalizados
Puede falsificar o aleatorizar encabezados de solicitud como agentes de usuario:
client = httpx.Client(headers = {
‘User-Agent‘: ‘MyBot 1.0‘
})
Esto imita los encabezados de un navegador real.
A través de estas funciones más avanzadas, se pueden crear raspadores robustos utilizando HTTPX y Python.
Rascadores de ejemplo
Veamos ahora algunos ejemplos de scrapers creados con HTTPX.
Raspador de API de Reddit
Aquí hay un raspador básico para la API de Reddit:
import httpx
client = httpx.Client()
subreddit = ‘python‘
listing = ‘hot‘
limit = 10
response = client.get(f‘https://www.reddit.com/r/{subreddit}/{listing}.json?limit={limit}‘)
data = response.json()[‘data‘]
for post in data[‘children‘]:
title = post[‘data‘][‘title‘]
score = post[‘data‘][‘score‘]
print(f"{title} (Score: {score})")
Esto recupera datos sobre las publicaciones principales del subreddit de Python. La API devuelve JSON que podemos analizar.
Podríamos ampliar este raspador para extraer datos de múltiples subreddits, almacenar resultados en una base de datos, etc.
Raspador de artículos de noticias
Aquí hay un raspador de noticias simple que extrae artículos de un sitio:
from bs4 import BeautifulSoup
import httpx
client = httpx.Client()
response = client.get("https://example.com/news")
soup = BeautifulSoup(response.text, ‘html.parser‘)
for article in soup.select(‘.article‘):
title = article.select_one(‘.article-title‘).text
content = article.select_one(‘.article-content‘).text
print(title)
print(content)
print()
Esto encuentra todo .article
elementos, extrae los campos de título y contenido e imprime los artículos.
Nuevamente, esto podría ampliarse para eliminar campos adicionales, analizar fechas, almacenar en una base de datos, etc.
Raspador de resultados de búsqueda
Y aquí hay un ejemplo de raspador para los resultados de búsqueda de Google:
import httpx
query = "httpx python"
url = f"https://www.google.com/search?q={query}"
client = httpx.Client()
response = client.get(url)
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, ‘html.parser‘)
for result in soup.select(‘.tF2Cxc‘):
title = result.select_one(‘.DKV0Md‘).text
link = result.select_one(‘.yuRUbf a‘)[‘href‘]
print(title)
print(link)
print()
Esto busca en Google una consulta determinada, analiza los enlaces/títulos de los resultados y los imprime.
Nuevamente, se podrían realizar muchas mejoras, como extraer recuentos de resultados de búsqueda, campos adicionales, raspar páginas de resultados, detectar captchas, etc.
Estos ejemplos demuestran patrones de raspado comunes con HTTPX. Se pueden aplicar las mismas técnicas para crear raspadores para muchos sitios web y API.
Resumen
En resumen, HTTPX proporciona un potente cliente HTTP para crear raspadores web de Python. Aquí hay algunos puntos clave:
HTTPX tiene una API simple de estilo solicitudes para realizar solicitudes.
El soporte asíncrono permite realizar solicitudes al mismo tiempo.
Manejo sólido de errores con tiempos de espera, reintentos y comprobaciones de estado.
Extraiga páginas HTML con Beautiful Soup y las API JSON fácilmente.
Los clientes persistentes proporcionan agrupación de conexiones, sesiones y manejo de cookies.
Técnicas avanzadas como proxies, encabezados y autenticación permiten raspadores sofisticados.
Siga las mejores prácticas, como el uso de limitaciones, retrasos aleatorios y agentes de usuario.
HTTPX facilita el inicio del scraping con Python. Con un manejo sólido de errores y concurrencia asincrónica, se pueden desarrollar raspadores escalables.
¡Pruebe HTTPX en su próximo proyecto de web scraping de Python!