-
-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trabalhando na issue #695 #968
base: main
Are you sure you want to change the base?
Conversation
Não está sendo pego os diários após chamar o callback sendo que ao criar um projeto do zero tudo funciona. |
arquivos de execução do raspador (executado em 14/11/2023): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oi @luxu ! Seu PR foi revisado no encontro do Grupo de Trabalho (GT) de Raspadores nessa terça (14/11).
Felizmente uma brecha no site foi encontrada pelo @alexjbs. Vou descrever ela no próximo review (pedindo mudanças) e deixar esse aqui apenas como comentários de melhorias no que foi desenvolvido (mas que provavelmente pouca coisa será aproveitada).
Uma dica geral pra contribuições seria que atualizar a branch usando merge deixa o histórico de commits bem confuso. Vou te mostrar aqui como tá o histórico da branch usando git log --oneline --graph
:
Os dois commits que realmente tem conteúdo estão destacados. E ao usar git rebase main
(com a main
atualizada), como fica:
Assim suas alterações ficam no "topo" e dessa maneira conseguimos manter um histórico de commits mais organizado com essa estrutura aqui onde cada PR fica facilmente demarcado na história quando é mesclado com um commit de merge:
Isso não é obrigação sua de saber, é mais dever de quem está mantendo em preservar um histórico de commits organizado, mas achei que valia a dica :) Saber um pouco a mais sobre git vai bem longe porque é usado em quase todo projeto de código.
import datetime as dt | ||
|
||
import scrapy | ||
from dateparser import parse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Como usamos bastante o parse
no contexto de raspagem, seria preferível evitar o import direto da função e assim o contexto ficaria explícito usando dateparser.parse
.
from dateparser import parse | |
import dateparser |
ano_atual = dt.datetime.now().year | ||
for year in [2022, ano_atual]: | ||
for month in range(1, 13): | ||
for day in range(1, 32): | ||
if len(str(day)) < 2: | ||
day = "".join(("0", str(day))) | ||
if len(str(month)) < 2: | ||
month = "".join(("0", str(month))) | ||
data = f"{year}-{month}-{day}" | ||
url = f"{response.url}?data={data}" | ||
yield scrapy.Request( | ||
url, | ||
callback=self.parse_gazette, | ||
cb_kwargs={"data": data}, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aqui temos dois comentários importantes:
- Para gerar datas em um dado intervalo a função
rrule
da bibliotecadateutil
é uma mão na roda gigante e evita gerarmos datas inválidas (como 31 de fevereiro). - É obrigatório implementar o filtro por datas por meio dos atributos
self.start_date
eself.end_date
. É importante dar uma lida nessa página da documentação.
ano_atual = dt.datetime.now().year | |
for year in [2022, ano_atual]: | |
for month in range(1, 13): | |
for day in range(1, 32): | |
if len(str(day)) < 2: | |
day = "".join(("0", str(day))) | |
if len(str(month)) < 2: | |
month = "".join(("0", str(month))) | |
data = f"{year}-{month}-{day}" | |
url = f"{response.url}?data={data}" | |
yield scrapy.Request( | |
url, | |
callback=self.parse_gazette, | |
cb_kwargs={"data": data}, | |
) | |
for date in rrule(DAILY, dtstart=self.start_date, until=self.end_date): # fazer uso do rrule para não gerar datas inválidas | |
# fazer uso de start_date e end_date é obrigatório na escrita do raspador | |
yield scrapy.Request( | |
f"{response.url}?data={date.date().isoformat()}", | |
callback=self.parse_gazette, | |
cb_kwargs={"data": date.date()}, |
link_diario = response.css("a[target]").css("::attr(href)").extract_first() | ||
data_do_diario = link_diario.split("/")[-1].split(".")[:-2] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alguns comentários aqui, em tom de sugestão:
- Não precisaria encadear uso do seletor, fica um pouco menor colocar tudo na mesma chamada do
.css
.extract_first
está em desuso, dê preferência a usar.get
- Porém, ao deixar de usar
.get
acima podemos aproveitar o método.re_first
da resposta do seletor e deixar a construção da variável da data mais legível - Como já temos a data da publicação no argumento
data
, mudaria o nome dessa aqui já que será utilizada apenas para validação em dias onde não há publicação
link_diario = response.css("a[target]").css("::attr(href)").extract_first() | |
data_do_diario = link_diario.split("/")[-1].split(".")[:-2] | |
link_diario = response.css("a[target='diario']::attr(href)") | |
data_no_link = link_diario.re_first(r"\d{4}\.\d{2}\.\d{2}") |
data_no_formato_string = str(data) | ||
data = data_no_formato_string.split("-") | ||
if data_do_diario == data: | ||
data_no_formato_date = parse( | ||
data_no_formato_string, languages=["br"] | ||
).date() | ||
yield Gazette( | ||
date=data_no_formato_date, | ||
file_urls=[link_diario], | ||
is_extra_edition=False, | ||
power="executive", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mais algumas sugestões e por último uma correção:
- Usar curto-circuito (essa condicional onde caso seja verdadeira encerra a execução da subrotina) em Python é interessante pois evita o crescimento horizontal do código (algo bem comum por conta das identações)
- Já temos a data a partir do argumento
data
, faria sentido usá-la no item - Se
link_diario
não usar.get
acima, seria necessário usar aqui - O
power
não é apenasexecutive
. Precisa serexecutive_legislative
por conta de diários como os de 15/10/2021, 22/10/2021 e 22/10/2021 onde há atos da câmara legislativa.
data_no_formato_string = str(data) | |
data = data_no_formato_string.split("-") | |
if data_do_diario == data: | |
data_no_formato_date = parse( | |
data_no_formato_string, languages=["br"] | |
).date() | |
yield Gazette( | |
date=data_no_formato_date, | |
file_urls=[link_diario], | |
is_extra_edition=False, | |
power="executive", | |
) | |
if dt.datetime.strptime(data_no_link, "%Y.%m.%d").date() != data: # usar curto-circuito é uma boa prática em python para o código não crescer muito horizontalmente | |
self.logger.debug(f"Não existe diário para esta data: {data}. Data encontrada: {data_no_link}") # log de debug ajudará caso esse comportamento mude e também ajuda na autodocumentação desse condicional | |
return | |
yield Gazette( | |
date=data, # como já temos a data pronta, faz sentido utilizá-la aqui | |
file_urls=[link_diario.get()], # como não usamos .get acima, é necessário colocar aqui | |
is_extra_edition=False, | |
power="executive_legislative", # provavelmente | |
) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Como mencionei na revisão anterior, o @alexjbs descobriu uma brecha!
Todos os arquivos estão acessíveis a partir do link https://apps.ioepa.com.br/Parauapebas/Busca/Arquivos/ .
Como a data de publicação no link é confiável, não perdemos nenhuma informação. E essa implementação otimizaria muito as requisições para fazer a raspagem completa (e faria uma requisição a menos na execução diária).
Pode desenrolar a reescrita do raspador?
AO ABRIR um Pull Request de um novo raspador (spider), marque com um
X
cada um dos items do checklistabaixo. NÃO ABRA um novo Pull Request antes de completar todos os items abaixo.
Checklist - Novo spider
start_date
eend_date
definidos) ao menos uma vez e os dados retornados estavam corretos.log_count/ERROR
igual a zero).start_date
no seu spider com a data do Diário Oficial mais antigo disponível na página da cidade.Descrição
<Descreva o seu Pull Request informando a issue (caso exista) que está sendo solucionada ou uma descrição do código apresentado>