Ir para o conteúdo

Lab 1.5: Desenvolvendo automações Web com DOM

Automações Web são limitadas ao escopo do navegador. Com isso, apesar de não ser possível interagir com elementos fora desse contexto, podemos interagir com o navegador em maior profundidade.

Podemos manipular mouse e teclado como na automação Desktop, além disso, podemos também executar código em JavaScript e interagir com elementos da DOM.

Automações Web também podem ser executadas em modo Headless (plano de fundo) e em paralelo com outras atividades sem interromper ou bloquear recursos de outras tarefas.

BotCity Web Inspector

Para esse treinamento, vamos usar a ferramenta BotCity Web Inspector.

Com essa ferramenta conseguimos navegar, inspecionar elementos de páginas web e gerar a base do código Python, conforme as configurações desejadas.

Uso básico

A ferramenta tem algumas funcionalidades e configurações, os principais são:

  • Page URL:

    Nesse campo passamos a URL da página que será acessada para inspeção.

  • Botão Start:

    Pressionando o botão Start, a ferramenta entra em modo de inspeção, abrindo o navegador na página definida, o cursor do mouse pode ser utilizado para selecionar os elementos da aplicação, veja que uma borda vermelha é exibida ao redor do elemento para identificação.

    Nesse momento clique no elemento ou pressione o atalho Alt + C para que o elemento seja inspecionado, mapeando ele.

    Você pode navegar e mapear todos os elementos antes de finalizar.

  • Botão Stop:

    Pressionando o botão Stop será finalizado o mapeamento de elementos e o navegador encerrado.

  • Botão Reset:

    Pressionando o botão reset, a ferramenta volta ao estado inicial, limpa os elementos e URL.

  • Janela Mapped Elements:

    Exibe os elementos mapeados, ou seja, os elementos que foram selecionados e que serão utilizados para gerar o código. Também é possível selecionar os diferentes elementos selecionados para visualizar as informações ou excluí-los no ícone de lixeira.

  • Janela Property/Value:

    Exibe as propriedades e valores do elemento selecionado, como HTML Element, Tag Name, Id, etc. Esses valores podem servir para refatorar o código gerado, deixando-o mais resiliente.

  • Janela Code:

    Nessa janela podemos selecionar algumas propriedades para gerar o código, como: Seleção de navegador, se deve gerar código para configurar o navegador ou iniciar o navegador.

  • Botão Generate Code:

    Pressionando o botão Generate Code, o código será gerado com base nos elementos e configurações selecionadas.

Alternativa

Como alternativa, pode ser utilizada a inspeção de elementos manualmente no navegador. Abrindo o navegador e pressionando a tecla F12.

Inspector

Coletando informações de canais de YouTube

Para esse Lab, vamos desenvolver uma automação web que acessa canais de YouTube e coleta informações sobre cada um deles, ao final mostra na tela as informações de nome do canal, número de inscritos e quantidade de vídeos.

Criando um projeto usando o template BotCity

A BotCity oferece templates de projeto que podem ser criados e customizados através do cookiecutter.

Para instalarmos o pacote do cookiecutter, vamos executar o seguinte comando:

python -m pip install --upgrade cookiecutter

Para criarmos um projeto usando o modelo, vamos invocar o cookiecutter e fornecer como argumento a URL do repositório onde os templates da BotCity estão localizados:

python -m cookiecutter https://github.com/botcity-dev/bot-python-template/archive/v2.zip

O sistema solicitará algumas respostas para criar seu projeto:

  • project_type: 2 | Tipo de projeto Web.
  • bot_id: BotYoutube | Nome do projeto.

Após o término do processo acima, uma nova pasta chamada BotYoutube estará disponível.

Sucesso

Parabéns, você agora tem um projeto com o framework Python da BotCity. 🏆

Entenda o que são os arquivos no projeto de exemplo

Ao obter o projeto, uma pasta com o nome BotYoutube é criada. Ao abrir a pasta, há os seguintes arquivos:

    BotYoutube
    ├── bot.py              <- Arquivo onde iremos trabalhar e desenvolver nosso robô.
    ├── resources           <- Pasta contendo os arquivos auxiliares para o robô.
    ├── build.bat           <- Script em Batch para gerar o pacote.
    ├── build.ps1           <- Script em PowerShell para gerar o pacote.
    ├── build.sh            <- Shell script para gerar o pacote.
    ├── requirements.txt    <- Arquivo descrevendo todas as dependências externas para seu robô.
    └── BotYoutube.botproj  <- Arquivo utilizado para carregar o projeto no BotStudio.

Finalmente, temos um modelo de projeto Web criado. Nos próximos passos vamos desenvolver o projeto através de inspeção de elementos de páginas web.

Baixando o WebDriver

Para trabalhar com automações web, precisamos usar o WebDriver correspondente ao navegador que está sendo utilizado na automação.

Utilizaremos o navegador Firefox para essa automação, portanto precisamos baixar o Geckodriver disponível deste link.

Você pode utilizar outros navegadores, como:

Configurando o caminho do WebDriver

No arquivo principal bot.py do template, vamos fazer a configuração do Geckodriver para esse projeto alterando as seguintes linhas:

  • bot.browser: Seleciona o navegador Chrome
  • bot.driver_path: Informa o caminho do Webdriver (para facilitar coloque dentro da pasta resources)
...
def main():
    bot = WebBot()

    # Configurando para rodar no modo headless
    bot.headless = False

    # Setando navegador padrão para o Chrome
    bot.browser = Browser.FIREFOX

    # Setando o caminho do WebDriver do Chrome
    bot.driver_path = r"resources\geckodriver.exe"
...

Sucesso

Toda essa informação é bem legal, mas é hora de vermos alguma ação.

Vamos testar nosso robô localmente. 🦾🤖

Criando ambiente isolado

Como boa prática, criaremos um ambiente virtual isolado para essa automação, evitando problema como diferentes versões de dependências. Para isso abra o terminal na pasta do projeto e use os seguintes comandos:

Cria o ambiente:

python -m venv venv

Ativa o ambiente:

venv\Scripts\activate

Cria o ambiente:

python -m venv venv

Ativa o ambiente:

source venv/bin/activate

Instalando as dependências no projeto

Vamos fazer a instalação das dependencias listadas no arquivo requirements.txt dentro do ambiente isolado com o seguinte comando:

python -m pip install -r requirements.txt

Vamos abrir o inspector para começar a navegar e capturar os elementos necessários, precisamos das seguintes informações:

  • Nome do canal
  • Número de inscritos
  • Quantidade de vídeos

Coletando as informações da página

Para abrir o Inspector, clique no ícone da extensão BotCity no VSCode, e em seguida, clique no botão Launch Web Inspector.

Inspector

Com a janela aberta, insira a URL que deseja inspecionar e clique em Start para iniciar o navegador.

  • url: https://www.youtube.com/@botcity_br

Ficará semelhante à imagem abaixo:

Inspector-conf

Nesse momento, procure com o mouse os elementos necessários, eles ficarão marcados com uma borda vermelha. Ao encontrá-los, clique diretamente no elemento ou pressione Alt + C com o mouse em cima do elemento para mapea-lo. Ele aparecerá no painel Mapped Elements.

Dica

Para esse caso específico, o elemento com o número de inscritos retorna todas as informações que precisamos, vamos usar esse elemento.

Após selecionar todos os elementos necessários, verifique as configurações:

  • Generate code with browser setup: Desmarcado
  • Generate code to start the browser: Marcado

Como já configuramos o navegador, não é necessário selecionar essa opção.

Inspector-element

Volte no VSCode e clique na linha onde quer que o código seja gerado, logo após as configurações iniciais que fizemos anteriormente, em seguida clique em Generate Code.

O código ficará semelhante ao abaixo:

# Starting browser
bot.browse("https://www.youtube.com/@botcity_br")

element = bot.find_element(selector='//span[@class="yt-core-attributed-string yt-content-metadata-view-model-wiz__metadata-text yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--link-inherit-color" and @role="text"]', by=By.XPATH)
## Getting text property, you can access other properties through the WebElement object
# print(element.text)

# Stopping the browser
bot.wait(3000)
bot.stop_browser()

Refatorando o código

Ao selecionar o elemento com o número de inscritos, podemos refatorar o código gerado para capturar as informações da seguinte maneira:

  • método: find_element() -> find_elements()
  • variável: adiciona -> nome_canal = element[0].text
  • variável: adiciona -> numero_inscritos = element[1].text
  • variável: adiciona -> quantidade_videos = element[2].text

Dica

O método find_element() retorna somente um elemento com o seletor indicado, já o find_elements() retorna uma lista de elementos com o mesmo seletor e podemos acessar cada um deles através do índice da lista.

# Retorna lista de elementos
element = bot.find_elements(selector='//span[@class="yt-core-attributed-string yt-content-metadata-view-model-wiz__metadata-text yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--link-inherit-color" and @role="text"]', by=By.XPATH)

# Captura o texto de cada elemento
nome_canal = element[0].text
numero_inscritos = element[1].text
quantidade_videos = element[2].text

Pesquisando outros canais

Vamos criar uma lista com o nome de outros canais para pesquisar e coletar as essas informações.

  • canais:
    canais = ['botcity_br', 'botcity-dev', 'youtube', 'github']
    

Agora criaremos um laço de repetição para iterar sobre a lista e repetir o processo criado acima. Também vamos alterar a URL para pesquisar o canal atual, adicionando uma string formatada. No final, vamos imprimir as informações coletadas de cada canal.

O código ficará semelhante ao abaixo:

for canal in canais:
    # Inicia o navegador
    bot.browse(f"https://www.youtube.com/@{canal}")

    # Retorna lista de elementos
    element = bot.find_elements(selector='//span[@class="yt-core-attributed-string yt-content-metadata-view-model-wiz__metadata-text yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--link-inherit-color" and @role="text"]', by=By.XPATH)

    # Captura o texto de cada elemento
    nome_canal = element[0].text
    numero_inscritos = element[1].text
    quantidade_videos = element[2].text
    print(f"Nome do canal: {nome_canal} | Número de inscritos: {numero_inscritos} | Quantidade de vídeos: {quantidade_videos}")


    # Finaliza o navegador
    bot.wait(3000)
    bot.stop_browser()

Código completo do projeto

# Import de Web Bot
from botcity.web import WebBot, Browser, By

# Import de integração com BotCity Maestro SDK
from botcity.maestro import *

# Desativa mensagem de erros por não estar conectado ao Maestro
BotMaestroSDK.RAISE_NOT_CONNECTED = False


def main():
    # Instancia do BotMaestroSDK
    maestro = BotMaestroSDK.from_sys_args()
    # Objeto com informações da execução
    execution = maestro.get_execution()

    print(f"Task ID is: {execution.task_id}")
    print(f"Task Parameters are: {execution.parameters}")

    bot = WebBot()

    # Configuração do modo headless
    bot.headless = False

    # Configuração do navegador
    bot.browser = Browser.FIREFOX

    # Caminho para o webdriver
    bot.driver_path = r"resources\geckodriver.exe"

    # Lista de canais para pesquisar
    canais = ['botcity_br', 'botcity-dev', 'youtube', 'github']

    for canal in canais:
        # Inicia o navegador
        bot.browse(f"https://www.youtube.com/@{canal}")

        # Retorna lista de elementos
        element = bot.find_elements(selector='//span[@class="yt-core-attributed-string yt-content-metadata-view-model-wiz__metadata-text yt-core-attributed-string--white-space-pre-wrap yt-core-attributed-string--link-inherit-color" and @role="text"]', by=By.XPATH)

        # Captura o texto de cada elemento
        nome_canal = element[0].text
        numero_inscritos = element[1].text
        quantidade_videos = element[2].text
        print(f"Nome do canal: {nome_canal} | Número de inscritos: {numero_inscritos} | Quantidade de vídeos: {quantidade_videos}")


        # Finaliza o navegador
        bot.wait(3000)
        bot.stop_browser()



if __name__ == '__main__':
    main()
Saida esperada:
Nome do canal: @botcity_br | Número de inscritos: 1,11 mil inscritos | Quantidade de vídeos: 45 vídeos
Nome do canal: @botcity-dev | Número de inscritos: 1,54 mil inscritos | Quantidade de vídeos: 22 vídeos
Nome do canal: @YouTube | Número de inscritos: 42,8 mi de inscritos | Quantidade de vídeos: 928 vídeos
Nome do canal: @GitHub | Número de inscritos: 456 mil inscritos | Quantidade de vídeos: 2,1 mil vídeos

Desafio

Desenvolvemos uma automação web que acessa canais de YouTube e coleta informações sobre cada um deles, ao final mostra na tela as informações de nome do canal, número de inscritos e quantidade de vídeos.

Como desafio, faça melhorias no código, como:

  • Navegar na página com BotCity Web Inspector e coletar informações diferentes;
  • Separar responsabilidades em funções;
  • Adicionar tratamento de exceções;
  • Adicionar canais dinamicamente;
  • Estruturar a saida das informações em um arquivo CSV: