Cómo hacer web scraping con Python
El web scraping es el proceso de extraer información de sitios web de forma automatizada mediante scripts. Esta técnica es popular en sectores como:
- E-commerce: monitoreo de precios y análisis de la competencia.
- Ciencia de datos: recolección masiva de datos para machine learning.
- Periodismo de datos: investigaciones automatizadas.
- Investigación académica: obtención de datos de fuentes públicas.
Gracias al web scraping, se pueden recopilar datos de múltiples sitios web de manera eficiente, algo inviable manualmente. Python es uno de los lenguajes más utilizados para esta tarea, gracias a sus bibliotecas como BeautifulSoup y Requests, que permiten descargar y procesar contenido web de forma programática, extrayendo información como texto, enlaces e imágenes.
Es importante tener en cuenta los aspectos legales y éticos del scraping, ya que algunos sitios prohíben esta práctica mediante sus términos de servicio, y existen leyes de privacidad en muchos países.
En el e-commerce, las empresas utilizan scraping para ajustar precios en tiempo real al analizar los de sus competidores. En ciencia de datos, facilita la creación de algoritmos y análisis predictivo. En el periodismo de datos, los periodistas extraen grandes volúmenes de información de bases públicas para descubrir patrones. En la investigación académica, los investigadores recopilan datos de sitios científicos y académicos para sus estudios.
Esta guía te enseñará a realizar web scraping con Python usando BeautifulSoup y Requests, abarcando desde la configuración hasta la extracción y almacenamiento de datos, con técnicas avanzadas para manejar errores y evitar bloqueos.
Paso 1: Preparar el entorno de trabajo
Antes de empezar con el web scraping, es básico preparar el entorno de trabajo e instalar las herramientas necesarias. Tener el entorno correctamente configurado evitará problemas técnicos futuros.
Asegúrate de usar una versión de Python compatible, como Python 3.6 o posterior, ya que la mayoría de las bibliotecas de scraping, como BeautifulSoup, requieren versiones recientes. Si utilizas versiones antiguas de Python, algunas funcionalidades pueden no estar disponibles.
Además de BeautifulSoup, puedes considerar otras bibliotecas como lxml, que procesa HTML más rápido, o Scrapy, que es más avanzada y adecuada para proyectos de scraping a gran escala, como la recolección de datos en múltiples páginas.
Instalación de Python y dependencias necesarias
Si aún no tienes Python instalado, puedes descargarlo desde el sitio oficial. Asegúrate de que Python esté correctamente instalado ejecutando el comando adecuado en tu terminal o línea de comandos.
python --version
Esto verificará la versión de Python instalada en tu sistema. Si no aparece una versión de Python, asegúrate de que esté correctamente instalado o en el PATH del sistema.
Instalación de BeautifulSoup y Requests
BeautifulSoup y Requests son dos de las bibliotecas más utilizadas para hacer web scraping con Python. Requests se utiliza para realizar solicitudes HTTP, mientras que BeautifulSoup facilita la extracción y el análisis del contenido HTML.
Para instalarlas, ejecuta el comando correspondiente en tu terminal.
pip install beautifulsoup4 requests
Paso 2: Hacer una solicitud HTTP a una página web
En web scraping, el método GET es el más común, ya que permite recuperar datos de una página web sin modificarlos. En algunos casos, podrías necesitar POST para enviar datos al servidor, como en formularios o cuando un sitio requiere autenticación.
También es importante manejar respuestas del servidor como redirecciones (códigos 3xx). Requests te permite seguir redirecciones automáticamente con el parámetro allow_redirects=True
.
El primer paso es obtener el contenido HTML de la página, y con Requests, puedes enviar solicitudes HTTP de manera eficiente.
Ejemplo básico de solicitud HTTP
Este ejemplo muestra cómo hacer una solicitud HTTP a una página web usando Requests en Python. La solicitud obtiene el contenido HTML de la página, que luego podrás analizar para extraer los datos.
Asegúrate de que la URL sea válida y que el servidor permita este tipo de solicitudes.
import requests
url = 'https://ejemplo.com'
response = requests.get(url)
html = response.text
Verificar el estado de la solicitud
Es esencial verificar si la solicitud fue exitosa para asegurar que el servidor respondió correctamente. Un código 200 indica éxito, pero si obtienes códigos como 404 (no encontrada) o 403 (prohibido), debes gestionarlos adecuadamente para evitar fallos, modificando la URL o los encabezados de la solicitud si es necesario.
A continuación, se muestra cómo verificar el estado de la solicitud y manejar posibles errores:
if response.status_code == 200:
print("La solicitud fue exitosa")
else:
print("Error en la solicitud:", response.status_code)
Si obtienes un 404, probablemente la URL está mal escrita o la página ya no está disponible. En el caso de un 403, el acceso está restringido, lo que podría requerir el uso de un User-Agent diferente o incluso la implementación de proxies para evitar bloqueos. Además, otros códigos como 500 (error interno del servidor) indican problemas temporales en el servidor que también pueden requerir reintentos o ajustes en las solicitudes.
Qué hacer si la solicitud falla
Si obtienes un error como 404 (página no encontrada) o 403 (acceso denegado), revisa que la URL sea correcta y que el contenido esté disponible. Un 403 puede ocurrir si el servidor detecta un script automatizado. Para evitar esto, puedes modificar los headers, especialmente el User-Agent, para que la solicitud parezca provenir de un navegador real.
Aquí te mostramos cómo incluir un User-Agent que simule el acceso desde un navegador:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=headers)
Además del User-Agent, algunos sitios pueden requerir otros encabezados como Referer o Cookies para permitir el acceso. Si persisten los problemas, revisa el archivo robots.txt del sitio o utiliza técnicas como la rotación de proxies o la limitación de velocidad entre solicitudes para reducir el riesgo de bloqueos y mejorar la confiabilidad del scraper.
Paso 3: Analizar y extraer datos con BeautifulSoup
Con el HTML de la página, puedes usar BeautifulSoup para analizar y extraer los datos que necesitas. No solo permite extraer texto, sino también acceder a atributos específicos de etiquetas HTML, como el atributo src
en las imágenes.
BeautifulSoup también facilita la navegación por el árbol DOM, permitiéndote moverte entre elementos relacionados con propiedades como .parent
, .next_sibling
o .previous_sibling
, ideal para extraer datos vinculados, como un título de producto y su precio.
Para simular una solicitud desde un navegador
Algunos servidores bloquean las solicitudes automatizadas. Para evitarlo, puedes modificar los encabezados HTTP de la solicitud, especialmente el User-Agent, para que parezca proveniente de un navegador real.
En este ejemplo, utilizamos la biblioteca BeautifulSoup para analizar el contenido HTML una vez que hemos obtenido una respuesta exitosa. Esto te permitirá extraer los datos que necesitas de la estructura HTML de la página.
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
Navegar por la estructura HTML
Una vez que hayas obtenido el contenido HTML de la página web, el siguiente paso es navegar por su estructura y localizar los elementos que te interesan. Con BeautifulSoup, puedes buscar y seleccionar elementos específicos del documento HTML, como etiquetas <div>
, <p>
, <a>
, entre muchas otras, utilizando métodos como find()
y find_all()
.
El método find()
te permite seleccionar el primer elemento que coincide con un criterio específico, mientras que find_all()
devuelve una lista de todos los elementos que coinciden con el criterio dado. Esto es muy útil cuando necesitas extraer información como títulos, párrafos o enlaces de una página web.
A continuación se muestra un ejemplo básico de cómo encontrar y extraer el primer título <h1>
de la página, así como todos los enlaces dentro de las etiquetas <a>
:
# Encontrar el primer título de la página
titulo = soup.find('h1').text
print(titulo)
# Encontrar todos los enlaces de la página
enlaces = soup.find_all('a')
for enlace in enlaces:
print(enlace.get('href'))
En este ejemplo, el primer título de la página se extrae utilizando find(‘h1’), seguido de la propiedad .text para obtener el contenido de texto de esa etiqueta. Luego, utilizamos find_all(‘a’) para obtener todos los enlaces de la página y, en un bucle, imprimimos el atributo href de cada enlace.
Estos métodos permiten extraer fácilmente la información estructurada de una página HTML. Además, si necesitas ser más específico, BeautifulSoup también admite filtros más detallados, como buscar por clases, atributos específicos o combinaciones de etiquetas, lo que facilita el acceso a los datos exactos que necesitas.
Uso de métodos como find()
y find_all()
find()
: encuentra el primer elemento que coincide con el criterio.find_all()
: encuentra todos los elementos que coinciden con el criterio especificado.
Extraer información clave
BeautifulSoup es muy útil para extraer información clave de páginas web, especialmente en sitios complejos como tiendas en línea, donde los datos están distribuidos en diferentes partes del HTML. Puedes extraer títulos, enlaces o párrafos, y usar clases específicas de los contenedores HTML para obtener selectivamente información como nombres, precios y descripciones de productos.
En el siguiente ejemplo, te mostramos cómo puedes extraer datos clave de productos, como el nombre y el precio, buscando etiquetas HTML específicas dentro de un div
con una clase particular:
productos = soup.find_all('div', class_='producto')
for producto in productos:
nombre = producto.find('h2').text
precio = producto.find('span', class_='precio').text
print(f"Producto: {nombre}, Precio: {precio}")
En este caso, estamos buscando todos los div con la clase “producto”. Una vez que los encontramos, accedemos a los elementos h2 dentro de cada div para obtener el nombre del producto, y a las etiquetas span con la clase “precio” para extraer su precio.
Este tipo de extracción es útil en escenarios donde los datos están bien estructurados y siguen un patrón claro. BeautifulSoup te permite definir criterios precisos para localizar y extraer estos elementos clave, y personalizar tu scraping según las necesidades del proyecto. A medida que tu proyecto de scraping se hace más complejo, también puedes combinar esta técnica con otras herramientas de análisis o almacenamiento de datos.
Paso 4: Manejar estructuras HTML más complejas
En páginas más complejas, debes ser más específico al seleccionar los datos. BeautifulSoup te permite extraer información de tablas HTML localizándolas con find()
y luego iterando sobre las filas y celdas con find_all('tr')
y find_all('td')
. Esto es útil en sitios de comercio electrónico, donde los productos suelen estar organizados en tablas.
También puedes extraer datos de listas <ul>
y <ol>
, comunes en blogs o directorios, para obtener enlaces a artículos, categorías o descripciones.
Extraer datos de tablas HTML
Cuando trabajas con datos que se presentan en formato de tabla, como tablas de precios, horarios o listados de productos, BeautifulSoup te permite extraer fácilmente la información contenida en dichas tablas. Las tablas HTML están estructuradas con etiquetas <table>
, <tr>
para las filas, y <td>
para las celdas de datos. El proceso de extracción implica localizar la tabla, iterar sobre las filas y, finalmente, extraer el contenido de las celdas.
En el siguiente ejemplo, te mostramos cómo localizar una tabla dentro de una página web y extraer sus datos fila por fila:
tabla = soup.find('table')
filas = tabla.find_all('tr')
for fila in filas:
celdas = fila.find_all('td')
for celda in celdas:
print(celda.text)
Aquí, find('table')
localiza la primera tabla en el documento HTML. Luego, find_all('tr')
encuentra todas las filas dentro de la tabla, y dentro de cada fila, find_all('td')
localiza todas las celdas de datos. El bucle recorre cada celda y utiliza .text
para extraer el contenido de texto dentro de las etiquetas <td>
.
Este método es útil cuando necesitas extraer y estructurar grandes volúmenes de datos tabulares, permitiéndote procesar la información de manera eficiente para su análisis posterior o almacenamiento en una base de datos.
Selección con selectores CSS
También puedes usar selectores CSS para obtener elementos más complejos, como listas de productos o tablas, en lugar de usar solo find()
y find_all()
. Los selectores CSS te permiten buscar elementos utilizando reglas de estilo similares a las que se aplican en una hoja de estilos, como seleccionar elementos por su clase, id, o incluso combinaciones más avanzadas.
Por ejemplo, si quieres seleccionar todas las imágenes dentro de un div
con una clase específica, puedes hacerlo con el siguiente código:
# Seleccionar todas las imágenes dentro de un div con clase 'galeria'
imagenes = soup.select('div.galeria img')
for img in imagenes:
print(img['src'])
El método select() permite una selección más flexible y precisa utilizando las mismas reglas que se aplicarían en un archivo CSS, lo que te da más control sobre la información que deseas extraer.
Iterar sobre elementos HTML
Para extraer múltiples elementos de una página, puedes iterar sobre los resultados de find_all()
o select()
. Esto es útil cuando trabajas con grandes volúmenes de datos, como listas de productos o enlaces, permitiéndote acceder a los atributos y contenido de cada elemento.
Este enfoque facilita la automatización y organización eficiente de los datos, aplicando las mismas operaciones a varios elementos similares en una página.
Paso 5: Evitar bloqueos o restricciones
Es importante respetar las normas del sitio web y no sobrecargar los servidores. Muchos sitios usan mecanismos de protección como CAPTCHAs o bloqueos por IP. Para evitar que tu IP sea bloqueada, puedes usar proxies rotativos que distribuyan las solicitudes desde diferentes direcciones IP. Servicios como ScraperAPI o ProxyMesh pueden ayudarte.
Otra técnica es variar los User-Agents para simular navegadores reales y evitar ser detectado como un script automatizado. También puedes añadir intervalos aleatorios entre solicitudes usando la biblioteca time para reducir la carga en los servidores y simular el comportamiento humano.
Manejo de headers y User-Agent
Muchos sitios web detectan solicitudes de scripts mediante el encabezado User-Agent, que identifica el navegador y sistema operativo. Si no especificas un User-Agent adecuado, el servidor puede bloquear tu acceso.
Para evitar bloqueos, modifica los headers de la solicitud para que simule ser de un navegador real, lo que hace que el comportamiento de tu scraper sea más difícil de detectar.
A continuación, se muestra un ejemplo de cómo añadir un User-Agent común de navegador a los headers de tu solicitud:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
response = requests.get(url, headers=headers)
Este método te permite disfrazar tu scraper para que sea menos probable que sea bloqueado por el servidor. Sin embargo, algunos sitios también analizan otros headers como Referer, Accept-Language o Cookies, por lo que en casos más complejos podrías necesitar añadir estos headers adicionales para emular mejor una solicitud de navegador real.
También es posible rotar diferentes User-Agents entre varias solicitudes para hacer que tu scraper sea aún más difícil de detectar, imitando accesos desde varios navegadores o dispositivos. Esta técnica se puede combinar con la rotación de proxies y la simulación de tiempos de respuesta variables, para evitar restricciones o bloqueos.
Respetar el archivo robots.txt
El archivo robots.txt indica qué áreas de un sitio web están permitidas o prohibidas para el acceso automatizado de scrapers. Se encuentra en la raíz del sitio (por ejemplo, https://ejemplo.com/robots.txt
) y contiene directrices para bots.
Respetar estas reglas es clave para evitar problemas legales o bloqueos. Aunque robots.txt no bloquea técnicamente el acceso, ignorarlo puede llevar a bloqueos de IP o CAPTCHAs, y en algunas jurisdicciones, el scraping contra las políticas del sitio puede ser ilegal.
Intervalos entre solicitudes
Para evitar sobrecargar el servidor y reducir el riesgo de bloqueo, es recomendable añadir un intervalo entre solicitudes. Enviar muchas solicitudes en poco tiempo puede hacer que el servidor bloquee tu IP o aplique restricciones como CAPTCHAs.
Añadir pausas entre peticiones no solo imita el comportamiento humano, sino que respeta los recursos del servidor y minimiza la probabilidad de ser bloqueado.
En Python, puedes implementar un intervalo entre las solicitudes utilizando la función time.sleep()
para introducir pausas. A continuación, un ejemplo que implementa una pausa de 2 segundos entre cada solicitud:
import time
time.sleep(2) # Pausa de 2 segundos
Es recomendable variar la duración de las pausas de manera aleatoria para hacer que el scraper sea aún más difícil de detectar. Puedes combinar esto con otras prácticas, como la rotación de proxies y User-Agents, para garantizar que tus solicitudes se realicen de manera eficiente y sin interrupciones.
Paso 6: Guardar los datos extraídos
Una vez extraídos los datos, el siguiente paso es almacenarlos para su uso posterior. Guardarlos en un archivo CSV es una opción común, pero para grandes volúmenes de datos o scraping continuo, es más eficiente usar bases de datos como MySQL, PostgreSQL o MongoDB, que permiten consultas rápidas y análisis en tiempo real.
Si estás scrapeando imágenes o archivos, puedes almacenarlos en servicios de almacenamiento en la nube como Amazon S3, lo que facilita la gestión de grandes cantidades de datos multimedia.
Almacenar los datos en archivos locales
Puedes guardar los datos en un archivo CSV para análisis posterior o utilizar otros formatos como JSON.
import csv
# Abrir archivo CSV en modo escritura
with open('datos.csv', 'w', newline='') as archivo_csv:
writer = csv.writer(archivo_csv)
# Escribir encabezados
writer.writerow(['Título', 'Enlace'])
# Escribir datos (ejemplo de un bucle)
for enlace in enlaces:
writer.writerow([titulo, enlace.get('href')])
Opciones adicionales de almacenamiento
Si necesitas almacenar grandes cantidades de datos o hacer un análisis más profundo, considera el uso de bases de datos como MySQL o MongoDB, o exportar los datos en formato JSON.
import json
datos = {'titulo': titulo, 'enlaces': [enlace.get('href') for enlace in enlaces]}
with open('datos.json', 'w') as archivo_json:
json.dump(datos, archivo_json)
Scraping dinámico con Selenium
Algunas páginas cargan contenido mediante JavaScript, por lo que obtener solo el HTML inicial no es suficiente. En estos casos, puedes usar Selenium para interactuar con la página como lo haría un navegador real.
Selenium es útil para sitios que dependen de JavaScript o generan contenido dinámico, como páginas que requieren iniciar sesión o donde el contenido se carga tras una búsqueda. Aunque es más lento que Requests o BeautifulSoup, Selenium permite simular el comportamiento de un usuario real, haciendo clic en botones, rellenando formularios y navegando por la página.
Qué es Selenium
Selenium es una herramienta que permite automatizar navegadores web. Esto significa que puedes usarla para realizar web scraping en páginas que cargan contenido dinámico, interactuar con formularios, hacer clic en botones, y más.
from selenium import webdriver
# Inicializar el controlador del navegador (ChromeDriver en este caso)
driver = webdriver.Chrome(executable_path='ruta/al/chromedriver')
# Navegar a una URL
driver.get('https://ejemplo.com')
# Obtener el HTML generado dinámicamente
html = driver.page_source
Cómo instalar y configurar Selenium
Instalar Selenium es un proceso sencillo y directo, pero además de instalar la biblioteca en Python, necesitarás configurar un controlador del navegador, ya que Selenium interactúa con navegadores reales para simular el comportamiento de un usuario.
1. Instalación de Selenium:
Puedes instalar Selenium en Python utilizando pip.
pip install selenium
2. Instalación del controlador del navegador
Selenium requiere un controlador específico para el navegador que vayas a utilizar. Los controladores más comunes son:
- ChromeDriver: para Google Chrome.
- GeckoDriver: para Mozilla Firefox.
- EdgeDriver: para Microsoft Edge.
Descarga el controlador correspondiente desde el sitio oficial del navegador y asegúrate de que el ejecutable esté en tu PATH o proporciona la ruta directamente en tu código.
3. Configuración en el código
Una vez instalado, puedes configurar Selenium para abrir un navegador y navegar a una página:
from selenium import webdriver
# Ruta al controlador del navegador (ChromeDriver en este caso)
driver = webdriver.Chrome(executable_path='/ruta/a/chromedriver')
# Abrir una página web
driver.get('https://ejemplo.com')
4. Versiones del navegador y controlador
Es importante que la versión del controlador coincida con la versión de tu navegador. Si el navegador se actualiza, puede ser necesario actualizar también el controlador.
Con esta configuración básica, puedes empezar a utilizar Selenium para interactuar con páginas dinámicas, hacer clic en botones, rellenar formularios, o extraer datos cargados mediante JavaScript.
Desafíos comunes y soluciones en el web scraping
Al realizar web scraping, te encontrarás con desafíos técnicos y legales que afectan la eficiencia del proceso. Desde bloqueos de IP hasta la gestión de datos dinámicos, cada problema tiene sus propias soluciones, como el uso de proxies, rotación de User-Agents o el manejo de CAPTCHAs.
Bloqueo de IP
Uno de los problemas más comunes es el bloqueo de IP. Muchos sitios web detectan patrones de scraping y bloquean las IP que envían demasiadas solicitudes en poco tiempo.
Soluciones
- Rotación de proxies: Usar proxies rotativos permite distribuir las solicitudes desde diferentes direcciones IP, evitando bloqueos.
proxies = {
'http': 'http://tu_proxy:puerto',
'https': 'https://tu_proxy:puerto'
}
response = requests.get(url, proxies=proxies)
- Proxies gratuitos vs. pagos: Los proxies gratuitos son más propensos a ser bloqueados, mientras que los pagos son más confiables para scraping a gran escala.
- Reducción de la velocidad de las solicitudes: Introducir pausas aleatorias entre solicitudes disminuye las probabilidades de detección y bloqueo.
Implementación básica
Usa bibliotecas como Requests para integrar proxies rotativos en tu código.
Detección de User-Agents
Algunos sitios bloquean scraping basado en el User-Agent. Si detectan un script automatizado, como el predeterminado de Python, pueden bloquear el acceso.
Soluciones
- Rotación de User-Agents: Cambiar el User-Agent regularmente para que las solicitudes simulen diferentes navegadores o dispositivos puede evitar detecciones.
from fake_useragent import UserAgent
ua = UserAgent()
headers = {
'User-Agent': ua.random
}
response = requests.get(url, headers=headers)
- User-Agent aleatorio: Usar bibliotecas como fake-useragent para generar User-Agents aleatorios en cada solicitud puede ayudar.
Gestión de CAPTCHAs
Muchos sitios usan CAPTCHAs para bloquear scraping automatizado.
Soluciones
- Servicios para resolver CAPTCHAs: Servicios como 2Captcha o AntiCaptcha resuelven CAPTCHAs de forma automatizada.
- Evitar páginas con CAPTCHAs: Si es posible, intenta hacer scraping en otras páginas del mismo sitio que no tengan CAPTCHAs.
- Uso de Selenium o Playwright: Estas herramientas pueden simular la interacción humana para resolver CAPTCHAs más simples.
Contenido dinámico generado por JavaScript
Algunos sitios cargan contenido mediante JavaScript, por lo que el HTML inicial puede no contener los datos que necesitas.
Soluciones
- Uso de Selenium o Playwright: Estas herramientas automatizan navegadores reales para cargar las páginas completamente, incluyendo el contenido dinámico.
from selenium import webdriver
driver = webdriver.Chrome(executable_path='ruta/al/chromedriver')
driver.get('https://ejemplo.com')
# Esperar a que el contenido se cargue
html = driver.page_source
- Uso de APIs internas: Algunos sitios cargan contenido dinámico a través de APIs internas que puedes usar para extraer los datos directamente, sin cargar toda la página.
Redirecciones inesperadas
Las redirecciones automáticas pueden llevarte a una página diferente, como una de inicio de sesión.
Soluciones
- Manejo de redirecciones con Requests: Puedes deshabilitar las redirecciones automáticas y manejarlas manualmente.
response = requests.get(url, allow_redirects=False)
if response.status_code == 302:
print(f"Redirigido a: {response.headers['Location']}")
Problemas con grandes volúmenes de datos
El scraping a gran escala puede generar problemas de memoria o velocidad, especialmente si debes navegar por muchas páginas o manejar grandes cantidades de datos.
Soluciones
- Scrapy: Para scraping a gran escala, usa frameworks como Scrapy, que son más eficientes que BeautifulSoup y Requests para proyectos grandes.
- Uso de bases de datos: A medida que tu scraper crece, es más eficiente guardar los datos en bases de datos como MongoDB o PostgreSQL en lugar de archivos locales.
Optimización del rendimiento
El scraping a gran escala puede afectar el rendimiento si no se gestiona adecuadamente.
Soluciones
- Concurrente o paralelismo: Implementar hilos concurrentes o procesos paralelos permite realizar múltiples solicitudes al mismo tiempo, mejorando la eficiencia.
import concurrent.futures
def scrape(url):
response = requests.get(url)
return response.content
urls = ['https://ejemplo1.com', 'https://ejemplo2.com']
with concurrent.futures.ThreadPoolExecutor() as executor:
results = executor.map(scrape, urls)
Teniendo en cuenta estos desafíos y soluciones, podrás optimizar tu scraper y hacerlo más resistente a los bloqueos, manteniendo la eficiencia y evitando limitaciones.