Índice
Introdução
Selenium automates browsers. That's it! What you do with that power is entirely up to you. Primarily it is for automating web applications for testing purposes, but is certainly not limited to just that. Boring web-based administration tasks can (and should) also be automated as well.
Essa frase foi retirada diretamente do site oficial do Selenium. Traduzindo, o Selenium começou como um framework para testes funcionais de aplicações web, porém com o passar do tempo foi amplamente utilizado para automatizar tarefas como acessar e coletar informações de páginas web.
Neste artigo, vamos abordar esse aspecto de automação e webscraping em um exemplo prático, onde vamos buscar informações sobre ações no site Fundamentus e montar um arquivo CSV que possa ser importado em qualquer lugar (banco de dados, Google Sheets, etc.)
Arquitetura
Para começar precisamos entender como funciona a arquitetura do selenium que está dividida em três partes.
- Cliente: é uma biblioteca específica para uma determinada linguagem feita para interagir com o selenium
- Driver: é o agente que irá permitir a interação entre o cliente e o browser de fato
- Browser: é o ambiente onde a página será renderizada e executada.
Instalações
Neste exemplo, vamos utilizar a linguagem python, então precisamos baixar duas coisas, a biblioteca oficial do selenium para python e o driver correspondente ao browser que estamos utilizando.
A instalação da biblioteca é bem simples:
pip install selenium
(ou você pode usar o poetry)
A instalação do driver pode ser um pouco complicada pois a versão dele deve ser idêntica a versão do browser instalado na máquina, então para facilitar, é possível utilizar a imagem docker do selenium.
version: '3.8'
services:
selenium-chrome:
image: selenium/standalone-chrome:latest
hostname: selenium-chrome
networks:
- selenium-network
privileged: true
shm_size: 2g
ports:
- "4444:4444"
- "7900:7900"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7900"]
interval: 10s
timeout: 10s
retries: 5
app:
build: ./
volumes:
- /Users/geraldoferraz/repositorios/blog/selenium-with-python:/tmp/
networks:
- selenium-network
depends_on:
selenium-chrome:
condition: service_healthy
networks:
selenium-network:
driver: bridge
Site Fundamentus
Agora que temos tudo que precisamos, vamos analisar a página do Fundamentus e entender onde estão as informações que queremos coletar. Vamos acessar a página de análise da ação da Magazine Luiza (MGLU3). Para isso, acesse este link e você verá uma página semelhante a essa:
Inspect do Chrome
A forma mais fácil de localizar um elemento é utilizando o XPATH. Vamos utilizar a ferramenta de inspeção do browser para recuperar o xpath das informações de “Cotação”, “Min 52 sem”, “Max 52 sem”, “Div. Yield” e a Oscilação “Dia”.
- Encontre a informação desejada, clique com o botão direito do mouse e selecione a opção “inspect”
- Na ferramenta de inspeção que se abriu, no item selecionado, clique com o botão direito do mouse e selecione a opção “Copy”→ “Copy full xpath”
Hands-on
Agora que sabemos como chegar no nosso elemento, vamos ver o código necessário para realizar a coleta.
Primeiro, a partir do driver, precisamos construir o objeto que irá representar o browser.
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
def main():
option = webdriver.ChromeOptions()
s = Service('/Users/geraldoferraz/Downloads/chromedriver')
navegador = webdriver.Chrome(service=s, options=option)
#para trabalhar dentro do docker
#navegador = webdriver.Remote('http://selenium-chrome:4444/wd/hub', options=option)
if __name__ == '__main__':
main()
Segundo, precisamos carregar a página desejada e utilizar o XPATH que obtivemos no passo anterior para encontrar as informações que queremos.
navegador.get("<https://fundamentus.com.br/detalhes.php?papel=MGLU3>")
try:
xpath_valor_atual = '/html/body/div[1]/div[2]/table[1]/tbody/tr[1]/td[4]/span'
valor_atual = navegador.find_element(By.XPATH, xpath_valor_atual).text
print(f'valor atual={valor_atual}')
finally:
navegador.close()
A lib do selenium possui diversas formas de encontrar elementos na página sendo o XPATH o mais preciso:
- By.ID
- By.XPATH
- By.NAME
- By.TAG_NAME
- By.CLASS_NAME
- By.CSS_SELECTOR
Você deve escolher a melhor forma de acordo com a sua lógica. Para o nosso exemplo, o XPATH será suficiente.
No exemplo a seguir, vamos ver como pegar todas as informações que precisamos de um conjunto de ações:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
def main():
option = webdriver.ChromeOptions()
s = Service('/Users/geraldoferraz/Downloads/chromedriver')
navegador = webdriver.Chrome(service=s, options=option)
#para trabalhar dentro do docker
#navegador = webdriver.Remote('http://selenium-chrome:4444/wd/hub', options=option)
acoes = ['MGLU3', 'LREN3', 'BBAS3', 'ITSA4']
try:
for acao in acoes:
navegador.get(f"https://fundamentus.com.br/detalhes.php?papel={acao}")
xpath_valor_atual = '/html/body/div[1]/div[2]/table[1]/tbody/tr[1]/td[4]/span'
xpath_min = '/html/body/div[1]/div[2]/table[1]/tbody/tr[3]/td[4]/span'
xpath_max = '/html/body/div[1]/div[2]/table[1]/tbody/tr[4]/td[4]/span'
xpath_yield = '/html/body/div[1]/div[2]/table[3]/tbody/tr[9]/td[4]/span'
xpath_valorizacao = '/html/body/div[1]/div[2]/table[3]/tbody/tr[2]/td[2]/span/font'
valor_atual = navegador.find_element(By.XPATH, xpath_valor_atual).text
min = navegador.find_element(By.XPATH, xpath_min).text
max = navegador.find_element(By.XPATH, xpath_max).text
_yield = navegador.find_element(By.XPATH, xpath_yield).text
valorizacao = navegador.find_element(By.XPATH, xpath_valorizacao).text
print(f'valor atual={valor_atual}, min={min}, max={max}, min={_yield}, min={valorizacao} ')
finally:
navegador.close()
if __name__ == '__main__':
main()
A parte mais simples é gerar o CSV
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import csv
def main():
option = webdriver.ChromeOptions()
utilize esse formato para trabalhar localmente
s = Service('/Users/geraldoferraz/Downloads/chromedriver')
navegador = webdriver.Chrome(service=s, options=option)
#para trabalhar dentro do docker
#navegador = webdriver.Remote('http://selenium-chrome:4444/wd/hub', options=option)
acoes = ['MGLU3', 'LREN3', 'BBAS3', 'ITSA4']
try:
with open('acoes.csv', 'w', newline='') as csvfile:
cabecalho = ['valor_atual', 'min', 'max', 'yield', 'valorizacao']
writer = csv.DictWriter(csvfile, delimiter=',', fieldnames=cabecalho)
writer.writeheader()
for acao in acoes:
navegador.get(f"https://fundamentus.com.br/detalhes.php?papel={acao}")
xpath_valor_atual = '/html/body/div[1]/div[2]/table[1]/tbody/tr[1]/td[4]/span'
xpath_min = '/html/body/div[1]/div[2]/table[1]/tbody/tr[3]/td[4]/span'
xpath_max = '/html/body/div[1]/div[2]/table[1]/tbody/tr[4]/td[4]/span'
xpath_yield = '/html/body/div[1]/div[2]/table[3]/tbody/tr[9]/td[4]/span'
xpath_valorizacao = '/html/body/div[1]/div[2]/table[3]/tbody/tr[2]/td[2]/span/font'
valor_atual = navegador.find_element(By.XPATH, xpath_valor_atual).text
min = navegador.find_element(By.XPATH, xpath_min).text
max = navegador.find_element(By.XPATH, xpath_max).text
_yield = navegador.find_element(By.XPATH, xpath_yield).text
valorizacao = navegador.find_element(By.XPATH, xpath_valorizacao).text
writer.writerow({'valor_atual': valor_atual, 'min': min, 'max': max,
'yield': _yield, 'valorizacao': valorizacao})
finally:
navegador.close()
if __name__ == '__main__':
main()
Atenção!
- Ao executar o programa, você deve ter notado que um browser se abriu. Para o desenvolvimento, pode ser interessante acompanhar visualmente tudo o que está acontecendo, assim podemos pegar alguma falha na nossa lógica, porém em um ambiente de produção, provavelmente não teremos um ambiente visual para que um browser seja aberto, então é interessante adicionar a configuração abaixo na construção do browser.
option.add_argument('headless')
- Algumas vezes, o site pode ter algum tipo de proteção contra robôs, que podem prejudicar a performance do site, então, precisamos ficar atentos a isso, caso contrário pode ser que o nosso robô não funcione. Não é o escopo desse artigo mostrar como contornar esse cenário, mas saiba que é possível, basta realizar uma pesquisa rápida no google.
- Para trabalhar dentro do docker, basta descomentar a linha abaixo e executar o docker-compose.yml
navegador = webdriver.Remote('http://selenium-chrome:4444/wd/hub', options=option)
Conclusão
O selenium é uma ferramenta extremamente versátil que permite automatizar diversas tarefas. A criatividade é o limite.