Web scraping com python —Selenium e Javascript

Henrique Coura
Jul 30, 2017 · 7 min read

Como utilizar o selenium para fazer scraping do twitter e outras páginas com Javascript.

Muitas vezes quando estamos realizando projetos de scraping esbarramos com um problema, páginas dinâmicas com conteúdo carregado por JavaScript, de forma que pelo HTML em si não conseguimos tudo que queremos.

Nesse artigo vou ensiná-los justamente como resolver esse problema, utilizando o selenium e o Chrome em modo headless para poder renderizar a página alvo tal qual vemos no nosso browser e podermos extrair tudo que queremos.

Esse artigo faz parte de uma série de artigos sobre scraping em python:

  1. Web scraping com python — Extraindo Dados de um Ecommerce
  2. Web scraping com python — Introdução ao Scrapy
  3. Web scraping com python — Selenium e Javascript

O código desse projeto se encontra no github: https://github.com/hcoura/twitter_crawler

Descrição do projeto

Como de costume, vamos fazer um projetinho de mundo real para exemplificar um pouco do que é possível fazer com o selenium.

Nesse projeto a partir de uma lista de palavras será possível:

  • Extrair o nome, username e tweet para as últimas 100 postagens dessa palavra no twitter;
  • Uma screenshot da página de cada busca;
  • Salvar essas informações localmente.

Preparando o ambiente

Caso queira acompanhar o tutorial enquanto escreve seu próprio código, você pode criar um ambiente, desde que tenha o anaconda instalado, assim:

pipenv install jupyter notebook lxml selenium

Se optar por clonar o repositório e rodar localmente:

git clone git@github.com:hcoura/twitter_crawler.git
pipenv install # instalar as dependências e criar o ambiente virtual

E ative o ambiente:

# Ativa o ambiente criado
pipenv shell

Se você não possui o pipenv instalado, veja aqui mais instruções: https://medium.com/@henriquecoura_87435/webscraping-com-python-extraindo-dados-de-um-ecommerce-89c16b622f69#5783

O Browser

Para o scraping de páginas com javascript precisamos também de um browser para renderizar o DOM e permitir que o javascript quando executado possa manipulá-lo injetando novos conteúdos na página. O que não precisamos é de uma interface gráfica, que exigem muitos recursos computacionais e tornam o scraping mais lento. Para isso utilizamos navegadores headless, que nada mais são que browsers que fazem tudo, exceto a renderização gráfica do dom.

Tendo esse browser o selenium nos permite conectar à ele e executar uma série de ações de forma automatizada sendo muito utilizado também para testes de integração em desenvolvimento Web.

Até pouco tempo atrás eu utilizava o phantomJS como meu browser, mas na versão 59 do Chrome eles liberaram o modo headless tornando possível, ou melhor mais eficiente, a utilização do Chrome para o scraping. Inclusive o criador do phantomJS após essa notícia decidiu não mais manter o projeto.

Mas enfim, para terminar de preparar o ambiente precisamos de duas coisas:

  1. O chrome instalado em uma versão igual ou superior à 59;
  2. O ChromeDriver — que é utilizado pelo selenium para se conectar ao chrome.

Para instalar o ChromeDriver acesse https://sites.google.com/a/chromium.org/chromedriver/downloads e baixe a versão mais recente do seu sistema operacional.

Após isso descompacte e inclua a localização na sua variável PATH, em linux:

unzip chromedriver_linux64.zip
chmod +x chromedriver

sudo mv -f chromedriver /usr/local/share/chromedriver
sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
sudo ln -s /usr/local/share/chromedriver /usr/bin/chromedriver

Pronto agora temos tudo para começarmos a brincar!

Selenium

O selenium, como descrito por seus criadores, simplesmente automatiza browsers. No contexto de web scraping nós o utilizamos para conectar ao browser(através do webdriver) que será responsável para a renderização da página alvo e posteriormente para enviar comandos à esse browser.

Vamos então aprender como utilizar essa lib para nossos propósitos, recomento acompanhar o código aqui com o jupyter notebook no repositório ou criando um próprio para você explorar um pouco além do que estou mostrando aqui.

Primeiro importamos o webdriver e criamos um objeto de opções para o Chrome, com isso buscamos nossa página inicial, o feed de tweets para palavra javascript:

from selenium import webdriveroptions = webdriver.ChromeOptions()
options.add_argument(‘headless’)
options.add_argument(‘window-size=1920x1080’)
driver = webdriver.Chrome(chrome_options=options)
driver.get("https://twitter.com/search?f=tweets&vertical=default&q=javascript")

Note que driver.get(url) inicialmente carrega a página toda como no seu browser, não só o html, como seria o caso de utilizarmos requests ou urllib para buscar essa página.

Inicialmente já temos alguns tweets carregados, podemos encontrá-los de várias formas, uma delas é com o método find_elements_by_xpath(), na documentação você pode ver outros métodos:

tweets = driver.find_elements_by_xpath(“//li[@data-item-type=’tweet’]”)

Quando eu rodei esse código len(tweets) me retornou 13, ou seja, a página carregou com 13 tweets, mas eu quero mais que isso, quero 100 tweets.

Ao acessar a página do twitter, percebe-se que ela possui infinite scrolling, ou seja, ao chegar ao fim da página mais tweets são carregados. Uma forma que podemos lidar com isso seria enviar um código de javascript fazendo o scroll do browser como abaixo.

driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”)
tweets = driver.find_elements_by_xpath(“//li[@data-item-type=’tweet’]”)
len(tweets) # Ainda retorna 13

Ao rodar esse código len(tweets) ainda retornará 13, por que isso? A verdade é que ao fazer o scroll a página fez uma requisição para mais tweets, mas o selenium não bloqueia o código até a resposta chegar. Se agora após algum tempo tentarmos encontrar os tweets novamente encontraremos outro valor, no meu caso 29:

tweets = driver.find_elements_by_xpath(“//li[@data-item-type=’tweet’]”)
len(tweets) # Agora o valor é outro, 29 no meu caso

Beleza, mas como esperar por isso para continuar o código? Quanto tempo devo esperar? Tem alguma forma mais eficiente de fazer isso?

Waits (Espera)

O selenium possui dois tipos de formas de esperar por algo:

  1. Espera implícita — Você determina quantos segundos você quer esperar e passa para o webdriver. por exemplo driver.implicitly_wait(2) esperaria por 2 segundos;
  2. Espera explícita — Onde o driver vai bloquear o prosseguimento do código até que uma dada condição seja verdadeira ou demore demais.

Nós vamos utilizar nesse projeto a espera explícita por ser mais previsível e mais eficiente, ou seja, o código ficará bloqueado até novos tweets serem carregados na página e nem um tempo a mais ou estourar o tempo máximo de espera(timeout).

Como você pode ver no código acima WebDriverWait() aceitar um webdriver como primeiro parâmetro e o tempo máximo de espera(10 segundos) como segundo parâmetro. Já a função until() espera uma função de callback que recebe o driver como parâmetro que será chamada várias vezes e deve retornar True quando a condição de espera for satisfeita e False enquanto não tiver sido.

No caso, uso uma função lambda para passar para new_posts() além do driver o tamanho da lista de tweets. Com isso, o código fica bloqueado até driver.find_elements_by_xpath(“//li[@data-item-type=’tweet’]”) retornar uma lista com mais tweets que o número atual.

Se você rodar localmente verá que dessa vez o número de tweets é atualizado, no meu caso len(tweets) retornou 45.

Agora para extrair pelo menos 200 tweets poderíamos criar um loop, por exemplo:

Após rodar o loop, tweets será uma lista com mais de 200 tweets.

Vale notar que, algumas condições de espera são tão comuns que a biblioteca do selenium nos fornece algumas, no caso ela não possui uma para “novos elementos” mas dependendo do seu projeto recomendo utilizá-las. Você pode ver todas elas na documentação: Expected Conditions

Screenshot

Uma outra coisa muito legal que é possível é salvar uma screenshot do estado atual da página, isso é feito através do método save_screenshot() do webdriver:

driver.save_screenshot(“current.png”)

No entanto, como fomos fazendo scroll para carregar a página, precisamos fazer scroll de volta ao topo para tirar uma screenshot do início da página.

driver.execute_script(“window.scrollTo(0, 0);”)
driver.save_screenshot(“top.png”)

Bem legal não é?

Extração de tweets

Já falei muito sobre extrair informações de um html nos outros artigos, então não vou entrar muito a fundo nisso. O código abaixo extrai o primeiro tweet:

# output de extracted_tweet
{'fullname': 'TrabajaCuandoQuieras',
'tweet': 'Programadores Java /\xa0Javascript http://trabajacuandoquieras.com/job/programadores-java-javascript-2/',
'username': '@TrabajaQ'}

Agora podemos até conferir na screenshot que extracted_tweet é o primeiro tweet dessa página :D

Só tem duas coisas importantes de se notar aqui:

  • Apesar de ser possível extrair essa mesma informação direto no selenium(sem utilizar o lxml) é muito mais lento.
  • As expressões xpath possuem um ponto no início(“.//…”) que quer dizer que são relativas ao elemento em que estão sendo chamadas e não em relação ao html todo.

Código Final

Agora temos as informações necessárias para escrever o crawler completo.

Esse código não tem quase nada de novo fora o que já vimos. Apenas lógica para salvar localmente as informações em um csv e tratamento de exceções para quando o nome completo do usuário não estiver disponível ou quando der timeout no WebDriverWait().

Por fim é isso, apenas 107 linhas, bem impressionante não?

Conclusão

Espero que tenha conseguido mostrar um pouco sobre como o selenium pode ser poderoso e nos ajudar bastante nos projetos de scraping.

O selenium porém, não substitui outras ferramentas importantes, por exemplo aqui uso selenium + lxml por mais eficiência e também é possível utilizá-lo com o scrapy. E se você não precisa se comportar como um browser durante o scraping existem outras formas mais eficientes de fazer o scraping.

Se eu te ajudei de alguma forma não se esqueça de clicar no 💚 abaixo.

Henrique Coura

Written by

Desenvolvedor full-stack, empreendedor e interessado por machine learning, data science e web scraping

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade