querido-diario
querido-diario copied to clipboard
Uberlândia-MG spider
Fix https://github.com/okfn-brasil/querido-diario/issues/621
Bom dia Renne.
Tudo bem ? Estava empenhando criar esse raspador. E consegui avançar positivamente na implementação, mas vi que vc criou esse PR. Sua solução é bem melhor que a minha rsrsrsr foi minha segunda tentativa de implementar o raspador. Mas aprendi demais dessa ultima vez.
Vi que vc tratou a questão do type="diariooficial" e "diario_oficial", e que vc criou uma função de callback que vc defini na chamada da scrapy.Request (muito massa).
Bom mas como brinquei bastante com o shell para achar o melhor caminho para minha implementação, eu verifiquei que existe alguns anos, que não tem todos os meses, ai com a função de callback, acredito que sempre vai devolver 404 tanto para o parametro type que vc implementou, o que iria entrar em loop, na validação da função on_error ? Assim, sou um iniciante, e já querendo achar erros no código do mestre rsrsrsrs longe disso, era mais para melhorar meu entendimento mesmo sobre a raspagem.
Aqui a baixo esta meu esboço da implementação, estava usando um request para pegar a pagina de todas as edições anteriores, e dela, pegar todos os itens (meses de cada ano), para evitar enviar a requisição HTTP desnecessarias, com o intuito de evitar request invalidos.
O meu problema era que quando eu pegava as url's de ano e mÊs, a url acho que estava sendo enviada para o segundo metodo codigicada e ai o seletor css não tava conseguindo pegar os itens do dom.
`class MgUberlandiaSpider(BaseGazetteSpider): TERRITORY_ID = "3170206" DATE_FIRST_GAZETTE_PUBLISHED = datetime.date(2005,1,1) DATE_REGEX_YEAR = r"/[0-9]{4}/" DATE_REGEX_MONTH = r"/[0-9]{2}/" name = "mg_uberlandia" allowed_domains = ["uberlandia.mg.gov.br"] start_urls = [ "https://www.uberlandia.mg.gov.br/prefeitura/orgaos-municipais/procuradoria-geral-do-municipio/diario-oficial-uberlandia/diario-oficial-anos-anteriores/" ]
def parse(self, response):
items = {}
if not hasattr(self, "start_date"):
self.start_date = self.DATE_FIRST_GAZETTE_PUBLISHED
#print(f"{self.start_date=} / type: {type(self.start_date)}")
pages_with_gazettes = response.css(".elementor-widget-wp-widget-annual_archive_widget ul li ::attr(href)").getall()
for page in pages_with_gazettes:
year = int(re.search(self.DATE_REGEX_YEAR, page).group().replace("/",""))
month = int(re.search(self.DATE_REGEX_MONTH, page).group().replace("/",""))
date_page = datetime.date(year, month, 1)
if self.start_date < date_page:
print(f"Page enviada: {page=}")
print(f"{self.start_date=} / {date_page=} / validation: {self.start_date < date_page}")
yield scrapy.Request(url=page, method="GET", callback=self.parse_pages)
def parse_pages(self, response):
print(f"response.url {response.url}")
print(f"response.request {response.request}")
#TODO validação do controle de paginação
pages = response.css("elementor-pagination page-numbers ::attr(href)").getall()
print(len(pages))
for page in pages:
print(f"Parse pages ===========> {page=}")
yield scrapy.Request(url=page, method="GET", callback=self.parse_items)
#sys.exit(1)
def parse_items(self, response):
print(f"response.url {response.url}")
print(f"response.request {response.request}")
gazettes = response.css("h3 a::attr(href)").getall()
for gazette in gazettes:
print(f"{gazette=}")
sys.exit(1)`
Olá @rafaelhfreitas
Não existe jeito certo, apenas jeitos diferentes de resolver o mesmo problema. Então a sua solução estava em um bom caminho.
Eu não havia percebido que existiam alguns meses que não apareciam na listagem geral de meses. Porém, sem querer, devido a maneira como gerei as URLs eu consegui encontrar páginas "ocultas" que me retornaram dados:
- https://www.uberlandia.mg.gov.br/2018/3/?post_type=diario_oficial - não existe e retorna 404
- https://www.uberlandia.mg.gov.br/2018/3/?post_type=diariooficial - existe mas não aparece na listagem principal
Como a raspagem completa só vai ser executada uma vez, e depois só vamos fazer raspagens diárias só para atualizar com os dados mais recentes, alguns requests extras (que retornam 404 e precisam ser tratados no on_error
) não são um problema grave e podemos aceitar esses requests a mais. Mas realmente a preocupação é muito válida.
Sobre o loop, caso as duas páginas (com post_type=diariooficial
e post_type=diario_oficial
) retornem 404, o Scrapy não iria tentar fazer a chamada novamente, pois ele possui um filtro de duplicados (https://docs.scrapy.org/en/latest/topics/settings.html?#dupefilter-class) que evita repetir requests que já foram feitos antes, mesmo que tentemos.
@rennerocha, como a PR já tem 2 anos, atualizei o histórico da main e testei. Segue tudo certo.
uberlandia-completo.txt uberlandia-completo.csv uberlandia-periodo.txt uberlandia-periodo.csv uberlandia-ultimo.txt uberlandia-ultimo.csv
Só adicionei um commit para colocar w3lib
como dependência explícita do projeto visto que este raspador usa, mas também outros como o de Fortaleza-CE e Belo Horizonte-MG. Recompilei e não mudou nada no requeriments.txt
.