Stack Overflow em Português Asked by Victor Heuer on September 27, 2021
Estou realizando um scraping de matérias de um jornal pernambucano (Diário de PE) conforme uma busca que fiz com algumas palavras-chave sobre o assunto de interesse. A busca do jornal retorna 10 resultados em uma página com HTML dinamico, contendo, ao final, uma lista com todas as paginas de resultados, numeradas de 1 a 10.
Quanto à 1ª pagina, eu consegui fazer o scraping do trecho que desejo do HTML utilizando o Selenium em Python sem grandes problemas, mas estou tendo problemas para acessar as demais páginas a partir da lista gerada.
As referências para as próximas páginas estão em tags <div class="gsc-cursor-page" aria-label="Página 2" role="link" tabindex="0">2</div>
(tomando como exemplo a referência para a 2ª pg.). Não há uma tag âncora <a>
associada a um href gerando o hiperlink para a próxima pagina e nem um botão "próxima". Vide um exemplo disso na imagem abaixo.
Minha estratégia: criei uma função que pegar as referências das páginas nessas divs dando os caminhos através do XPATH. Essas referências foram armazenadas em uma lista, e criei um laço ‘for’ para iterar referencia após referência, utilizando a função click() do Selenium para acessá-las. Criei essa estratégia baseado em várias dicas dadas no próprio Stackoverflow (versão em inglês) e infelizmente ela não funcionou, e só a 2ª página é retornada (além da 1ª).
Abaixo estão os código das duas funções que criei, a primeira para pegar o HTML da 1ª página, e a segunda para fazer isso nas demais, usando a lista que comentei.
Bibliotecas importadas:
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
import time
Primeira função para fazer o scraping do HTML da área desejada na página inicial de resultados da busca:
def get_target_html(url):
chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(options=chrome_options)
wait = WebDriverWait(driver, 10)
driver.get(url)
#Finding target HTML
site_html = wait.until(lambda driver: driver.find_element_by_xpath('//div[@class="gsc-expansionArea"]').get_attribute('innerHTML'))
driver.close()
with open('target_page_1.html', 'wt', encoding='utf-8') as file:
file.write(site_html)
Segunda função para navegar para as demais páginas e fazer o scraping do HTML:
def get_next_pages(url):
chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
#Creating a list that receives all elements according to the given XPATH
page_list = driver.find_elements_by_xpath('//div[@class="gsc-cursor-page"]')
#Reading each element in the list to access the related page and get the target HTML code
count = 1
for page in page_list:
count += 1
number = str(count)
page.click()
time.sleep(10)
target_html = driver.find_element_by_xpath('//div[@class="gsc-expansionArea"]').get_attribute('innerHTML')
#Writing and saving a html file with the target code
with open(f'target_page_{number}.html', 'wt', encoding='utf-8') as file:
file.write(target_html)
driver.close()
print('Finished')
Em seguida, chamei as duas funções fornecendo a URL inicial (https://www.diariodepernambuco.com.br/capa_busca.html?q=seguran%C3%A7a&q=seguran%C3%A7a%20%2B%20p%C3%BAblica%20%2B%20pernambuco&q=%22seguran%C3%A7a%20p%C3%BAblica%22%20%2B%20pernambuco&q=%22seguran%C3%A7a%20p%C3%BAblica%22%20%2B%20crime*%20%2B%20pernambuco).
A excessão que foi retornada:
File "C:UsersVictorOneDriveScrapy ProjectsCorpus_Jornaisteste_selenium_diariodepernambuco5.py", line 57, in <module>
get_next_pages(url)
File "C:UsersVictorOneDriveScrapy ProjectsCorpus_Jornaisteste_selenium_diariodepernambuco5.py", line 40, in get_next_pages
page.click()
File "C:UsersVictoranaconda3envspy36libsite-packagesseleniumwebdriverremotewebelement.py", line 80, in click
self._execute(Command.CLICK_ELEMENT)
File "C:UsersVictoranaconda3envspy36libsite-packagesseleniumwebdriverremotewebelement.py", line 633, in _execute
return self._parent.execute(command, params)
File "C:UsersVictoranaconda3envspy36libsite-packagesseleniumwebdriverremotewebdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "C:UsersVictoranaconda3envspy36libsite-packagesseleniumwebdriverremoteerrorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
StaleElementReferenceException: stale element reference: element is not attached to the page document
(Session info: headless chrome=84.0.4147.89)
Entendo que através dessa excessão StaleElementReferenceException
, é informado que o objeto desejado para o click() não está mais disponível, contudo creio que ele esteja na lista para ser acessado. Rodei o código da 2ª função em isolado, e depois pedi o tamanho da lista (len(page_list)
), retornando a quantidade exata de referências que foram coletadas (=9).
Alguém teria alguma dica sobre como lidar com esse problema?
Consegui achar uma solução pesquisando mais sobre o que estava acontecendo. Encontrei aqui no Stackoverflor em português uma resposta que me ajudou a estruturar o que eu precisava resolver. Para aqueles que se depararem com esse tipo de problema, segue:
Primeiramente, recomendo a leitura da resposta dada à essa questão: Problemas de StaleElementReferenceException em Selenium com Python
Ela determina, em síntese, que se aquela exceção aparece é porque o elemento que o click() está tentando acessar foi perdido.
Construí a solução com base na ideia de que é preciso atualizar a lista a cada vez que seja feito um acesso à página na sequencia. A primeira função que criei foi mantida. A segunda ficou assim:
def get_next_pages(url):
chrome_options = Options()
chrome_options.add_argument('--headless')
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
#Creating a list that receives all elements according to the given XPATH
page_list = driver.find_elements_by_xpath('//div[@class="gsc-cursor-page"]')
pages_amount = len(page_list)
#Creating a loop to update the pages list and get their target HTML
for index in range(pages_amount):
number = str(index+2)
updated_pages_list = driver.find_elements_by_xpath('//div[@class="gsc-cursor-page"]')
print(f'Accessing page {number}')
updated_page_list[index].click()
time.sleep(5)
target_html = driver.find_element_by_xpath('//div[@class="gsc-expansionArea"]').get_attribute('innerHTML')
#Writing and saving a HTML file with the target code
print(f'Saving page {number} html')
with open(f'target_page_{number}.html', 'wt', encoding='utf-8') as file:
file.write(target_html)
driver.close()
print('Finished')
Explicando o que eu alterei:
No inicio da função ainda é criada uma lista de páginas (page_list), mas dessa vez, só como um referêncial para que possa ser calculado a quantidade de elementos nele (page_amount).
Com esse tamanho definido, pode-se realizar um 'for', onde em cada nova iteracão é criada uma nova lista de páginas (updated_page_list), acessando o elemento na posição indexada de acordo com a iteração.
Eu acrescentei alguns prints para ter uma visão daquilo que estava sendo feito, usando o number = str(index+2) para ter a referência ajustada das impressões em tela de acordo com a forma como a lista aparece originalmente no site (de 1 a 10, e não de 0 a 9).
Finalmente, os HTML alvos de cada uma das paginas listadas é raspado e salvo em arquivo numerado também de acordo com a variável "number".
Caso algum interessado tenha ficado com duvida em relação a essa resposta, é só falar aqui que terei o prazer de reiterá-la. E caso alguém tenha uma solução melhor, mais elegante, fica o espaço para apresentá-la.
Minhas saudações!
Correct answer by Victor Heuer on September 27, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP