Apenas extraímos as primeiras 10 moedas porque é esse o número que o CoinMarketCap carrega no carregamento inicial da página. Para extrair mais, precisamos de realizar uma ação humana, que consiste em percorrer a página. Felizmente, o Playwright é adequado para esta tarefa.
Vamos começar por refatorar a função $$eval que utilizámos anteriormente e implementar a paginação. Vamos chamar a esta nova função extractData:
const extractData = async (page, currentPage, perPage = 10) => {
}
Ampliamos o seletor :nth-child selecionando itens em etapas (elementos de 0 a 10, de 11 a 21, de 22 a 32, etc.). Definimos o seletor inicial (primeiros 10 elementos):
let selector = `:nth-child(-n+${currentPage * perPage})`;
Por último, mas não menos importante, adicionamos suporte para as páginas seguintes. O código fica assim:
if(currentPage > 1) {
selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;
}
A função final ficará assim:
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;
});
};
Agora é hora de voltar ao nosso código do scraper, implementar a rolagem e ampliar a extração de dados. Fazemos todo o trabalho após esta linha:
await page.goto('https://coinmarketcap.com');
Redefinimos a variável currencies:
// Extract the currencies data
let currencies = await extractData(page, 1, 10);
Usando a função evaluate, fazemos o scroll da página para 1,5x da janela de visualização. Isto irá acionar o carregamento dos próximos elementos da tabela:
// Scroll the page to a little more than the viewport height
await page.evaluate(() => {
window.scrollTo(0, window.innerHeight * 1.5);
});
O segundo de espera dará à interface do utilizador algum tempo para preencher a tabela com os dados recuperados da API:
// Wait for the new elements to load
await page.waitForTimeout(1000);
Por fim, vamos extrair os dados da segunda página e registar os resultados:
// Extract the next 10 elements
currencies = [...currencies, ...await extractData(page, 2, 10)]
// Display the results
console.log(currencies)
O código completo do scraper deve ficar assim:
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();
})();
Para recapitular, o scraper irá abrir a página inicial do CoinMarketCap, extrair os dados das primeiras 10 moedas, percorrer a página, extrair os dados das próximas 10 moedas e apresentar os resultados.
Deve obter resultados semelhantes a estes:
[
{ 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' }
]