Cuando accedes a Internet directamente, los sitios web pueden rastrear fácilmente tus solicitudes hasta tu dirección IP. Esta exposición puede generar publicidad dirigida y seguimiento en línea y, potencialmente, comprometer tu identidad digital.
Ahí es donde entran en juego los proxies. Actúan como intermediarios entre tu ordenador e Internet y te ayudan a proteger tu identidad digital. Cuando usas un servidor proxy, este envía solicitudes a sitios web en tu nombre utilizando la dirección IP del propio proxy.
En lo que respecta al raspado web, los proxies pueden ayudarte a eludir las prohibiciones de IP, evitar el bloqueo geográfico y proteger tu identidad. En este artículo, aprenderás a implementar proxies en C# para todos tus proyectos de raspado web.
Requisitos previos
Antes de empezar este tutorial, asegúrate de tener lo siguiente:
- Visual Studio 2022 o Visual Studio Code
- .NET 7 o una versión posterior
- Paquete NuGet HTMLAgilityPack
En los ejemplos de este artículo, se usa una aplicación de consola .NET independiente. Para crear la tuya propia, puedes usar una de las siguientes guías:
- Crear una aplicación de consola .NET con Visual Studio Code
- Crear una aplicación de consola .NET con Visual Studio
Para empezar, necesitas crear dos aplicaciones de consola, WebScrapApp
y WebScrapBrightData
:
Para el raspado web, especialmente si trabajas con contenido HTML, necesitas herramientas específicas como HtmlAgilityPack. Esta biblioteca simplifica el análisis y la manipulación de HTML, lo que facilita la extracción de datos de páginas web.
En ambos proyectos (esto es, WebScrapApp
y WebScrapBrightData
), añade el paquete NuGet HTMLAgilityPack haciendo clic con el botón derecho en la carpeta NuGet y, luego, en Administrar paquetes NuGet. Cuando aparezca una ventana emergente, busca «HTMLAgilityPack» e instálalo para ambos proyectos:
Para ejecutar cualquiera de los siguientes proyectos, debes ir al directorio del proyecto en la línea de comandos y usar cd path\to\your\project
seguido de dotnet run
. También puedes pulsar F5 para crear y ejecutar el proyecto en Visual Studio. Ambos métodos compilan y ejecutan la aplicación y muestran el resultado correspondiente.
Nota: si no tienes Visual Studio 2022, puedes usar cualquier IDE alternativo que sea compatible con .NET 7. Ten en cuenta que algunos pasos de esta guía pueden variar.
Cómo configurar un proxy local
En lo que respecta al raspado web, lo primero que hay que hacer es utilizar un servidor proxy. Este tutorial usa el proxy de código abierto mitmproxy.
Para empezar, entra en descargas de mitmproxy, descarga la versión 10.1.6 y selecciona la versión correcta para tu sistema operativo. Para obtener ayuda adicional, consulta la guía oficial de instalación de mitmproxy.
Tras instalar mitmproxy, ejecútalo introduciendo el siguiente comando en tu terminal:
mitmproxy
Deberías ver una ventana en tu consola o terminal con este aspecto:
Para probar el proxy, abre otra terminal o intérprete de comandos y ejecuta la siguiente solicitud curl:
curl --proxy http://localhost:8080 "http://wttr.in/Dunedin?0"
El resultado debería ser el siguiente:
Weather report: Dunedin
Cloudy
.--. +11(9) °C
.-( ). ↙ 15 km/h
(___.__)__) 10 km
0.0 mm
En la ventana de mitmproxy, deberías ver que interceptó la llamada a través del proxy local:
Raspado web en C#
En la siguiente sección, configurarás la aplicación de consola de C# de WebScrapApp
para el raspado web. Esta aplicación utiliza el servidor proxy e incluye la rotación del proxy para mejorar la eficiencia.
Crear un HttpClient
La clase ProxyHttpClient
está diseñada para configurar una instancia HttpClient
para dirigir las solicitudes a través de un servidor proxy específico.
En tu proyecto de WebScrapApp
, crea un nuevo archivo de clase llamado ProxyHttpClient.cs
y añade el siguiente código:
namespace WebScrapApp
{
public class ProxyHttpClient
{
public static HttpClient CreateClient(string proxyUrl)
{
var httpClientHandler = new HttpClientHandler()
{
Proxy = new WebProxy(proxyUrl),
UseProxy = true
};
return new HttpClient(httpClientHandler);
}
}
}
Implementar la rotación del proxy
Para implementar la rotación del proxy, crea un archivo de clase ProxyRotator.cs
en tu solución de WebScrapApp
:
namespace WebScrapApp
{
public class ProxyRotator
{
private List<string> _validProxies = new List<string>();
private readonly Random _random = new();
public ProxyRotator(string[] proxies, bool isLocal)
{
if (isLocal)
{
_validProxies.Add("http://localhost:8080/");
}
else
{
_validProxies = ProxyChecker.GetWorkingProxies(proxies.ToList()).Result;
}
if (_validProxies.Count == 0)
throw new InvalidOperationException();
}
public HttpClient ScrapeDataWithRandomProxy(string url)
{
if (_validProxies.Count == 0)
throw new InvalidOperationException();
var proxyUrl = _validProxies[_random.Next(_validProxies.Count)];
return ProxyHttpClient.CreateClient(proxyUrl);
}
}
}
Esta clase administra una lista de proxies y proporciona un método para seleccionar un proxy para cada solicitud web de forma aleatoria. Esta distribución aleatoria es clave para reducir el riesgo de detección y posibles prohibiciones de IP durante el raspado web.
Cuando isLocal
se establece en True
, toma el proxy local de mitmproxy. Si se establece en False
, toma las IP públicas de los proxies.
El ProxyChecker
se usa para validar la lista de servidores proxy.
A continuación, crea un nuevo archivo de clase llamado ProxyChecker.cs
y añade el siguiente código:
using WebScrapApp;
namespace WebScrapApp
{
public class ProxyChecker
{
private static SemaphoreSlim consoleSemaphore = new SemaphoreSlim(1, 1);
private static int currentProxyNumber = 0;
public static async Task<List<string>> GetWorkingProxies(List<string> proxies)
{
var tasks = new List<Task<Tuple<string, bool>>>();
foreach (var proxyUrl in proxies)
{
tasks.Add(CheckProxy(proxyUrl, proxies.Count));
}
var results = await Task.WhenAll(tasks);
var workingProxies = new List<string>();
foreach (var result in results)
{
if (result.Item2)
{
workingProxies.Add(result.Item1);
}
}
return workingProxies;
}
private static async Task<Tuple<string, bool>> CheckProxy(string proxyUrl, int totalProxies)
{
var client = ProxyHttpClient.CreateClient(proxyUrl);
bool isWorking = await IsProxyWorking(client);
await consoleSemaphore.WaitAsync();
try
{
currentProxyNumber++;
Console.WriteLine($"Proxy: {currentProxyNumber} de {totalProxies}");
}
finally
{
consoleSemaphore.Release();
}
return new Tuple<string, bool>(proxyUrl, isWorking);
}
private static async Task<bool> IsProxyWorking(HttpClient client)
{
try
{
var testUrl = "http://www.google.com";
var response = await client.GetAsync(testUrl);
return response.IsSuccessStatusCode;
}
catch
{
return false;
}
}
}
}
Este código define el ProxyChecker
para validar una lista de servidores proxy. Cuando utilizas el método GetWorkingProxies
con una lista de URL de proxy, comprueba el estado de cada proxy de forma asincrónica mediante el método CheckProxy
y recopila los proxies operativos en una lista workingProxies
. En CheckProxy
, estableces un HttpClient
con la URL del proxy, realizas una solicitud de prueba a http://www.google.comy registras el progreso de forma segura mediante un semáforo.
El método IsProxyWorking
confirma la funcionalidad del proxy al examinar el código de estado de la respuesta y devolver true
para los proxies operativos. Esta clase ayuda a identificar los proxies que funcionan a partir de una lista determinada.
Raspado de datos web
Para extraer datos, crea un nuevo archivo de clase WebScraper.cs
en tu solución de WebScrapApp
y añade el siguiente código:
using HtmlAgilityPack;
namespace WebScrapApp
{
public class WebScraper
{
public static async Task ScrapeData(ProxyRotator proxyRotator, string url)
{
try
{
var client = proxyRotator.ScrapeDataWithRandomProxy(url);
// Use HttpClient to make an asynchronous GET request
var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
// Load the HTML content into an HtmlDocument
HtmlDocument doc = new();
doc.LoadHtml(content);
// Use XPath to find all <a> tags that are direct children of <li>, <p>, or <td>
var nodes = doc.DocumentNode.SelectNodes("//li/a[@href] | //p/a[@href] | //td/a[@href]");
if (nodes != null)
{
foreach (var node in nodes)
{
string hrefValue = node.GetAttributeValue("href", string.Empty);
string title = node.InnerText; // This gets the text content of the <a> tag, which is usually the title
// Since Wikipedia URLs are relative, we need to convert them to absolute
Uri baseUri = new(url);
Uri fullUri = new(baseUri, hrefValue);
Console.WriteLine($"Title: {title}, Link: {fullUri.AbsoluteUri}");
// You can process each title and link as required
}
}
else
{
Console.WriteLine("No article links found on the page.");
}
// Add additional logic for other data extraction as needed
}
catch (Exception ex)
{
throw ex;
}
}
}
}
En este código, defines el WebScraper
, que encapsula la funcionalidad de raspado web. Cuando llamas al método ScrapeData
, le proporcionas una instancia de ProxyRotator
y una URL de destino. En este método, utilizas un HttpClient
para realizar una solicitud GET asincrónica a la URL, recuperar el contenido HTML y analizarlo mediante la biblioteca HtmlAgilityPack. A continuación, empleas consultas XPath para localizar y extraer los enlaces y los títulos correspondientes de elementos HTML específicos. Si encuentras algún enlace a un artículo, imprimes sus títulos y URL absolutas; de otra manera, imprimes un mensaje que indica que no se encontró ningún enlace.
Tras configurar el mecanismo de rotación del proxy e implementar la funcionalidad de raspado web, es necesario que integres con fluidez estos componentes en el punto de entrada principal de la aplicación, que normalmente se encuentra en Program.cs
. Esta integración permite a la aplicación ejecutar tareas de raspado web mientras utilizas proxies rotativos con un enfoque específico en extraer datos de la página de inicio de Wikipedia:
namespace WebScrapApp {
public class Program
{
static async Task Main(string[] args)
{
string[] proxies = {
"http://162.223.89.84:80",
"http://203.80.189.33:8080",
"http://94.45.74.60:8080",
"http://162.248.225.8:80",
"http://167.71.5.83:3128"
};
var proxyRotator = new ProxyRotator(proxies, false);
string urlToScrape = "https://www.wikipedia.org/";
await WebScraper.ScrapeData(proxyRotator, urlToScrape);
}
}
}
En este código, la aplicación inicializa una lista de URL de proxy, crea una instancia de ProxyRotator
, especifica la URL de destino para el raspado (en este caso, https://www.wikipedia.org/
) e invoca el método WebScraper.ScrapeData
para iniciar el proceso de raspado web.
La aplicación usa una lista de direcciones IP de proxy gratuitas, especificadas en la matriz de proxies, para enrutar las solicitudes de extracción web, ocultar la fuente fiable y minimizar el riesgo de que el servidor de Wikipedia la bloquee. El método ScrapeData
está configurado para raspar la página de inicio de Wikipedia y extraer y mostrar los títulos y enlaces de los artículos en la consola. La clase ProxyRotator
gestiona la rotación de estos proxies, lo que mejora la discreción del raspado.
Ejecutar WebScrapApp
Para ejecutar WebScrapApp
, abre una nueva terminal o intérprete de comandos en el directorio raíz de tu aplicación de WebScrapApp
y ejecuta los siguientes comandos:
dotnet build
dotnet run
El resultado debería ser el siguiente:
…output omitted…
Title: Latina, Link: https://la.wikipedia.org/
Title: Latviešu, Link: https://lv.wikipedia.org/
Title: Lietuvių, Link: https://lt.wikipedia.org/
Title: Magyar, Link: https://hu.wikipedia.org/
Title: Македонски, Link: https://mk.wikipedia.org/
Title: Bahasa Melayu, Link: https://ms.wikipedia.org/
Title: Bahaso Minangkabau, Link: https://min.wikipedia.org/
Title: bokmål, Link: https://no.wikipedia.org/
Title: nynorsk, Link: https://nn.wikipedia.org/
Title: Oʻzbekcha / Ўзбекча, Link: https://uz.wikipedia.org/
Title: Қазақша / Qazaqşa / قازاقشا, Link: https://kk.wikipedia.org/
Title: Română, Link: https://ro.wikipedia.org/
Title: Simple English, Link: https://simple.wikipedia.org/
Title: Slovenčina, Link: https://sk.wikipedia.org/
Title: Slovenščina, Link: https://sl.wikipedia.org/
Title: Српски / Srpski, Link: https://sr.wikipedia.org/
Title: Srpskohrvatski / Српскохрватски, Link: https://sh.wikipedia.org/
Title: Suomi, Link: https://fi.wikipedia.org/
Title: தமிழ், Link: https://ta.wikipedia.org/
…output omitted…
Cuando se invoca el método ScrapeData
de la clase WebScraper
, realiza la extracción de datos de Wikipedia, lo que da como resultado una visualización de los títulos de los artículos y sus enlaces correspondientes en la consola. Este código usa proxies disponibles públicamente y, cada vez que ejecutas la aplicación, elige una de las direcciones IP de la lista como proxy.
Para realizar pruebas con el proxy local de mitmproxy, actualiza el método ProxyRotator
del archivo Program
con lo siguiente:
var proxyRotator = new ProxyRotator(proxies, true)
string urlToScrape = "http://toscrape.com/";
Al establecer el valor en true
, utiliza el servidor proxy local que se ejecuta en el puerto 8080 de tu host local (esto es, el servidor de mitmproxy).
Para simplificar el proceso de instalación al configurar un certificado en tu equipo, cambia la URL a http://toscrape.com/
.
Comprueba que el servidor de mitmproxy esté funcionando y, a continuación, vuelve a ejecutar los mismos comandos:
dotnet build
dotnet run
El resultado debería ser el siguiente:
…output omitted…
Title: fictional bookstore, Link: http://books.toscrape.com/
Title: books.toscrape.com, Link: http://books.toscrape.com/
Title: A website, Link: http://quotes.toscrape.com/
Title: Default, Link: http://quotes.toscrape.com/
Title: Scroll, Link: http://quotes.toscrape.com/scroll
Title: JavaScript, Link: http://quotes.toscrape.com/js
Title: Delayed, Link: http://quotes.toscrape.com/js-delayed
Title: Tableful, Link: http://quotes.toscrape.com/tableful
Title: Login, Link: http://quotes.toscrape.com/login
Title: ViewState, Link: http://quotes.toscrape.com/search.aspx
Title: Random, Link: http://quotes.toscrape.com/random
…output omitted…
Si compruebas la ventana de mitmproxy desde tu terminal o intérprete de comandos, verás que interceptó la llamada:
Como puedes ver, configurar un proxy local o cambiar entre varios proxies puede ser una tarea compleja y lenta. Afortunadamente, herramientas como Bright Data pueden ayudar. En la siguiente sección, aprenderás a usar el servidor proxy de Bright Data para simplificar el proceso de raspado.
Proxy de Bright Data
Bright Data ofrece una red de servicios de proxy disponibles en 195 ubicaciones. Esta red integra la función de rotación del proxy de Bright Data, que alterna sistemáticamente entre servidores para mejorar la eficacia y la seguridad del raspado web.
El uso de este sistema de proxy rotativo reduce el riesgo de prohibiciones o bloqueos de IP que suelen producirse durante las tareas de raspado web. Cada solicitud utiliza un proxy diferente, lo que oculta la identidad del raspador y dificulta que los sitios web detecten y limiten el acceso. Este enfoque mejora la fiabilidad de la recopilación de datos y garantiza un mayor anonimato y seguridad.
La plataforma de Bright Data está diseñada para facilitar su uso y una configuración sencilla, por lo que es perfecta para proyectos de raspado web en C#, que verás en la siguiente sección.
Crear un proxy residencial
Antes de poder usar un proxy de Bright Data en tu proyecto, debes configurar una cuenta. Para hacerlo, visita el sitio web de Bright Data y regístrate para obtener una prueba gratuita.
Una vez que hayas configurado tu cuenta, inicia sesión y haz clic en el icono de ubicación de la izquierda para ir a Infraestructura de proxies y raspado. A continuación, haz clic en Añadir y elige Proxies residenciales:
Mantén los nombres predeterminados y vuelve a hacer clic en Añadir para crear un proxy residencial:
Una vez creado el proxy, deberías ver las credenciales, incluidos el host, el puerto, el nombre de usuario y la contraseña. Guarda estas credenciales en un lugar seguro, ya que las necesitarás más adelante:
Entra en tu proyecto de WebScrapingBrightData desde tu IDE o terminal/intérprete de comandos. A continuación, crea un archivo de clase BrightDataProxyConfigurator.cs
y añade el siguiente código:
using System.Net;
namespace WebScrapBrightData
{
public class BrightDataProxyConfigurator
{
public static HttpClient ConfigureHttpClient(string proxyHost, string proxyUsername, string proxyPassword)
{
var proxy = new WebProxy(proxyHost) {
Credentials = new NetworkCredential(proxyUsername, proxyPassword)
};
var httpClientHandler = new HttpClientHandler() {
Proxy = proxy,
UseProxy = true
};
var client = new HttpClient(httpClientHandler);
client.DefaultRequestHeaders.Add("User-Agent", "YourUserAgent");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.TryAddWithoutValidation("Proxy-Authorization", Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{proxyUsername}:{proxyPassword}")));
return client;
}
}
}
En este código, defines una clase BrightDataProxyConfigurator
, que incluye el método ConfigureHttpClient
. Cuando se llama, este método configura y devuelve un HttpClient
que está configurado para usar un servidor proxy. Para ello, crea una URL de proxy con el nombre de usuario
, contraseña
, host
y puerto
proporcionados y, a continuación, configura un HttpClientHandler
con este proxy. En último término, el método devuelve una instancia HttpClient
que enruta todas sus solicitudes a través del proxy especificado.
A continuación, crea el archivo de clase WebContentScraper.cs
en el proyecto de WebScrapingBrightData
y añade el siguiente código:
using HtmlAgilityPack;
namespace WebScrapBrightData
{
public class WebContentScraper
{
public static async Task ScrapeContent(string url, HttpClient client)
{
var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
HtmlDocument doc = new();
doc.LoadHtml(content);
var nodes = doc.DocumentNode.SelectNodes("//li/a[@href] | //p/a[@href] | //td/a[@href]");
if (nodes != null)
{
foreach (var node in nodes)
{
string hrefValue = node.GetAttributeValue("href", string.Empty);
string title = node.InnerText;
Uri baseUri = new(url);
Uri fullUri = new(baseUri, hrefValue);
Console.WriteLine($"Title: {title}, Link: {fullUri.AbsoluteUri}");
}
}
else
{
Console.WriteLine("No article links found on the page.");
}
}
}
}
Este código define una clase WebContentScraper
con un método asincrónico estático, ScrapeContent
. Este método toma una URL y un HttpClient, obtiene el contenido de la página web, lo analiza como HTML y extrae enlaces de elementos HTML específicos (elementos de lista, párrafos y celdas de tablas). A continuación, imprime los títulos y los URI absolutos de estos enlaces a la consola.
Clase de programa
Ahora, vuelva a raspar Wikipedia y comprueba cómo Bright Data mejora el acceso y el anonimato.
Actualiza el archivo de clase Program.cs
con el siguiente código:
namespace WebScrapBrightData
{
public class Program
{
public static async Task Main(string[] args)
{
// Bright Data Proxy Configuration
string host = "your_brightdata_proxy_host";
string username = "your_brightdata_proxy_username";
string password = "your_brightdata_proxy_password";
var client = BrightDataProxyConfigurator.ConfigureHttpClient(host, username, password);
// Scrape content from the target URL
string urlToScrape = "https://www.wikipedia.org/";
await WebContentScraper.ScrapeContent(urlToScrape, client);
}
}
}
Nota: asegúrate de reemplazar las credenciales del proxy de Bright Data por las que guardaste anteriormente.
A continuación, para probar y ejecutar tu aplicación, abre tu terminal o intérprete de comandos desde el directorio raíz de tu proyecto de WebScrapBrightData
y ejecuta el siguiente comando:
dotnet build
dotnet run
Deberías obtener el mismo resultado que antes cuando utilizabas proxies públicos:
…output omitted…
Title: Latina, Link: https://la.wikipedia.org/
Title: Latviešu, Link: https://lv.wikipedia.org/
Title: Lietuvių, Link: https://lt.wikipedia.org/
Title: Magyar, Link: https://hu.wikipedia.org/
Title: Македонски, Link: https://mk.wikipedia.org/
Title: Bahasa Melayu, Link: https://ms.wikipedia.org/
Title: Bahaso Minangkabau, Link: https://min.wikipedia.org/
Title: bokmål, Link: https://no.wikipedia.org/
Title: nynorsk, Link: https://nn.wikipedia.org/
Title: Oʻzbekcha / Ўзбекча, Link: https://uz.wikipedia.org/
Title: Қазақша / Qazaqşa / قازاقشا, Link: https://kk.wikipedia.org/
Title: Română, Link: https://ro.wikipedia.org/
Title: Simple English, Link: https://simple.wikipedia.org/
Title: Slovenčina, Link: https://sk.wikipedia.org/
Title: Slovenščina, Link: https://sl.wikipedia.org/
Title: Српски / Srpski, Link: https://sr.wikipedia.org/
Title: Srpskohrvatski / Српскохрватски, Link: https://sh.wikipedia.org/
Title: Suomi, Link: https://fi.wikipedia.org/
Title: தமிழ், Link: https://ta.wikipedia.org/
…output omitted…
El programa usa el proxy de Bright Data para raspar la página principal de Wikipedia y mostrar los títulos y enlaces extraídos en la consola. Esto demuestra la eficacia y la facilidad de integrar el proxy de Bright Data en un proyecto de raspado web de C# para una extracción de datos discreta y sólida.
Si deseas visualizar el efecto de usar un proxy de Bright Data, puedes intentar enviar una solicitud GET a http://lumtest.com/myip.json. Este sitio web devolverá la ubicación y otros detalles relacionados con la red del cliente que actualmente intenta acceder al sitio web. Si quieres probarlo tú mismo, abre el enlace en una nueva pestaña del navegador. Deberías ver los detalles de tu red, que son visibles públicamente.
Para probarlo con un proxy de Bright Data, actualiza el código en WebContentScraper.cs
para que coincida con lo siguiente:
using HtmlAgilityPack;
public class WebContentScraper
{
public static async Task ScrapeContent(string url, HttpClient client)
{
var response = await client.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
HtmlDocument doc = new();
doc.LoadHtml(content);
Console.Write(content);
}
}
A continuación, actualiza la variable urlToScrape
del archivo Program.cs
para raspar el sitio web:
string urlToScrape = "http://lumtest.com/myip.json";
Ahora, intenta ejecutar la aplicación de nuevo. Deberías ver un resultado como este en tu terminal:
{"ip":"79.221.123.68","country":"DE","asn":{"asnum":3320,"org_name":"Deutsche Telekom AG"},"geo":{"city":"Koenigs Wusterhausen","region":"BB","region_name":"Brandenburg","postal_code":"15711","latitude":52.3014,"longitude":13.633,"tz":"Europe/Berlin","lum_city":"koenigswusterhausen","lum_region":"bb"}}
Esto indica que la solicitud se está enviando a través de uno de los servidores proxy de Bright Data.
Conclusión
En este artículo, has aprendido a usar servidores proxy con C# para el raspado web.
Si bien los servidores proxy locales pueden resultar útiles en algunos casos, suelen presentar limitaciones para los proyectos de raspado web. Afortunadamente, los servidores proxy pueden ayudar. Con su amplia red global y una gran gama de opciones de proxy, que incluyen proxies residenciales, de ISP, de centros de datosy móviles, Bright Data garantiza un alto grado de flexibilidad y fiabilidad. Cabe destacar que la función de rotación del proxy tiene un valor incalculable para las tareas de raspado a gran escala, ya que ayuda a mantener el anonimato y a reducir el riesgo de prohibiciones de IP.
A medida que continúes con tu viaje de raspado web, considera la posibilidad de utilizar las soluciones de Bright Data como una forma potente y ajustable a escala de recopilar datos de manera eficiente y, al mismo tiempo, seguir las mejores prácticas de raspado web.
Todo el código de este tutorial está disponible en este repositorio de GitHub.
No se requiere tarjeta de crédito