Web Scraping com Golang
Recentemente precisei extrair algumas informações de um site, meu plano era simples, verificar a URL que o formulário de pesquisa apontava e fazer a requisição diretamente pelo meu programa, daí usar o package goquery fazer parser dos resultados e pegar os dados que eu queria.
Claro que não foi fácil assim. O site que eu queria crawlear era repleto de JavaScripts que interferiam com o formulário, com os cookies e com basicamente tudo.
Não havia nenhuma senha nem captcha, bastava preencher o formulário de pesquisa e pegar os dados, mas fazer isso diretamente como eu planejava era impossível.
Então fiquei procurando formas de fazer isso simulando um navegador, minha ideia era usar alguma coisa como Selenium, mas eu não estava nem um pouco contente com essa ideia, seria mais um software para lidar e aprender o funcionamento.
Mas enquanto procurava acabei esbarrando no chromedp, um package Golang que usa Chrome DevTools Protocol sem nenhuma dependência além do próprio Chrome.
O melhor de tudo, com uma API fácil de usar e entender.
Basicamente você controla o navegador dizendo onde clicar, quais campos preencher e quando deve esperar até que alguma coisa aconteça como a carga de outra página, por exemplo.
Não é de bom-tom mostrar o site original que me deu tanto trabalho aqui, mas fiz um exemplo que vai até o manual do package time, no exemplo da data, executa o exemplo, espera o resultado e captura apenas o retorno do script.
Chrome DevTools Protocol
A primeira coisa a fazer é criar um contexto com uma função cancel
. Isso é importante para fazer shutdown do Chrome quando o programa terminar. Se você simplesmente abortar a execução ou retornar um Panic sem executar a função cancel acabará com várias instâncias headless do Chrome.
Ao mesmo tempo, podemos configurar uma função de testes com chromedp.WithLogf
, qualquer função com a interface de Printf
servirá.
ctx, cancel := chromedp.NewContext(
context.Background(),
chromedp.WithLogf(log.Printf),
)
defer cancel()
Em seguida passamos o contexto e as ações que queremos executar no navegador para a função chromedp.Run
como no exemplo abaixo.
var result string
err := chromedp.Run(ctx,
chromedp.Navigate(`https://golang.org/pkg/time/#example_Date`),
chromedp.WaitVisible(`body > footer`),
chromedp.Click(`
#example_Date >
div.expanded >
div >
div.buttons >
button.Button.Button--primary.run`,
chromedp.NodeVisible),
chromedp.WaitVisible(`
#example_Date >
div.expanded >
div >
div.output >
pre >
span.system`),
chromedp.Text(`
#example_Date >
div.expanded >
div >
div.output >
pre >
span.stdout`,
&result),
chromedp.Stop(),
)
if err != nil {
log.Println(err)
return
}
Como ultima ação eu pedi para o navegador parar de carregar qualquer recurso com a função chromedp.Stop()
, acredito que isso não é necessário já que em seguida eu ia terminar o programa, mas pareceu uma boa prática.
Por fim foi só tratar os erros como de costume. E pegar o resultado na variável result.
log.Printf("result %q", result)
Coloquei o código-fonte gist para você testar.
Poder usar bots para navegar na internet e automatizar tarefas é bem interessante, outra coisa bem legal é automatizar os testes de aplicações web simulando o comportamento dos usuários. O chromedp consegue inclusive tirar print screens que ajuda bastante na hora de depurar.
Um livro muito útil para quem está interessado em web scraping é o Web Scraping com Python.