Puppeteer is a browser testing and automation library that’s also nice for web scraping. In comparison to simpler tools like Axios and cheerio, Puppeteer enables developers to scrape dynamic content (ie content that changes based on user actions).
Isto significa que pode utilizá-lo para raspar aplicações web (aplicativos de página única) que carregam o seu conteúdo utilizando JavaScript, que é exatamente o que vai fazer aqui.
Web Scraping Using Puppeteer
In this tutorial, you’ll learn how to scrape static and dynamic data (ie post titles and links from Bright Data’s Blog) with Puppeteer.
Setting Up
Before you begin the tutorial, you need to make sure that you have Node.js installed on your computer. You can download it from their official Downloads page.
Em seguida, crie um diretório para o projeto e navegue até ele com os seguintes comandos:
mkdir puppeteer_tutorial
cd puppeteer_tutorial
npm init -y
Em seguida, instale o Puppeteer com este comando:
npm i puppeteer --save
Este comando também descarrega um navegador dedicado que a biblioteca irá utilizar.
Scraping a Static Site
Como todas as ferramentas de raspagem da web, o Puppeteer permite-lhe raspar o código HTML das páginas web.
Seguem-se os passos que pode seguir para utilizar o Puppeteer para raspar a primeira página de publicações do blogue da Bright Data:
Create an index.js
file and import Puppeteer:
const puppeteer = require('puppeteer');
Em seguida, insira o modelo necessário para executar o Puppeteer:
(async () => {
const browser = await puppeteer.launch({
headless: false,
defaultViewport: null
});
const page = await browser.newPage();
await page.goto('https://brightdata.com/blog');
// all the web scraping will happen here
await browser.close();
})();
Esta função abre um navegador, navega até à página para a raspar e fecha o navegador.
Tudo o que resta fazer é raspar os dados da página.
In Puppeteer, the easiest way to access HTML data is through the page.evaluate
method. While Puppeteer has $
and $$
methods, which are wrappers useful for fetching elements, it’s simpler to just get all the data from page.evaluate
.
On the Bright Data blog, all blog post data is wrapped in an <a>
tag with the class of brd_post_entry
. The title of the post is in an <h3>
element with the class of brd_post_title
. The link to the post is the href
value of the brd_post_entry
.
Here’s what a page.evaluate
function that extracts those values looks like:
const data = await page.evaluate( () => {
let data = [];
const titles = document.querySelectorAll('.brd_post_entry');
for (const title of titles) {
const titleText = title.querySelector('.brd_post_title').textContent;
const titleLink = title.href;
const article = { title: titleText, link: titleLink };
data.push(article);
}
return data;
})
Por fim, pode imprimir os dados na consola:
console.log(data);
O script completo tem o seguinte aspeto:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false,
defaultViewport: null
});
const page = await browser.newPage();
await page.goto('https://brightdata.com/blog');
const data = await page.evaluate(() => {
let data = [];
const titles = document.querySelectorAll('.brd_post_entry');
for (const title of titles) {
const titleText = title.querySelector('.brd_post_title').textContent;
const titleLink = title.href;
const article = { title: titleText, link: titleLink };
data.push(article);
}
return data;
})
console.log(data);
await browser.close();
})();
Run it by calling node index.js
in the terminal. The script should return a list of post titles and links:
[
{
title: 'APIs for Dummies: Learning About APIs',
link: 'https://brightdata.com/blog/web-data/apis-for-dummies'
},
{
title: 'Guide to Using cURL with Python',
link: 'https://brightdata.com/blog/how-tos/curl-with-python'
},
{
title: 'Guide to Scraping Walmart',
link: 'https://brightdata.com/blog/how-tos/guide-to-scraping-walmart'
},
…
Scraping Dynamic Content
A raspagem de conteúdos estáticos é uma tarefa simples que pode ser efetuada facilmente com ferramentas simples. Felizmente, o Puppeteer pode ser utilizado para realizar uma vasta gama de ações, tais como clicar, escrever e rolar. Pode utilizar tudo isto para interagir com páginas dinâmicas e simular ações do usuário.
Uma tarefa comum de raspagem da web com uma biblioteca como esta seria procurar um determinado conjunto de dados na página. Por exemplo, pode querer utilizar o Puppeteer para procurar todas as publicações de Bright Data sobre o Puppeteer.
Eis como o pode fazer:
Step 1: Accept Cookies
Quando uma pessoa visita o blogue da Bright Data, aparece por vezes um banner de cookies:
To deal with that, you need to click on the Accept all button with the following code:
await page.waitForSelector('#brd_cookies_bar_accept', {timeout: 5000})
.then(element => element.click())
.catch(error => console.log(error));
The first line of the code waits for an item with the #brd_cookies_bar_accept
to appear for 5 seconds. The second line clicks on that element. The third line makes sure that the script doesn’t crash if the cookie bar doesn’t appear.
Note que a espera no Puppeteer acontece fornecendo alguma condição pela qual se quer esperar, não definindo um certo tempo de espera para que a ação anterior seja executada. A primeira chama-se espera implícita e a segunda chama-se espera explícita.
A espera explícita no Puppeteer é fortemente desencorajada, pois leva a problemas de execução. Se fornecer um tempo de espera explícito, é provável que seja demasiado longo (o que é ineficiente) ou demasiado curto (o que significa que o script não será executado corretamente).
Step 2: Search for Posts
Depois disso, o script precisa de clicar no ícone de pesquisa. Escreva “Puppeteer” e prima novamente o ícone de pesquisa para ativar uma pesquisa:
Isto pode ser feito com o seguinte código:
await page.click('.search_icon');
await page.waitForSelector('.search_container.active');
const search_form = await page.waitForSelector('#blog_search');
await search_form.type('puppeteer');
await page.click('.search_icon');
await new Promise(r => setTimeout(r, 2000));
This example works similarly to the cookie banner example. After clicking the button, you need to wait until the search container appears, which is why the code waits for an element that matches the CSS selector of .search_container.active
.
Além disso, no final, é necessário adicionar uma paragem de dois segundos para que os elementos sejam carregados. Embora as esperas explícitas sejam desaconselhadas no Puppeteer, não existem outras boas opções neste momento.
In most websites, if a change in the URL happens, you can use the waitForNavigation
method. If a new element appears, you can use the waitForSelector
method. Figuring out if some elements are refreshed is a bit more difficult and out of scope for this article.
If you want to try it on your own, this Stack Overflow answer can be of help.
Step 3: Collect the Posts
Depois de procurar as publicações, pode utilizar o código que já utilizou para a raspagem de páginas estáticas para obter os títulos das publicações do blogue:
const data = await page.evaluate( () => {
let data = [];
const titles = document.querySelectorAll('.brd_post_entry');
for (const title of titles) {
const titleText = title.querySelector('.brd_post_title').textContent;
const titleLink = title.href;
const article = { title: titleText, link: titleLink };
data.push(article);
}
return data;
})
console.log(data);
Eis o código completo do script:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: false,
defaultViewport: null
});
const page = await browser.newPage();
await page.goto('https://brightdata.com/blog');
const cookie_bar_accept = await page.waitForSelector('#brd_cookies_bar_accept');
await cookie_bar_accept.click();
await new Promise(r => setTimeout(r, 500));
await page.click('.search_icon');
await page.waitForSelector('.search_container.active');
const search_form = await page.waitForSelector('#blog_search');
await search_form.type('puppeteer');
await page.click('.search_icon');
await new Promise(r => setTimeout(r, 2000));
const data = await page.evaluate( () => {
let data = [];
const titles = document.querySelectorAll('.brd_post_entry');
for (const title of titles) {
const titleText = title.querySelector('.brd_post_title').textContent;
const titleLink = title.href;
const article = { title: titleText, link: titleLink };
data.push(article);
}
return data;
})
console.log(data);
await browser.close();
})();
Can You Do Better?
Embora seja possível utilizar scripts de raspagem da web com o Puppeteer, não é o ideal. O Puppeteer é feito para automação de testes, o que o torna um pouco incómodo para realizar a raspagem da web.
For example, if you want to achieve scale and efficiency in your scripts, it’s important to be able to scrape without being blocked. To do this, you can use proxies—gateways between you and the website you scrape. While Puppeteer supports the use of proxies, you need to find and contract with a proxy network on your own (learn more about Puppeteer proxy integration with Bright Data).
Além disso, otimizar o Puppeteer para uso paralelo não é fácil. Se pretender raspar muitos dados, terá de trabalhar arduamente para obter um desempenho ótimo.
Estas desvantagens significam que o Puppeteer é uma boa escolha para pequenos scripts para utilização por hobby, mas levará muito tempo escalar as suas operações se o utilizar.
In case you want something that’s easier to use, you can pick a web data platform like Bright Data. It enables companies to gather massive amounts of structured data from the web using easy-to-use tools, like the Scraping Browser (Puppeteer/Playwright compatible), that’s specially made for scraping.
Conclusion
Neste artigo, você aprendeu a usar o Puppeteer para raspar páginas da web estáticas e dinâmicas.
O Puppeteer pode executar a maioria das ações que um navegador pode fazer, incluindo clicar em itens, escrever texto e executar JavaScript. E devido ao uso de esperas implícitas, os scripts escritos no Puppeteer são rápidos e fáceis de escrever.
Mas também pode haver alguns problemas: o Puppeteer não é a ferramenta mais eficiente para a raspagem da web e a sua documentação não é adequada para principiantes. Também é difícil escalar operações de raspagem usando o Puppeteer se você não estiver bem familiarizado com ele.
Tired of scraping data yourself? Get precollected or custom datasets.