Solo hemos extraído las primeras 10 divisas porque es la cantidad que CoinMarketCap carga en la carga inicial de la página. Para extraer más, necesitamos realizar una acción humana, que es desplazarnos por la página. Afortunadamente, Playwright es ideal para esta tarea.
Empecemos por refactorizar la función $$eval que utilizamos anteriormente e implementar la paginación. Llamaremos a esta nueva función extractData:
const extractData = async (page, currentPage, perPage = 10) => {
}
Ampliamos el selector :nth-child seleccionando elementos por pasos (elementos del 0 al 10, del 11 al 21, del 22 al 32, etc.). Definimos el selector inicial (los primeros 10 elementos):
let selector = `:nth-child(-n+${currentPage * perPage})`;
Por último, pero no menos importante, añadimos soporte para las páginas siguientes. El código queda así:
if(currentPage > 1) {
selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;
}
La función final tendrá este aspecto:
const extractData = async (page, currentPage, perPage = 10) => {
let selector = `:nth-child(-n+${currentPage * perPage})`;
if(currentPage > 1) {
selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;
}
return await page.$$eval(`.cmc-table tbody tr${selector}`, trs => {
const data = [];
trs.forEach(tr => {
data.push({
name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,
price: tr.querySelector('td:nth-child(4) a span').innerHTML,
});
});
return data;
});
};
Ahora es el momento de volver a nuestro código de scraper, implementar el desplazamiento y ampliar la extracción de datos. Hacemos todo el trabajo después de esta línea:
await page.goto('https://coinmarketcap.com');
Redefinimos la variable de divisas:
// Extract the currencies data
let currencies = await extractData(page, 1, 10);
Usando la función evaluate, desplazamos la página a 1,5 veces el tamaño de la ventana de visualización. Esto hará que se carguen los siguientes elementos de la tabla:
// Scroll the page to a little more than the viewport height
await page.evaluate(() => {
window.scrollTo(0, window.innerHeight * 1.5);
});
El segundo de espera le dará a la interfaz de usuario algo de tiempo para rellenar la tabla con los datos recuperados de la API:
// Wait for the new elements to load
await page.waitForTimeout(1000);
Por último, extraigamos los datos de la segunda página y registremos los resultados:
// Extract the next 10 elements
currencies = [...currencies, ...await extractData(page, 2, 10)]
// Display the results
console.log(currencies)
El código completo del rastreador debería tener este aspecto:
const { chromium } = require('playwright');
const extractData = async (page, currentPage, perPage = 10) => {
let selector = `:nth-child(-n+${currentPage * perPage})`;
if(currentPage > 1) {
selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;
}
return await page.$$eval(`.cmc-table tbody tr${selector}`, trs => {
const data = [];
trs.forEach(tr => {
data.push({
name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,
price: tr.querySelector('td:nth-child(4) a span').innerHTML,
});
})
return data;
})
};
(async () => {
// Launch a Chromium browser
const browser = await chromium.launch();
// Create a new page in the browser
const page = await browser.newPage();
// Navigate to a website
await page.goto('https://coinmarketcap.com');
// Extract the currencies data
let currencies = await extractData(page, 1, 10)
// Scroll the page to a little more than the viewport height
await page.evaluate(() => {
window.scrollTo(0, window.innerHeight * 1.5);
});
// Wait for the new elements to load
await page.waitForTimeout(1000);
// Extract the next 10 elements
currencies = [...currencies, ...await extractData(page, 2, 10)];
// Display the results
console.log(currencies);
// Close the browser
await browser.close();
})();
En resumen, el scraper abrirá la página de inicio de CoinMarketCap, extraerá los datos de las primeras 10 monedas, desplazará la página, extraerá los datos de las siguientes 10 monedas y mostrará los resultados.
Deberías obtener resultados similares a estos:
[
{ name: 'Bitcoin', price: '$16,742.58' },
{ name: 'Ethereum', price: '$1,244.45' },
{ name: 'Tether', price: '$0.9997' },
{ name: 'USD Coin', price: '$1.00' },
{ name: 'BNB', price: '$255.78' },
{ name: 'XRP', price: '$0.335' },
{ name: 'Binance USD', price: '$1.00' },
{ name: 'Dogecoin', price: '$0.07066' },
{ name: 'Cardano', price: '$0.2692' },
{ name: 'Polygon', price: '$0.7762' },
{ name: 'Dai', price: '$0.9994' },
{ name: 'Litecoin', price: '$73.80' },
{ name: 'Polkadot', price: '$4.59' },
{ name: 'Solana', price: '$12.95' },
{ name: 'TRON', price: '$0.0505' },
{ name: 'Shiba Inu', price: '$0.000008234' },
{ name: 'Uniswap', price: '$5.29' },
{ name: 'Avalanche', price: '$11.43' },
{ name: 'UNUS SED LEO', price: '$3.47' },
{ name: 'Wrapped Bitcoin', price: '$16,725.03' },
{ name: 'Cosmos', price: '$9.97' }
]