Uma Visão Geral sobre Web Scraping com Node.js
Mesmo tendo iniciado na área de Web Scraping com Python, eu preferi me aventurar no ecossistema de ferramentas que o Node.js oferece. Acabei gostando bastante, partes porque eu prefiro a sintaxe do TypeScript ao invés do Python e outras porque há ferramentas de parsing de HTML que oferecem uma API semelhante ao que temos nos navegadores para manipulação de DOM.
Introdução ao Web Scraping
Web Scraping é uma técnica de obtenção de dados disponíveis na web. Difere-se da maneira tradicional utilizando APIs pois, na maioria das vezes, simula o acesso que um usuário humano faria em um site para obter seu conteúdo e, posteriormente, transformá-lo em dados estruturados que atendam a um propósito.
Com ela, podemos obter dados como notícias de portais, informações sobre produtos de um ou vários e-commerces, dados financeiros da bolsa de valores e muito mais. Web Scraping é uma forma de transformar páginas imundas em dados estruturados que agregam valor de alguma forma.
Web Scraping, assim como muitas coisas do mundo de programação é uma forma inteligente de resolver tarefas repetitivas. Isso significa que, ao invés de ter pessoas monitorando manualmente alguma informação, você terá um script raspando essa informação quantas vezes você quiser todos os dias.
Scraper vs. Crawler
No mundo de Web Scraping, podemos chamar um script ou um algoritmo de dois nomes: Scraper e Crawler. Apesar de às vezes serem confundidos como sinônimos, possuem propósitos bem definidos e talvez um pouco distintos.
Um Crawler também é um Scraper porém, um Scraper não necessariamente é um Crawler. Isso acontece porque, enquanto um Scraper é um script que busca qualquer tipo de informação de uma página, um Crawler tem como sua única função encontrar links de paginação de forma recursiva.
Como talvez você tenha imaginado, o Google utiliza Crawlers para indexar links de sites, encontrando links novos para páginas antes desconhecidas todos os dias. Por outro lado, você pode também ter imaginado que sites como Buscapé, Zoom e outros sites de promoções utilizam Scrapers para monitorar preços de produtos dos sites de e-commerce.
Listando ferramentas
Aqui vai uma lista útil das principais bibliotecas que eu uso ou conheço para a criação de scripts que extraem dados na Web:
| Ferramenta | Propósito | Nome do Pacote | Descrição | Link |
|---|---|---|---|---|
| Axios | Cliente HTTP | axios | Biblioteca leve para fazer requisições HTTP com suporte a Promises; ideal para baixar páginas e APIs. | https://npmjs.org/package/axios |
| Node HTML Parser | Parser de HTML | node-html-parser | Extrator de HTML rápido e leve, sem emular DOM completo; bom para scraping simples. | https://npmjs.org/package/node-html-parser |
| Cheerio | Parser de HTML | cheerio | Implementa uma versão do jQuery para Node, permitindo query e manipulação de HTML facilmente. | https://www.npmjs.com/package/cheerio |
| He | Encoder e Decorder de HTML | he | Converte entidades HTML para texto puro e vice-versa; útil para limpar conteúdos codificados. | https://npmjs.com/package/he |
| Sanetize HTML | Higienizar HTML | sanitize-html | Remove tags e atributos perigosos de HTML, mantendo apenas o permitido. | https://npmjs.com/package/sanitize-html |
| Markdown It | Transformador de Markdown para HTML | markdown-it | Converte markdown em HTML com alta customização e suporte a plugins. | https://npmjs.com/package/markdown-it |
| JMESPath | Query de JSON | jmespath | Linguagem declarativa para transformar e projetar dados JSON, permitindo selecionar e reestruturar objetos com expressões funcionais. | https://npmjs.com/package/jamespath |
| JSONPath | Query de JSON | jsonpath | Permite navegar e filtrar estruturas JSON com sintaxe similar a XPath. | https://npmjs.com/package/jsonpath |
| Got | Cliente HTTP | got | Cliente HTTP moderno com suporte nativo a streams, retries e HTTP2. | https://npmjs.org/package/got |
| Got Scraping | Cliente HTTP | got-scraping | Versão do Got otimizada para scraping com spoofing de headers e manipulação anti-bloqueio. | https://npmjs.org/package/got-scraping |
| Impit | Cliente HTTP | impit | "Substituto do Got Scraping"; Requisições HTTP minimalistas com suporte embutido a headless scraping. | https://npmjs.org/package/impit |
| Fake User Agent | Gerador de User-Agents | fake-useragent | Fornece rotação de User-Agents reais para evitar bloqueios em requisições. | https://npmjs.com/package/fake-useragent |
| Header Generator | Gerador de Cabeçalhos de Requisição | header-generator | Gera cabeçalhos HTTP realistas com base em padrões de navegadores reais. | https://npmjs.org/package/header-generator |
| Puppeteer | Manipulador de Navegador | puppeteer | Controla o Chrome/Chromium de forma automatizada; ideal para páginas com JavaScript. | https://npmjs.org/package/puppeteer |
| Puppeteer Extra | Manipulador de Navegador | puppeteer-extra | Wrapper do Puppeteer com suporte a plugins como stealth e adblock. | https://npmjs.org/package/puppeteer-extra |
| Puppeteer Real Browser | Manipulador de Navegador | puppeteer-real-browser | Emula um navegador real com comportamento humano para evitar detecção. | https://npmjs.org/package/puppeteer-real-browser |
| Playwright | Manipulador de Navegador | playwright | Automação de navegadores com suporte multi-engine (Chromium, Firefox, WebKit). | https://npmjs.com/package/playwright |
| Playwright Extra | Manipulador de Naveador | playwright-extra | Versão estendida do Playwright com sistema de plugins similar ao Puppeteer Extra. | https://npmjs.com/package/playwright-extra |
| Nut.js | Automatizador janela | @nut-tree-fork/nut-js | Controla mouse e teclado a nível de sistema operacional para automação fora do navegador. | https://npmjs.com/package/@nut-tree-fork/nut-js |
| JSAutoGui | Automatizador de janela | jsautogui | Ferramenta inspirada no PyAutoGUI para automatizar cliques, teclado e captura de tela. | https://npmjs.com/package/jsautogui |
| Tesseract | Reconhecimento Óptico de Caracteres | node-tesseract-ocr | Wrapper para OCR Tesseract, permitindo extrair texto de imagens. | https://npmjs.com/package/node-tesseract-ocr |
| Axios Cookie Jar Support | Suporte a Cookie Jar | axios-cookiejar-support | Adiciona a funcionalidade de gerenciamento de cookies (cookie jar) ao Axios, permitindo persistir cookies entre requisições. | https://npmjs.com/package/axios-cookiejar-support |
| Tough Cookie | Gerenciador de Cookies | tough-cookie | Biblioteca robusta para análise e armazenamento de cookies HTTP; geralmente usada em conjunto com outros clientes HTTP. | https://npmjs.com/package/tough-cookie |
Aqui vai uma lista de plugins para Puppeteer Extra, Puppeteer Real Browser e Playwright Extra que talvez você precise algum dia:
| Plugin | Nome do Pacote | Descrição | Link |
|---|---|---|---|
| Amazon Captcha | @mihnea.dev/puppeteer-extra-amazon-captcha | Resolve automaticamente captchas da Amazon utilizando técnicas de OCR e interação simulada. | https://npmjs.com/package/@mihnea.dev/puppeteer-extra-amazon-captcha |
| Stealth | puppeteer-extra-plugin-stealth | Obfusca características do navegador automatizado para evitar detecção por sites anti-bot. | https://npmjs.com/package/puppeteer-extra-plugin-stealth |
| AdBlocker | puppeteer-extra-plugin-adblocker | Bloqueia anúncios e rastreadores durante a navegação para acelerar scraping e reduzir ruído visual. | https://npmjs.com/package/puppeteer-extra-plugin-adblocker |
| reCAPTCHA Solver | puppeteer-extra-plugin-recaptcha | Detecta automaticamente desafios reCAPTCHA e os resolve via serviços externos como 2Captcha. | https://npmjs.com/package/puppeteer-extra-plugin-recaptcha |
| Anonymize UA | puppeteer-extra-plugin-anonymize-ua | Substitui o User-Agent padrão do Puppeteer por versões mais realistas para reduzir detecção. | https://npmjs.com/package/puppeteer-extra-plugin-anonymize-ua |
| Block Resources | puppeteer-extra-plugin-block-resources | Impede o carregamento de recursos pesados como imagens, fontes e mídia para acelerar scraping. | https://npmjs.com/package/puppeteer-extra-plugin-block-resources |
Para encontrar seletores de elementos, além do Dev Tools, utilizo algumas extensões no navegador:
Evitando bloqueios de requisições
Sites utilizam muitas formas para bloquear requisições consideradas suspeitas, que eles julgam que possa ser de bots e scripts.
1. User-Agents
Alguns utilizam algoritmos simples que apenas validam o User-Agent no cabeçalho da requisição, tentando deduzir se ele parece confiável.
Neste contexto, você pode utilizar bibliotecas como fake-useragent para rotacionar o seu User-Agent ou então descobrir qual é o seu User-Agent aqui e explicitamente definir no código para que seu site alvo então pense que você está acessando pelo seu navegador convencional. Se você estiver utilizando Puppeteer Extra, talvez seja melhor utilizar a extensão Anonymize UA (puppeteer-extra-plugin-block-resources) já que ela é preparada para esse caso de uso.
2. Rate Limiting
Já outros sites possuem além disso, possuem um rate limit que é um controle de quantas requisições podem ser feitas dentro de uma janela de tempo, ou seja, se você ultrapassar esse limite o servidor não vai te responder com o que você deseja e, em casos piores, irá banir seu IP por um tempo ou para sempre.
Para evitar ser bloqueado por rate limit você deve criar filas de requisições em seu código, permitindo x quantidade de requisições por janela de tempo determinada.
3. Cabeçalhos
Os cabeçalhos de requisição embora você talvez não saiba, diz muito sobre você. A falta de informações que normalmente um navegador de verdade enviaria levanta uma suspeita que pode acabar acarretando em uma punição para seu IP ou a não obtenção do conteúdo desejado.
Para evitar isso, eu particularmente gosto de entrar no site usando o navegador e abrir o Dev Tools e então copiar a requisição com o tipo node-fetch e ir testando o quanto daqueles cabeçalhos eu posso retirar sem afetar a resposta. Ok, esta não é uma forma muito elegante, e para lidar com isso melhor você pode optar por usar clientes HTTP que tentam se assemelhar a navegadores de verdade como o got-scraping e o impit.
4. Extensões, Webdiver exposto e preferências
Quando você está está fazendo web scraping com navegadores de verdade usando puppeteer ou playwright é comum que eventualmente você acabe sendo descoberto como uma automação por algum site ele e comece a exigir captchas para continuar ou punir o seu IP. Para contornar isso você pode utilizar o plugin Stealth (puppeteer-extra-plugin-stealth) do Puppeteer Extra que também é compatível com Playwright como citei anteriormente.
Há formas de se evitar isso manualmente também, se você quiser tentar, seria algo assim:
4.1 Forjando Extensões
Aqui, a nossa intenção é enganar a verificação em client-side que busca por extensões no nosso navegador:
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, "plugins", {
get: () => [1, 2, 3, 4, 5],
})
})
4.2 Forjando Idiomas
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, "languages", {
get: () => ["en-US", "en"],
})
})
4.3 Escondendo Extensões de Automação
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, "webdriver", {
get: () => undefined
})
delete (navigator as any).__proto__.webdriver
})
4.4 Passando Checagem de Webdriver
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, "webdriver", {
get: () => false
})
})
4.5 Passando Checagem do Chrome
await page.evaluateOnNewDocument(() => {
(window as any).chrome = {
runtime: {}
}
})
4.6 Passando Checagem de Notificações
await page.evaluateOnNewDocument(() => {
const originalQuery = window.navigator.permissions.query as any
return window.navigator.permissions.query = (parameters) => {
return parameters.name === "notifications" ?
Promise.resolve({ state: Notification.permission }) :
originalQuery(parameters)
}
})
5. Fingerprinting
Alguns sites que possuem algoritmos de bloqueio mais robustos e complexos que podem identificar você com fingerprinting. Estes sites vão criar um hash utilizando-se de características do seu computador e navegador como sistema operacional, resolução da tela, profundidade de cor da tela, extensões instaladas, fontes instaladas, APIs WebGL e Canvas (que podem revelar detalhes sobre a sua placa de vídeo), idiomas do navegador e configurações de fuso horário.
Ou seja, ainda que você toque de IP ou o seu User-Agent, você ainda terá a mesma identificação de hash. Para contornar esse tipo de coisa, geralmente usa-se Puppeteer Extra juntamente com o Plugin Stealth (puppeteer-extra-plugin-stealth).
6. Autenticação e Cookies
Alguns sites, requisitam de um token de acesso para conseguir acessar rotas e pode ser que exijam que este token esteja nos cookies do site. Neste caso, se você não estiver utilizando um navegador de verdade, irá precisar utilizar bibliotecas complementares, no caso do Axios você precisará de Axios Cookie Jar Support (axios-cookiejar-support) e Tough Cookie (tough-cookie) e no caso da Got você precisará apenas da Tough Cookie.
Já outros sites guardam esse token no localStorage, neste caso você teria de injetar usando um navegador real como o Puppeteer ou Playwright usando page.evaluate(() => {...}).
7. Captchas
Os captchas são um dos maiores problemas para quem quer fazer web scraping sem gastar dinheiro. Eles são criados para limpar o tráfego dos sites, bloqueando e punindo o acesso de bots.
Alguns captchas são mais fáceis de se desviar, como por exemplo o captcha da Amazon, que consiste em apenas identificar caracteres distorcidos, digitar em um campo e clicar em um botão. Você pode tentar implementar isso usando Puppeteer e Tesseract.js, mas o Puppeteer Extra já tem a solução em forma de extensão, o Amazon Captcha (@mihnea.dev/puppeteer-extra-amazon-captcha) que eu havia citado lá em cima.
Outros captchas como por exemplo o da hCaptcha, reCaptcha e Cloudflare Turnstile são mais complexos de se desviar, se você conseguir criar uma solução automatizada sem depender de um serviço de terceiros provavelmente vai parar de funcionar muito em breve. Eu consegui contornar o Cloudflare Turnstile usando uma combinação de puppeteer-real-browser para ser um navegador que aparenta ser normal e @nut-tree-fork/nut-js para dar um clique mais realista.
Se você precisar lidar com captchas desse tipo para um projeto sério, recomendo fortemente que você utilize serviços com 2Captcha, Capsolve ou NextCaptcha.
Estou escrevendo um livro :D
Galera, queria compartilhar com vocês que que eu estou escrevendo um livro sobre Web Scraping com Node.js e estava me sentindo um pouco travado, achando que a forma com que escrevo talvez não fosse o suficiente para um livro, mas continuo tentando!
E foi com o objetivo de tentar me soltar um pouco que eu fiz este artigo, espero que tenha sido útil para vocês, e se ainda não foi, espero que um dia seja.

