Como criar um bot de compra e venda de Bitcoin usando Node.js – Parte 2

Atualizado em 28/12/17!

Desde que escrevi a primeira parte deste tutorial de criação de um bot para comprar e vender Bitcoins e outras criptomoedas usando a API do Mercado Bitcoin a valorização da moeda atingiu níveis históricos, ultrapassando a marca de U$15.000 em apenas 1 BTC!!!

Na primeira parte foquei nos conceitos fundamentais, criação e configuração da conta na exchange, criação do projeto e codificação do algoritmo de monitoramento do mercado. Agora faremos a codificação do nosso bot para que ele consiga comprar e vender criptomoedas.

Ajustando a API

Abra novamente o seu projeto (sugiro estar utilizando o Visual Studio Code) e vá até o seu arquivo api.js, que é nosso REST client para a API do Mercado Bitcoin. Caso não tenha feito a primeira parte do tutorial, você pode baixá-la no post anterior, no formulário ao fim do mesmo.

Logo no início do arquivo api.js, vamos modificar o original para adicionar dois novos módulos que serão necessários, o crypto e o querystring (o unirest é do tutorial anterior):

O primeiro serve para criptografar mensagens e o segundo para concatenar variáveis no formato de querystring do HTTP. Precisaremos de ambos pois o endpoint que vamos acessar para efetuar transações exige criptografia e para algumas operações devemos enviar dados no body de um POST.

Para que estes módulos funcionem corretamente precisamos instalá-los no projeto usando o NPM via console:

Ainda no arquivo api.js, logo abaixo dos requires, vamos definir duas novas constantes para o path da Trade API e para o endpoint da mesma (o outro endpoint é o de consulta de informações):

E por fim, logo abaixo do prototype da API de informações, vamos adicionar o construir de um novo objeto que representa a inicialização da API de trading:

As configurações passadas por parâmetro são a chave da API, o segredo da API, o PIN da sua conta e a moeda que vai negociar com esse bot (BTC, BCH e LTC no caso do Mercado Bitcoin). Note que KEY, SECRET e PIN eu lhe ensinei como obter no post anterior, dentro da sua área de trader da exchange.

Logo abaixo desta função construtora, vamos adicionar o prototype da API de trading, como segue:

Aqui estou definindo diversas operações que a API nos permite realizar, apenas configurando as chamadas e passando-as para uma função call que não está implementada. Vale citar o que cada operação realiza:

  • getAccountInfo: pega informações da sua conta, especialmente o seu saldo atual em cada moeda, incluindo BRL;
  • listMyOrders: traz as suas últimas ordens no mercado, tanto de compra quanto de venda e incluindo as que estão pendentes e canceladas, sendo que você pode filtrar livremente pelos parâmetros que discutirei mais tarde;
  • placeBuyOrder: criar uma nova ordem de compra no livro de negociações. Caso haja disponibilidade (tem moedas sendo vendidas ao preço que deseja pagar), a ordem será executada imediatamente. Caso contrário, ela vai pro livro e seu saldo fica bloqueado para honrar a compra;
  • placeSellOrder: cria uma nova ordem de venda no livro de negociações. Caso haja disponibilidade (tem ordens de compra ao preço que você deseja vender), a ordem será executada imediatamente. Caso contrário, ela vai pro livro e seu saldo na criptomoeda fica bloqueado para honrar a venda;
  • cancelOrder: cancela uma ordem sua no livro de negociações;

Você deve ter notado que diversas chamadas concatenam ‘BRL’ à sigla da criptomoeda que vamos negociar, gerando strings como BRLBTC, que é o que chamamos de ‘coin pair’ ou ‘par de moedas’, o que indica que você está negociando Reais por Bitcoins.

Também deve ter notado que em algumas ocasiões eu concateno variáveis numéricas com strings vazias, para forçá-las a serem strings, que é o formato que a API pede em diversos casos.

Para lidar com a assincronicidade inerente às chamadas de rede do Node, a maioria das functions espera um callback de success e outro de error.

E a função call?

Ela é um pouquinho complexa, mas segue abaixo:

A Trade API opera somente com requisições POST, cujos parâmetros da requisição devem estar no formato querystring no body. Para montar este body, conto com a ajuda do módulo querystring, concatenando os parâmetros, o nome da ação que esta request fará e um número único que identifica esta request, que aqui apenas estou pegando o timestamp atual, que sabemos que nunca se repetirá.

A Trade API também exige que cada requisição seja enviada com um cabeçalho TAPI-ID com a chave de API e um header TAPI-MAC, que é um hash HMAC-SHA512 que montamos usando o path completo desta requisição e o API secret como chave de criptografia. De acordo com o response da API, executamos o callback de success ou de error.

Para finalizar a nossa API, vamos apenas modificar o module.exports no final para expor ambas APIs neste módulo api.js:

Comprando Bitcoins em Node.js

Uma vez que seu bot esteja monitorando o mercado e você tenha implementado a APIs de trading, você deverá definir a lógica de compra e venda de criptomoedas. Não há uma regra aqui, exceto que você deve procurar comprar em baixa e vender em alta, para lucrar com a oscilação da moeda.

Para começar, vamos criar uma regra bem simples, baseada em uma observação do gráfico candlestick do Bitcoin no momento que escrevo este post:

Gráfico Bitcoin
Gráfico Bitcoin

Podemos observar neste gráfico que o Bitcoin está valorizado em R$51.800 por unidade, o que é um valor mediano considerando a máxima e mínima dos últimos dias. Por exemplo, vamos modificar nosso index.js para ordenar uma compra quando o Bitcoin cair abaixo de R$50.000 a unidade:

Neste código eu inicializo a trade API logo no topo, usando diversas variáveis de ambiente presentes no seu arquivo .env que criamos na parte 1 deste tutorial.

Agora após realizar o ticker para pegar as informações atuais do mercado, eu verifico o preço de venda mais barato do Bitcoin atualmente. Se ele for menor que o número que eu defini como bom para compra (50000), eu mando comprar. Note que você pode criar todo tipo de inteligência para calcular este número, como verificar o spread das últimas 24h para inferir variação, verificar volume de vendas e compras realizadas para inferir tendências de alta ou baixa, etc.

Uma vez que o preço esteja dentro do seu patamar considerado aceitável, é hora de criar a ordem de compra no livro de negociações. O primeiro parâmetro é quantos bitcoins você deseja comprar (1, mas podem ser frações com até 5 casas decimais) e o segundo parâmetro é o preço máximo que deseja pagar por uma unidade de bitcoin (50000). Diversas coisas podem acontecer neste momento.

Caso você não tenha saldo suficiente, você terá um erro como retorno. Já no caso de que não exista nenhuma ordem de venda compatível com o preço que deseja pagar, sua compra não será efetuada imediatamente. Ela vai parar no livro, seu saldo será bloqueado, e ela será executada assim que alguém estiver vendendo pelo valor que você deseja comprar (ou mais barato). Nestes casos de execução futura, você está sendo executado, e geralmente paga uma taxa de comissão menor para a exchange.

Caso você tenha saldo e exista uma ordem de venda no mercado compatível com a sua ordem de compra, ela será executada imediatamente. O seu saldo em BRL (R$) será liquidado, você receberá saldo em BTC e pagará a comissão de executador, que geralmente é mais alta do que a taxa de quem foi executado (o vendedor cuja ordem já estava no livro antes da sua).

Vendendo Bitcoins em Node.js

Da mesma forma que realizamos a compra, podemos criar uma condição de venda de criptomoedas em nosso robô. Aqui existem dois cenários: operar em STOP ou buscar a lucratividade máxima. Obviamente o risco da segunda alternativa é muito maior e não existe um algoritmo 100% eficiente.

Se optar por operar em STOP, você pode definir uma lucratividade padrão que deseja atingir em cada trade, como 3% por exemplo, e assim que realizar uma compra, você já emite uma ordem de venda com o ágio correspondente, como abaixo (substitua o código de compra por este):

Com isso, você garante que venderá a um valor mínimo que lhe renda o lucro desejado (neste caso, vender a 51500 lhe renderá 2% líquido, por causa das comissões de compra e venda da exchange), tão logo o mercado atinja esse patamar. Eu gosto de operar assim, pois é uma forma de investimento de renda fixa com Bitcoin. Essa porcentagem deve ser relativa ao spread atual (diferença entre mínima e máxima dentro de um período) e deve ser sempre superior a 1% (por causa das comissões).

Note que esta venda está sendo posicionada no livro de negociações no mesmo instante que você está comprando a moeda. Ela não vai ser executada instantaneamente, mas assim que o mercado atingir aquele patamar, o que pode levar alguns minutos em dias de alta volatilidade, ou até mesmo semanas, em períodos de baixa volatilidade (ou caso tenha escolhido um valor muito alto).

Ordens de compra e de venda não executadas podem ser canceladas a qualquer momento usando a function apropriada e seu dinheiro é devolvido (seja ele BTC ou BRL).

Se optar por operar com lucratividade máxima, você não deverá posicionar sua ordem de venda imediatamente após a ordem de compra e sim, deverá continuar monitorando o mercado para entender qual o melhor momento de vender. Esse desafio eu deixo para você bolar.

Otimizando Compra e Venda de Bitcoin

Usei números fixos nos exemplos anteriores, mas é óbvio que essa estratégia não é lá muito boa pois cria um bot muito burro. O que sugiro é que você sempre realize uma consulta à sua conta para saber quanto possui de saldo da moeda que vai utilizar, para poder calcular quanto pode comprar/vender, evitando erros na API.

Para fazer isso, vamos criar uma function na index.js chamada getQuantity:

Esta function espera a moeda que estamos negociando, o preço de uma unidade dela, se é uma compra ou venda (true/false) e um callback.

Após algumas conversões usamos a Trade API para pegar informações da conta do indivíduo e descobrir o saldo dele da moeda que vamos criar a ordem. Se for uma compra (isBuy === true), estaremos operando em BRL e temos de ter no mínimo R$50 na conta para poder gastar, comprando uma quantidade igual a saldo / preço. Se for uma venda de criptomoeda, o próprio saldo (balance) da criptomoeda é a quantidade que queremos negociar.

Aqui considero que você sempre deseja usar todo o seu saldo na transação o que, em volumes altos, pode ser muito arriscado.

Agora vamos usar esta getQuantity nas nossas chamadas de compra, para deixá-las dinâmicas, adaptadas ao saldo atual na sua conta do Mercado Bitcoin:

Também usei a quantidade retornada pelo response da compra para passar ao request de venda, evitando chamadas desnecessárias à API. E por fim, usei o valor de venda do mercado (tick.sell) e multipliquei pela rentabilidade desejada que eu deixei configurada no arquivo .env na primeira parte do tutorial, lembra? Assim, fica fácil de mudar isso depois conforme a sua análise do mercado, até que tenha capacidade para montar uma fórmula em cima do spread corrente.

A única otimização que eu não tenho como lhe ajudar é a de determinar quando é o melhor momento para compra, deixei o 50000 chumbado ali no código e depois você deve mudar conforme o mercado. Parece um problema simples, mas não é, é tão complicado descobrir a mínima de compra quanto a máxima de venda e se eu tivesse essa fórmula já estaria rico.

Espero que tenham gostado deste tutorial e tendo qualquer dúvida, deixe nos comentários!

Curtiu o post? Que tal aprender mais sobre como criar aplicações incríveis usando Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Como criar um bot de compra e venda de Bitcoin usando Node.js

Estamos em 2017 e jamais as criptomoedas estiveram tão em alta. Com o Bitcoin batendo recordes de valorização superiores a U$11.000 e centenas de milhares de brasileiros comprando e vendendo a moeda todos os dias é difícil ficar apenas olhando. E eu não sou o tipo de empreendedor que fica apenas olhando.

Independente de você acreditar ou não que o Bitcoin está em uma bolha, a oscilação da moeda traz diversas oportunidades para quem quer fazer trading, ou seja, compra e venda de moeda visando lucro na diferença entre as cotações. Em alguns dias é até mesmo possível fazer day trade de Bitcoin, considerando que nas últimas semanas tivemos diversos dias com spread (diferença entre mínima e máxima) superior a 10%!!!

Para comprar e vender Bitcoin e demais criptomoedas (considerando que minerar não é mais tão lucrativo quanto foi no passado) você deve ter conta em algum exchange. Dentre os brasileiros eu confio apenas em dois: Mercado Bitcoin e Foxbit, tendo 500 mil brasileiros operando no primeiro e 120 mil no segundo. O processo é bem burocrático para que seu cadastro seja aprovado, necessitando o envio de selfies, documentos, etc. Uma vez com a conta aberta (o que pode demorar de um a dois dias), você deve fazer um depósito bancário na conta bancária do exchange para que sua conta tenha saldo para operar.

A operação tradicional é muito simples: você usa reais para comprar Bitcoins (a preço atual ou futuro) e usa Bitcoins para vender e ganhar reais, que ficam na sua conta do exchange até ser sacado de volta para sua conta bancária. Sim, você deve ter uma conta bancária para realizar essas transações e, se não possuir nenhuma, sugiro criar uma no banco que trabalho, a Agiplan. É super fácil, rápido e não tem qualquer custo, basta baixar o app Agipag e fazer o cadastro no próprio app (iOS e Android).

Como o preço oscila bastante, um trader experiente pode fazer muita grana comprando em baixa e vendendo nas altas da moeda. Um colega de serviço fez R$4 mil em um único dia com essa estratégia em uma oscilação de 20% que a moeda teve esses dias com os rumores de uma possível abertura de contratos futuros na Nasdaq no ano que vem. Pois é, o negócio está ficando sério.

Como eu não sou um trader, sequer experiente, decidi criar um bot para fazer o monitoramento do mercado, além da compra e venda das moedas. E é isso que vou ensinar você a fazer hoje. Não, eu não vou revelar meus ganhos com bitcoin para não me responsabilizar caso você perca dinheiro e não lhe incentivar a colocar muita grana, mas sim, estou ganhando mais do que na renda fixa. Trading de bitcoin é algo muito arriscado e não recomendo colocar mais do que 20% das suas economias nele.

Primeiro passo: criar uma conta no exchange

Para conseguir fazer este tutorial você deve ter uma conta criada na Mercado Bitcoin, o maior exchange brasileiro e o mais confiável até onde sei. Eles possuem uma API REST muito completa que usaremos para monitorar e operar no mercado de criptomoedas. Sim, porque você não precisa apenas operar bitcoins, mas também bitcoin cash e litecoins, sendo que em breve eles devem disponibilizar bitcoin gold também.

Como esse processo de criação de conta pode mudar com o tempo, não vou entrar em detalhes aqui. Crie sua conta e faça todo o processo de validação, com selfie e tudo mais. Após o cadastro, você precisa depositar dinheiro na sua conta Mercado Bitcoin. Esse depósito inicial possui taxa, esteja ciente disso. Na verdade tudo que você for fazer possui taxa: depositar, comprar, vender e sacar. Ao todo, o ciclo completo deve lhe comer uns 5%, então só vale operar no Mercado Bitcoin se for a longo prazo (se você é um dos que acredita no futuro do bitcoin) ou quando o spread estiver alto (acima de 6%).

Após estar com a sua conta 100% aprovada, habilite todos os mecanismos de segurança (na área de configurações da sua conta) como uma senha forte, Two Factor Authentication, palavra-segura e PIN. Nem todos estes itens são necessários para fazer este tutorial, mas sugiro que faça todos eles pois estes exchanges são atacados por hackers o tempo todo e você não vai querer facilitar, não é mesmo?

Após criar o seu PIN, você pode criar uma chave de segurança que será utilizada depois para se autenticar na API de negociações do Mercado Bitcoin. O link para criação da chave é esse, mas caso não funcione, procure as instruções neste artigo deles.

Segundo passo: criar o projeto Node.js

Crie uma pasta no seu computador com o nome de mercadobitcoin e dentro dele coloque um arquivo index.js vazio e via console execute um ‘npm init’ nesta pasta para fazer as configurações iniciais de um projeto Node.js.

Vamos instalar alguns pacotes via NPM pra deixar nosso projeto preparado. Seguem os comandos de instalação:

O módulo dotenv-safe serve para carregar automaticamente variáveis de ambiente na sua aplicação Node.js. Usaremos estes variáveis de ambiente para colocar as configurações do nosso bot.

Crie um arquivo “.env.example” na raiz do seu projeto com o seguinte conteúdo dentro, representando o modelo de arquivo de configuração que vamos precisar:

Agora crie um arquivo “.env” na raiz do seu projeto usando o mesmo conteúdo do .env.example como base, mas colocando os seus valores de verdade ao lado do sinal de igualdade de cada uma das variáveis de ambiente. Uma rápida explicação de cada variável:

  • CRAWLER_INTERVAL: milissegundos entre cada execução do bot, sendo que o Mercado Bitcoin não tolera mais de 60 chamadas por minuto e pode lhe bloquear se tentar usar mais a API deles do que isso.
  • KEY: sua API Key (alfanumérico), criada no Mercado Bitcoin
  • SECRET: o seu API Secret (alfanumérico), criado no Mercado Bitcoin
  • PIN: o seu PIN (quatro números), criado nas configurações do Mercado Bitcoin
  • PROFITABILITY: a rentabilidade desejada que seu bot deve alcançar em decimal, ou seja, 1.1 representa 10%, 1.05 representa 5% e assim por diante.

Tenha em mente que as informações de KEY, SECRET e PIN são secretas e você não deve compartilhar com ninguém, caso contrário quem descobri-las poderá comprar e vender bitcoins em seu nome, usando seu dinheiro.

Sobre a variável PROFITABILITY (rentabilidade ou lucratividade), entenda que quanto maior a rentabilidade desejada, mais irá demorar para que você consiga ter retorno com seu bot e às vezes isso pode nunca acontecer. Exemplo, se sua PROFITABILITY é 1.2 (20%) e você compra bitcoin hoje, vai demorar 20 dias para o bot vender se a moeda estiver valorizando 1% ao dia. Claro que diversas técnicas podem reduzir ou aumentar esse prazo (como uma queda da moeda ao invés de valorização), e cabe a você estudá-las (como analisar os gráficos da moeda, por exemplo).

Além disso, uma PROFITABILITY inferior a 1.05 (5%) não é muito interessante pois você gasta quase isso com as taxas do Mercado Bitcoin para um ciclo completo de depósito, compra, venda e saque de criptomoedas.

Se você for versionar o seu projeto ou mesmo compartilhar o seu código com outras pessoas via Git, coloque um .gitignore na raiz do seu projeto incluindo a pasta node_modules (padrão) e o arquivo .env, que é mantido localmente só no seu projeto.

Para carregar estas configurações na sua aplicação, dentro do seu index.js na raiz do seu projeto adicione a seguinte linha de código:

Caso você tenha se esquecido de alguma variável ou o arquivo .env não esteja presente na raiz do projeto, ao chamar esta função load do dotenv-safe, sua aplicação não iniciará.

Note que cabe a você entender as melhores configurações para os seus objetivos financeiros, a sua situação financeira, a situação do mercado de criptomoedas no momento que você está atuando nele, etc. Me focarei aqui na construção do robô de compra e venda de bitcoins, não na sua configuração.

Terceiro passo: consumindo a API de dados

Existem duas APIs na maioria dos exchanges: uma de dados (somente leitura, aberta) e outra de trading (leitura e escrita, exige credenciais mais específicas). Vamos começar pela primeira, que é mais simples e é a base do monitoramento do robô.

A documentação completa e atualizada da API de dados do Mercado Bitcoin pode ser encontrada aqui. Para organizar melhor nosso código, crie um arquivo api.js na raiz do seu projeto e dentro dele vamos colocar o protótipo do nosso objeto da API de dados:

Nesta amostra de código eu carrego a biblioteca unirest para fazer as chamadas à API REST do Mercado Bitcoin e crio uma constante para armazenar o endereço da API. Crio uma função construtora que recebe um objeto de configurações por parâmetro que neste primeiro momento coloquei apenas uma variável currency que determina a moeda que estou operando no mercado, uma vez que esta informação deve ir na URL da chamada, usando a notação:

  • BTC:  Bitcoin
  • BCH: Bitcoin Cash
  • LTC: Litecoin

No protótipo do objeto Mercado Bitcoin eu  tenho as funções ticker, orderbook, trades e a função genérica call que é a base para as demais. Todas as funções esperam uma função success como callback para que o retorno seja trabalhado de alguma forma e abaixo coloco uma explicação sucinta de cada uma das funções:

  • ticker: retorna um objeto com o estado atual do mercado, incluindo o menor valor de venda da moeda no livro (sell), o maior valor de compra no livro (buy), o valor da última transação (last), o maior valor da moeda nas últimas 24h (high) e menor no mesmo período (low), além do timestamp desta informação. Estas informações são a base do monitoramento, para tomar decisões de compra e venda.
  • orderbook: retorna um array de ordens do livro, sendo as ordens de compra mais altas e as ordens de venda mais baixas abertas atualmente.
  • trades: retorna um array das últimas operações realizadas, serve para monitorar se o mercado vai cair (muitas vendas sucessivas) ou vai subir (muitas compras sucessivas).

Com esse arquivo de API pronto, podemos voltar ao nosso index.js para criar uma rotina básica de monitoramento em nosso bot, como abaixo:

Aqui eu carreguei nosso módulo de api e criei um objeto de monitoramento da moeda BTC (bitcoin), que vai rodar a cada x segundos, definidos pela variável de ambiente CRAWLER_INTERVAL (sugiro começar com 5000).

Se você rodar agora este arquivo usando o comando ‘node index’, ele já deve estar funcionando e o seu bot vai estar monitorando o mercado continuamente (até ser encerrado), embora ainda não esteja tomando ação alguma.

Na próxima parte deste tutorial, vamos lidar com a segunda API, a de trading. Até lá espero que você já tenha sua conta criada e aprovada e este monitoramento 100%. Confira a parte 2 aqui.

Um abraço!

Curtiu o post? Que tal aprender mais sobre como criar aplicações incríveis usando Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Webscrapping com Node.js: Parte 2

Este artigo é uma continuação do Webscrapping com Node.js que escrevi no início de outubro. Crawlers e webscrappers são peças fundamentais para a construção de mecanismos de busca e já há algum tempo que estava para escrever sobre eles a pedido de leitores e amigos.

Isso porque crio tais bots há uns 7 anos, desde que comecei o projeto do Busca Acelerada pela primeira vez e de lá para cá não parei, tendo lançado diversos projetos para diversas empresas (como o BuildIn para a Softplan), e até mesmo alguns projetos pessoais (como o SóFamosos).

Mas por que uma parte 2? Não ensinei tudo que tinha de ensinar na parte 1?

O grande problema do webscrapping

Quase. O webscrapper que ensinei a criar na primeira parte deste artigo é bem básico e sofre de alguns males bem comuns que quem já deve ter feito webscrapping antes deve conhecer: HTML dinâmico e execução de scripts.

Claro, existem outros problemas também, como o bloqueio do bot após crawling excessivo, mas considerando que quase a totalidade dos sites atuais possuem execução de scripts para tornar suas páginas dinâmicas, é quase certo que você mais cedo ou mais tarde terá problemas com o seu webscrapper enxergando um HTML x e você no navegador enxergando um HTML y, o que dificultará seu trabalho de capturar os dados corretamente.

Explicando melhor: quando você acessa um site via servidor, usando um script como o webscrapper do post anterior, você não executa o código front-end da página, não manipula o DOM final, não dispara as chamadas Ajax, etc. O seu robô apenas lê as informações que foram geradas no servidor do site-alvo, o que pode fazer com que seu robô nem tenha nada de útil pra ler na página caso a mesma se construa usando Ajax.

A solução: Headless browsers

Para contornar este problema usamos um tipo especial de browser chamado headless (“sem cabeça”).

Os browsers web como conhecemos possuem 3 tarefas principais:

  • dada uma URL, acessar uma página HTML (funcionalidade principal, o tronco do browser);
  • carregar o DOM e executar os scripts front-end (os membros);
  • renderizar visualmente o DOM (a cabeça);

Os primeiros browsers antigos, que eram em modo texto, só faziam o primeiro item. O browser que você está usando pra ler este post faz os três. Um headless browser faz somente os dois primeiros, ou seja, ele apenas não possui uma interface gráfica, mas carrega o HTML, o DOM e executa todos os scripts de front-end.

Esse tipo de browser é como se fosse um console application rodando em modo silencioso, apenas um processo no seu sistema operacional que age como um browser. No fundo ele é um browser, apenas não tem UI.

Muito útil para testes automatizados de interface, é comum os headless browsers exporem bibliotecas de manipulação do DOM como JQuery para permitir que através de scripts o programador consiga usar a página carregada pelo headless browser, como executar cliques em botões, preencher campos, etc; tudo isso sem estar enxergando uma UI ou sequer estar usando mouse e teclado.

Não é preciso ser um gênio para entender que podemos usar headless browsers para fazer webscrapping, certo? Com esse tipo de tecnologia conseguiremos que nosso bot leia a versão final do HTML, da mesma forma que um usuário “veria” no navegador, após a execução dos scripts de front-end. Não apenas isso, mas você consegue fazer com que seu bot não apenas leia conteúdo, mas que use a página em si, clicando em botões e selecionando opções, por exemplo.

Se DOM, HTML, front-end e outros termos utilizados aqui não forem de seu domínio, dificilmente você conseguirá criar um crawler realmente bom. Em meu livro de Programação Web com Node.js, cubro estes e outros aspectos essenciais da programação web, caso te interesse.

Escolhendo um headless browser

Existem diversas opções de headless browsers no mercado. A maioria é open-source e baseada no Webkit, a mesma base na qual o Apple Safari e o Google Chrome foram construídos, o que garante uma grande fidelidade em relação ao uso de web browser comuns para carregar o HTML+JS.

Algumas opções disponíveis são:

Destes, o SlimerJS usa a plataforma Gecko da Mozilla ao invés do Webkit, enquanto que o PhantomJS e o HtmlUnit são famosos por se integrarem ao Selenium, uma popular ferramenta de automação de testes. Além disso, o Phantom é o único que eu usei profissionalmente em uma startup que trabalhei que usava este headless browser para tirar screenshots de páginas web.

No entanto, meu intuito de escrever este post era justamente para aprender a usar outro headless browser que não o Phantom, pois este possui alguns problemas conhecidos como não-conformidade com ES6, obrigatoriedade de uso do JQuery e nenhuma novidade desde 2016 (o projeto está parado). Esses problemas (que não são o fim do mundo, mas que me motivaram a buscar outra tecnologia) são comentados neste excelente post do Matheus Rossi. Então se você realmente quer aprender a usar o PhantomJS, leia o post que acabei de citar.

Uma coisa que descobri recentemente é que desde a versão 59 do navegador Google Chrome que foi disponibilizada a opção de rodar o navegador do Google sem a interface gráfica funcionando, em modo headless. Usar o Chrome em modo headless é o sonho de muito testador por aí, pois representa rodar scripts de testes automatizados no navegador mais popular do mundo, com a maior fidelidade possível em relação ao que o usuário final vai usar.

Certo, headless browser escolhido, como fazer um webscrapper que use-o para carregar o mesmo DOM que um usuário do Chrome veria?

Manipulando o headless browser via Node.js

No mesmo Github do Google Chrome existe um projeto chamado Puppeteer, que em uma tradução livre seria um manipulador de marionetes, ou um mestre dos fantoches. Ele é essencialmente um módulo Node.js que permite manipular tanto o Headless Chrome quanto o Chrome “normal” para fazer tarefas como:

  • gerar screenshots e PDFs de páginas web;
  • fazer crawling em cima de páginas com Ajax e SPAs;
  • automatizar o uso de UIs web;
  • fazer webscrapping com alta fidelidade;

Para que o Puppeteer funcione você precisa ter o Node.js versão 8+ instalado na sua máquina. Depois que tiver o Node instalado, vamos criar um novo projeto Node.js chamado webscrapper2, criar um index.js vazio dentro dele, navegar via terminal até essa pasta e rodar um ‘npm init’ para configurá-lo com as opções default.

Se você nunca programou em Node.js antes, sugiro começar pelos tutoriais mais iniciantes de meu blog ou mesmo pelo meu livro, que pega o iniciante pela mão e ensina desde JS tradicional até diversos módulos do Node.

Agora rode o comando abaixo para instalar o Puppeteer no seu projeto, sendo que ele automaticamente irá baixar sua própria versão do Chrome, independente se você já possuir uma (ou seja, a instalação irá demorar um pouco mais que o normal para módulos NPM):

Usá-lo é muito simples, no seu index.js, inclua o seguinte código que representa a estrutura básica do nosso webscrapper:

Na primeira linha carregamos a dependência do módulo puppeteer. No bloco seguinte, criamos a função assíncrona que fará todo o trabalho de verdade, com a lógica de acesso à página e scrapping (comentado).

Neste primeiro momento, essa função scrape apenas executa o Headless Chrome, acessa uma nova página com a URL de um site fake de livros e espera 1 segundo antes de se fechar. Ainda não há a lógica de scrapping aqui.

No bloco final, executamos a função e quando ela terminar, o valor retornado pela função scrape será impresso no console.

Antes de fazer a versão completa da função scrape, temos de entender o que vamos capturar de informações da página em questão.

Fazendo HTML Scrapping

O site Books to Scrape serve para o único propósito de ajudar desenvolvedores a praticar webscrapping. Ele mostra um catálogo de livros, como na imagem abaixo:

Books to Scrape
Books to Scrape

Acesse ele no seu navegador Chrome e com o clique direito do mouse sobre a área do primeiro livro da listagem, mais especificamente sobre o título dele e selecione a opção Inspecionar/Inspect.

Isso irá abrir a aba Elements do Google Developer Tools onde você poderá entender como construir um seletor que diga ao seu web scrapper onde estão os dados que ele deve ler. Neste caso, vamos ler apenas os nomes dos livros. Se você não está acostumado com CSS e/ou seletores (no melhor estilo JQuery), você pode usar o recurso Copy > Selector que aparece no painel elements para gerar o seletor para você, como mostra a imagem abaixo.

Copiando o selector
Copiando o selector

Nesse caso específico não usarei a sugestão do Chrome que é bem engessada. Usarei um seletor bem mais simples: “h3 > a”, ou seja, vou pegar o link/âncora de cada H3 do HTML dessa página. Os H3 dessa página são exatamente os títulos dos livros.

Se analisarmos em detalhes este bloco que queremos extrair o título, veremos que o texto interno da âncora nem sempre é o título fiel pois, por uma questão de espaço, algumas vezes ele está truncado com reticências no final. Sendo assim, o atributo HTML que vamos ler com o scraper é o title da âncora, que esse sim sempre possui o título completo.

Vamos atualizar nosso código de scrapping para pegarmos esta informação:

Analisando apenas o bloco de código de scrapping, criamos uma função que executa um código JavaScript sobre a página que carregamos. A execução de scripts é realizada usando a função evaluate. Nesse código JavaScript, estamos usando a função querySelector no documento e o seletor que criamos anteriormente para acessar um componente do DOM da página. Sobre este componente, queremos o seu atributo title.

Se você rodar este código Node agora verá que ele retornará apenas o nome do primeiro livro, “A Light in the Attic”. Isso porque a função querySelector retorna apenas o primeiro componente que ela encontra com aquele seletor.

O código abaixo resolve isso:

Aqui, usei a querySelectorAll, sobre a qual apliquei um forEach. Para cada livro encontrado com aquele seletor, eu adiciono o title do livro em um array de livros, retornado ao fim do evaluate.

Com isso, se você executar novamente este script verá que ele retorna o título de todos os livros da primeira página do site, concluindo este post.

Qual seria o próximo passo? Navegar pelas demais páginas, coletando os títulos dos outros livros. Esta é a tarefa principal de um webcrawler ou webspider, to crawl (rastejar). Ao algoritmo de webscrapping cabe apenas coletar os dados.

Até a próxima!

Curtiu o post? Que tal aprender mais sobre como criar aplicações incríveis usando Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!