querido-diario icon indicating copy to clipboard operation
querido-diario copied to clipboard

Uberlândia-MG spider

Open rennerocha opened this issue 2 years ago • 2 comments

Fix https://github.com/okfn-brasil/querido-diario/issues/621

rennerocha avatar Sep 06 '22 00:09 rennerocha

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)`

rafaelhfreitas avatar Sep 09 '22 14:09 rafaelhfreitas

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 avatar Sep 20 '22 00:09 rennerocha

@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.

trevineju avatar Apr 16 '24 18:04 trevineju