Como construir um raspador e descarregar um ficheiro com o Puppeteer
Mihnea-Octavian Manolache em 25 de abril de 2023

Se gosta de web scraping e está a usar Node JS, então muito provavelmente já ouviu falar do Puppeteer. E, com certeza, já se deparou com uma tarefa que exigia o download de um arquivo com o Puppeteer. Esta é, de facto, uma tarefa recorrente na comunidade de scraping. Mas não está bem documentada na documentação do Puppeteer.
Felizmente, vamos tratar disso juntos. Neste artigo, vamos discutir o download de arquivos no Puppeteer. Há dois objectivos que quero que abordemos hoje:
- Ter uma sólida compreensão de como o Puppeteer lida com os downloads
- Criar um raspador de download de ficheiros funcional utilizando o nó e o Puppeteer
No final deste artigo, terá adquirido as competências teóricas e práticas de que um programador necessita para construir um raspador de ficheiros. Se este projeto me parece tão excitante como parece a mim, vamos a isto!
Porquê descarregar um ficheiro com o Puppeteer?
Há muitos casos de uso para um raspador de arquivos e o StackOverflow está cheio de desenvolvedores procurando respostas sobre como baixar arquivos com o puppeteer. E temos de compreender que os ficheiros incluem imagens, PDFs, documentos do Excel ou do Word e muitos outros. Pode ver porque é que tudo isto pode fornecer informações muito importantes a alguém.
Por exemplo, há empresas no sector do dropshipping que dependem de imagens extraídas de fontes externas, como mercados. Outro bom exemplo de caso de utilização de um raspador de descarregamento de ficheiros é para empresas que monitorizam documentos oficiais. Ou mesmo pequenos projectos. Eu próprio tenho um script que descarrega facturas do sítio Web de um parceiro.
Agora, no que diz respeito à utilização do Puppeteer para descarregar ficheiros, acho que a maioria das pessoas o escolheu principalmente por duas razões:
- Foi concebido para Node JS, e Node JS é uma das linguagens de programação mais populares, tanto para o front end como para o back end.
- Abre um navegador real e alguns sítios Web dependem do JavaScript para processar o conteúdo. Isto significa que não seria possível descarregar os ficheiros utilizando um cliente HTTP normal que não consegue processar ficheiros JavaScript.
Como o Puppeteer lida com o download de ficheiros
Para compreender como descarregar ficheiros com o Puppeteer, temos de saber como o Chrome também o faz. Isto porque, na sua essência, o Puppeteer é uma biblioteca que "controla" o Chrome através do Chrome DevTools Protocol (CDP).
No Chrome, os ficheiros podem ser descarregados:
- Manualmente, com um clique de um botão, por exemplo
- De forma programática, através do domínio de página do CDP.
E há ainda uma terceira técnica utilizada na recolha de dados da Web. Nomeadamente, integra um novo ator: um cliente HTTP. Desta forma, o web scraper recolhe `hrefs` dos ficheiros e depois é utilizado um cliente HTTP para descarregar os ficheiros. Cada opção tem os seus casos de utilização particulares e, por isso, vamos explorar ambas as formas.
Descarregar ficheiros no Puppeteer com um clique de um botão
No caso de o site que você deseja extrair arquivos usar botões, tudo o que você precisa fazer é simular o evento de clique no Puppeteer. A implementação de um downloader de arquivos é bastante simples neste cenário. O Puppeteer até documenta o método Page.click() e você pode encontrar mais informações aqui.
Uma vez que este é um comportamento "semelhante ao humano", o que precisamos de fazer é:
- Abrir o browser
- Navegar para a página Web visada
- Localize o elemento do botão (através do seu seletor CSS ou xPath, por exemplo)
- Clicar no botão
Existem apenas quatro passos simples que precisamos de implementar no nosso script. No entanto, antes de começarmos a codificá-lo, deixe-me dizer-lhe que, tal como o seu browser do dia a dia, a instância do Chrome controlada pelo Puppeteer irá guardar o ficheiro transferido na pasta de transferências predefinida, que é:
- \Users\<username>\Downloads for Windows
- /Users/<username>/Downloads for Mac
- /home/<username>/Downloads for Linux
Com isto em mente, vamos começar a programar. Vamos assumir que somos astrofísicos e que precisamos de recolher alguns dados da NASA, que iremos processar mais tarde. Por agora, vamos concentrar-nos em descarregar os ficheiros .doc.
#1: Identificar elementos "clicáveis
Vamos navegar para o domínio da NASA aqui e inspecionar os elementos da página. O nosso objetivo é identificar os elementos "clicáveis". Para encontrar os elementos, abra as Ferramentas de desenvolvimento (Command + Option + I / Control + Shift + I no Chrome):

#2: Codificar o projeto
import puppeteer from "puppeteer"
(async () => {
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
await page.goto('https://www.nasa.gov/centers/dryden/research/civuav/civ_uav_doc-n-ref.html',
{ waitUntil: 'networkidle0' })
const tr_elements = await page.$x('html/body/div[1]/div[3]/div[2]/div[2]/div[5]/div[1]/table[2]/tbody/tr')
for (let i = 2; i<=tr_elements.length; i ++) {
const text = await tr_elements[i].evaluate(el => el.textContent)
if (text.toLocaleLowerCase().includes('doc')) {
try {
await page.click(`#backtoTop > div.box_710_cap > div.box_710.box_white.box_710_white > div.white_article_wrap_detail.text_adjust_me > div.default_style_wrap.prejs_body_adjust_detail > table:nth-child(6) > tbody > tr:nth-child(${i}) > td:nth-child(3) > a`)
}catch {}
}
}
await browser.close()
})()
O que estamos a fazer aqui é:
- Iniciar o Puppeteer e navegar para o nosso sítio Web de destino
- Selecionar todos os elementos `tr`, que contêm a `href` em que queremos clicar mais tarde
- Iterar através dos elementos tr e
a. Verificar se o texto no interior do elemento contém a palavra 'doc'
b. Se contiver, criamos o seletor e clicamos no elemento - Fechar o browser
E é tudo. O download de ficheiros com o Puppeteer pode ser tão simples quanto possível.
Descarregar ficheiros no Puppeteer com CDP
Eu sei que a pasta de downloads padrão não é um grande problema para pequenos projetos. Para projetos maiores, por outro lado, você certamente vai querer organizar os arquivos baixados com o Puppeteer em diferentes diretórios. E é aí que o CDP entra em cena. Para atingir esse objetivo, manteremos o código atual e apenas adicionaremos algo a ele.
A primeira coisa em que se pode pensar é resolver o caminho para o diretório atual. Felizmente, nós podemos usar o módulo node:path embutido. Tudo o que precisamos fazer é importar o módulo `path` para o nosso projeto e usar o método `resolve`, como você verá daqui a pouco.
O segundo aspeto é definir o caminho no nosso navegador usando o CDP. Como eu disse antes, usaremos o método `.setDownloadBehavior` do Page Domain. Aqui está o aspeto do nosso código atualizado com os dois métodos adicionados:
import puppeteer from "puppeteer"
import path from 'path'
(async () => {
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
const client = await page.target().createCDPSession()
await client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: path.resolve('./documents')
});
await page.goto('https://www.nasa.gov/centers/dryden/research/civuav/civ_uav_doc-n-ref.html',
{ waitUntil: 'networkidle0' })
const tr_elements = await page.$x('html/body/div[1]/div[3]/div[2]/div[2]/div[5]/div[1]/table[2]/tbody/tr')
for (let i = 1; i<=tr_elements.length; i ++) {
const text = await tr_elements[i].evaluate(el => el.textContent)
if (text.toLocaleLowerCase().includes('doc')) {
try {
await page.click(`#backtoTop > div.box_710_cap > div.box_710.box_white.box_710_white > div.white_article_wrap_detail.text_adjust_me > div.default_style_wrap.prejs_body_adjust_detail > table:nth-child(6) > tbody > tr:nth-child(${i}) > td:nth-child(3) > a`)
} catch {}
}
}
await browser.close()
})()
Eis o que estamos a fazer com o código adicionado:
- Estamos a criar uma nova CDPSession para "falar do protocolo Chrome Devtools em bruto
- Estamos emitindo o evento `Page.setDownloadBehavior` onde
a. `behavior` é definido como `allow` downloads
b.`downloadPath` é construído com `node:path` para apontar para a pasta onde armazenaremos nossos arquivos
E isto é tudo o que tem de fazer se quiser mudar o diretório onde guarda os ficheiros com o Puppeteer. Além disso, também atingimos o nosso objetivo de construir um web scraper de download de ficheiros.
Descarregar ficheiro com o Puppeteer e o Axios
A terceira opção que discutimos consiste em recolher ligações para os sítios visados e utilizar um cliente HTTP para as descarregar. Pessoalmente, prefiro o axios, mas também há alternativas a ele quando se trata de raspagem da Web. Por isso, para efeitos deste tutorial, vou utilizar o axios e assumir que estamos a construir um scraper para imagens de carros leiloados.
#1: Recolher as ligações para os nossos ficheiros
const get_links = async (url) => {
const hrefs = []
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
await page.goto(url, { waitUntil: 'networkidle0' })
const images = await page.$$('img')
for (let i = 1; i<=images.length; i ++) {
try {
hrefs.push(await images[i].evaluate(img => img.src))
} catch {}
}
await browser.close()
return hrefs
}
Eu tenho certeza que agora você já está familiarizado com a sintaxe do Puppeteer. Ao contrário dos scripts acima, o que estamos fazendo agora é avaliar os elementos `img`, extraindo sua url de origem, anexando-a a um array e retornando o array. Não há nada de especial nesta função.
#2: Guardar ficheiros com o axios
const download_file = async (url, save) => {
const writer = fs.createWriteStream(path.resolve(save))
const response = await axios({
url,
method: 'GET',
responseType: 'stream'
})
response.data.pipe(writer)
return new Promise((resolve, reject) => {
writer.on('finish', resolve)
writer.on('error', reject)
})
}
Esta função é um pouco mais complexa, pois introduz dois novos pacotes: `fs` e `axios`. O primeiro método do pacote `fs` é praticamente auto-explicativo. O que ele faz é criar um fluxo gravável. Você pode ler mais sobre ele aqui.
Em seguida, usamos o axios para 'tocar' na URL do servidor, e deixamos o axios saber que a resposta será do tipo 'stream'. Por fim, como estamos trabalhando com um stream, vamos usar `pipe()` para escrever a resposta no nosso stream.
Com esta configuração, tudo o que resta fazer é combinar as duas funções num programa executável. Para o fazer, basta adicionar as seguintes linhas de código:
let i = 1
const images = await get_links('https://www.iaai.com/Search?url=PYcXt9jdv4oni5BL61aYUXWpqGQOeAohPK3E0n6DCLs%3d')
images.forEach(async (img) => {
await download_file(img, `./images/${i}.svg`)
i += 1
})
Conclusões
A documentação do Puppeteer pode não ser clara sobre o download de ficheiros. Apesar disso, hoje descobrimos algumas formas de o implementar. Mas note que fazer scraping de arquivos com o Puppeteer não é uma tarefa fácil. É que o Puppeteer lança um browser sem cabeça e estes normalmente são bloqueados muito rapidamente.
Se está à procura de uma forma furtiva de descarregar ficheiros programaticamente, pode voltar a sua atenção para um serviço de raspagem da Web. Na Web Scraping API, investimos muito tempo e esforço para esconder as nossas impressões digitais, de modo a não sermos detectados. E isso mostra resultados reais na nossa taxa de sucesso. Descarregar ficheiros utilizando a Web Scraping API pode ser tão fácil como enviar um pedido curl, e nós tratamos do resto.
Dito isto, espero que o artigo de hoje o tenha ajudado no seu processo de aprendizagem. Além disso, espero que tenhamos atingido ambos os nossos objectivos iniciais. A partir de agora, deve ser capaz de construir as suas próprias ferramentas e descarregar ficheiros com o Puppeteer.
Notícias e actualizações
Mantenha-se atualizado com os mais recentes guias e notícias sobre raspagem da Web, subscrevendo a nossa newsletter.
We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Artigos relacionados

Aprenda a extrair sites dinâmicos renderizados em JavaScript usando o Scrapy e o Splash. Desde a instalação até à escrita de um spider, à manipulação da paginação e à gestão das respostas do Splash, este guia abrangente oferece instruções passo a passo tanto para principiantes como para especialistas.


Saiba qual é o melhor browser para contornar os sistemas de deteção Cloudflare enquanto faz web scraping com o Selenium.


Descubra como extrair e organizar eficientemente dados para raspagem da Web e análise de dados através de análise de dados, bibliotecas de análise de HTML e metadados schema.org.
