fga-eps-mds / 2021.2-INDICAA

Este projeto tem como objetivo criar um software para a melhor visualização de informações relacionadas às matérias ofertadas no SIGAA, com o objetivo de melhorar o trabalho da secretaria na volta gradual as aulas.
https://fga-eps-mds.github.io/2021.2-INDICAA-Wiki/
GNU General Public License v3.0
8 stars 7 forks source link

Web scraping dos dados de quantidades de disciplinas ofertadas #86

Closed Matheuspimentell closed 2 years ago

Matheuspimentell commented 2 years ago

Descrição

Realizar o scraping dos dados do SIGAA relacionados à quantidade de disciplinas ofertadas pelo departamento: FACULDADE DO GAMA - BRASÍLIA.

Tarefas

Critérios de aceitação

gabrielm2q commented 2 years ago

Iniciamos a busca de dados da listagem do SIGAA. Até o momento, o código já é capaz de buscar, contar e imprimir todas as disciplinas disponíveis no departamento selecionado (FGA).

Abaixo, segue o código produzido até o momento.

import time
import requests
#import pandas as pd
#from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from webdriver_manager.firefox import GeckoDriverManager
#import json

url = "https://sig.unb.br/sigaa/public/turmas/listar.jsf?aba=p-ensino"

option = Options()
option.headless = True
driver = webdriver.Firefox(executable_path=GeckoDriverManager().install())

driver.get(url)
time.sleep(6)

#driver.find_element_by_xpath("//*[@id='formTurma:inputDepto']").click()
driver.find_element_by_id("formTurma:inputDepto").click()
driver.find_element_by_xpath('//*[@id="formTurma:inputDepto"]/option[79]').click()
driver.find_element_by_name('formTurma:j_id_jsp_1370969402_11').click()

contadorMaterias = 0
element = driver.find_elements_by_class_name('tituloDisciplina')
for k in element:
    html_content = k.get_attribute('innerHTML')
    print(html_content)
    contadorMaterias += 1

print('Total de disciplinas apresentadas na listagem: ', contadorMaterias)

# element1 = driver.find_elements_by_class_name('linhaPar')
# element2 = driver.find_elements_by_class_name('linhaImpar')
# for k in element1:
#     html_content = k.get_attribute('outerHTML')
#     #print(html_content)
#     i += 1
# for k in element2:
#     html_content = k.get_attribute('outerHTML')
#     #print(html_content)
#     i += 1

driver.quit()

Colaboradores

@AdneMoretti @gabrielm2q

AdneMoretti commented 2 years ago

As partes comentadas do código acima podem ser utilizadas futuramente para acessar todas as turmas e calcular o total de turmas do departamento.

# element1 = driver.find_elements_by_class_name('linhaPar')
# element2 = driver.find_elements_by_class_name('linhaImpar')
# for k in element1:
#     html_content = k.get_attribute('outerHTML')
#     #print(html_content)
#     i += 1
# for k in element2:
#     html_content = k.get_attribute('outerHTML')
#     #print(html_content)
#     i += 1
gabrielm2q commented 2 years ago

Para complementar o código produzido, estaremos elaborando um pequeno "tutorial" para a execução do código, mostrando quais tecnologias foram utilizadas e como realizar a instalação.

gabrielm2q commented 2 years ago

Além disso, é possível evoluir o código com a estruturação dos dados recebidos, de modo a produzir um arquivo Json com todos os dados retirados do site, de modo a facilitar a inserção no BD.

gabrielm2q commented 2 years ago

O seguinte vídeo possui explicações que podem ajudar no entendimento do Web Scraping utilizando o Selenium, BeautifulSoup e o Pandas. Vídeo: Python na Prática fazendo Web Scraping (de JavaScript dinâmico)

oCaioOliveira commented 2 years ago
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from webdriver_manager.firefox import GeckoDriverManager

option = Options()
option.headless = True
driver = webdriver.Firefox(executable_path=GeckoDriverManager().install())

def acessarURL():
    url = "https://sig.unb.br/sigaa/public/turmas/listar.jsf?aba=p-ensino"
    driver.get(url)

def selecionarNivelEnsino():
    botaoCampoEnsino = driver.find_element_by_id('formTurma:inputNivel')
    botaoGraduacao = driver.find_element_by_xpath('//*[@id="formTurma:inputNivel"]/option[3]')
    botaoCampoEnsino.click()
    botaoGraduacao.click()

def selecionarUnidade():
    botaoUnidade = driver.find_element_by_id('formTurma:inputDepto')
    botaoFGA = driver.find_element_by_xpath('//*[@id="formTurma:inputDepto"]/option[79]')
    botaoUnidade.click()
    botaoFGA.click()

def acionarBotaoBuscar():
    botaoBuscar = driver.find_element_by_name('formTurma:j_id_jsp_1370969402_11')
    botaoBuscar.click()

def verificaVagas():
    contadorDocentes = 0
    contadorVagas = 0
    element1 = driver.find_elements_by_xpath("//td[@style='text-align: center;']")
    for x in element1:
        resto = contadorVagas % 2
        html_content = x.get_attribute('innerHTML')
        if resto == 1: 
            disciplina = driver.find_elements_by_xpath("//td[@class='nome']")[contadorDocentes]
            turma = disciplina.get_attribute('innerHTML')
            print(f'Vagas em {turma}: {html_content}')
            contadorDocentes += 1
        contadorVagas += 1
    return contadorVagas

def fecharJanela():
    driver.quit()

def main():
    acessarURL()
    driver.implicitly_wait(6)
    selecionarNivelEnsino()
    selecionarUnidade()
    acionarBotaoBuscar()
    contadorVagas = verificaVagas()
    print(f'Numero de Turmas encontradas: {contadorVagas/2}')
    fecharJanela()

main()

Código formatado de uma forma diferente, porém com as mesmas lógicas e funcionalidades, diferença para auxiliar na leitura para quem não pode participar do desenvolvimento (funcionalidades separadas em funções e variáveis melhor descritas).

gabrielm2q commented 2 years ago

Versionamento

Na produção da primeira versão do código, foram usados os seguintes softwares:

Instale o webdriver manager:

pip3 install webdriver-manager==3.5.3

Para instalar o urllib3 na versão correta talvez seja necessário desinstalar a versão já existente. Para isso, use o comando no terminal:

pip3 uninstall urllib3

Então, instale o urllib3 com o seguinte comando:

pip3 install urllib3==1.21.1

Por fim, para verificar se as versões dos softwares instalados estão corretas, basta usar o seguinte comando e verificar a versão de cada um deles:

pip3 list

Código comentado

# Importando as bibliotecas e módulos necessários para a execução do scraping
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options
from webdriver_manager.firefox import GeckoDriverManager

# Armazenando a URL da página inicial da listagem do SIGAA
url = "https://sig.unb.br/sigaa/public/turmas/listar.jsf?aba=p-ensino"

# Configurando o driver do navegador e passando a URL do SIGAA para ele
option = Options()
option.headless = True
servicoFirefox = Service(GeckoDriverManager().install())
driver = webdriver.Firefox(service=servicoFirefox, options=option)
driver.get(url)

# Aqui temos uma pausa de 2 segundos para que o navegador carregue as
# informações necessárias antes de iniciar o scraping
time.sleep(2)

# Aqui estamos selecionando a opção "Graduação"
driver.find_element(By.ID, "formTurma:inputNivel").click()
driver.find_element(By.XPATH, '//*[@id="formTurma:inputNivel"]/option[3]').click()

# Aqui estamos selecionando o departamento "Faculdade do Gama"
driver.find_element(By.ID, "formTurma:inputDepto").click()
driver.find_element(By.XPATH, '//*[@id="formTurma:inputDepto"]/option[79]').click()

# Clicando no botão de busca
driver.find_element(By.NAME, 'formTurma:j_id_jsp_1370969402_11').click()

# Aqui temos uma pausa de 2 segundos para que o navegador carregue as
# informações necessárias antes de iniciar o scraping
time.sleep(2)

# contadorMaterias armazenará a quantidade de matérias contadas na lista
contadorMaterias = 0

# Aqui estamos importando uma lista com todos os títulos de matérias
# presentes na página do SIGAA
element = driver.find_elements(By.CLASS_NAME, 'tituloDisciplina')

# Vamos passar por todos os elementos (matérias) da lista de oferta
# para printar todas elas e contar a quantidade total de matérias
for k in element:
    html_content = k.get_attribute('innerHTML')
    print(html_content)
    contadorMaterias += 1

print('Quantidade de matérias apresentadas na listagem: ', contadorMaterias)

# Encerrando o navegador
driver.quit()

Tutorial produzido por: @AdneMoretti @gabrielm2q

gabrielm2q commented 2 years ago

O código foi refatorado de forma a retirar quaisquer funcionalidades obsoletas (ou deprecated) e de modo a proporcionar uma melhor visualização do mesmo, comentando e separando as funcionalidades. Segue para revisão.

gabrielm2q commented 2 years ago

Seguindo o exemplo deixado pelo @oCaioOliveira, o código foi "refatorado" de modo a dividir todas as atividades em métodos e centralizar os passos da execução do código na "main()". Novamente, o código segue para revisão.

oCaioOliveira commented 2 years ago

Revisão

O código foi revisado e está coletando os dados normalmente, mas não está inicializando o firefox, para inicializá-lo basta retirar as linhas 13 e 14 e inserir o seguinte comando:

driver = webdriver.Firefox(executable_path=GeckoDriverManager().install())
gabrielm2q commented 2 years ago

Feedback da Revisão

O Firefox é iniciado no background devido ao parâmetro "options=option" passado para o driver do mesmo. Para exibir o navegador, basta retirar este parâmetro! Todavia, com ou sem a exibição do navegador, os dados são coletados e disponibilizados normalmente, conforme citado.

Abaixo, o trecho do código (linha 14) responsável por exibir ou ocultar o navegador:

driver = webdriver.Firefox(service=servicoFirefox, options=option)

Como foi passado que os dados estão sendo coletados normalmente, a issue será fechada.