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
Mg Uberlândia [WIP] #37
Conversation
Atualizando o papo que começou em outro PR (mas sobre Uberlândia), a Prefeitura disse que está de olho! Aguardemos resposta então : ) |
Pode fazer dessa forma abaixo, to preferindo usar o BeautifulSoup é bem mais simples e resolve perfeitamente. from bs4 import BeautifulSoup class MgUberlandia(scrapy.Spider):
|
@stefersonferreira consigo o mesmo resultado com o scrapy. O problema que não estou conseguindo pegar a 'data' que fica logo após a tag html (/a) |
@juniorcarvalho , dá uma olhada na minha resposta lá no SO. |
Obrigado @anapaulagomes . Depois da uma olhada no código e me de seu feedback. :-) |
urls = [ | ||
'http://www.uberlandia.mg.gov.br/?pagina=Conteudo&id=2649', | ||
'http://www.uberlandia.mg.gov.br/?pagina=Conteudo&id=2779', | ||
'http://www.uberlandia.mg.gov.br/?pagina=Conteudo&id=3035', |
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.
Até agora não consigo acessar o site da prefeitura de Uberlândia. :( Consegue me dizer o que cada página significa?
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.
Cada uma é um ano.
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.
Nesse caso, acho que seria interessante pegar os anos e os links dinamicamente ao invés de deixá-los hardcoded. Se não, quando chegar em 2019, você terá que mexer no código pra atualizar com o novo link. Isso acaba virando muito trabalho para os mantenedores.
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.
Então...a falta de padronização da página que está matando. Por isto optei pelo hardcoded. Uma pena você não está conseguindo acesso para ver. Uma outra possibilidade seria utilizar o 'caminho xpath' tipo '//*[@id="home"]/table/tbody/tr/td[2]/table/tbody/tr[2]/td[1]/p[2]/span/span/a', mas ficaria hardcoded da mesma forma. Vou testar mais opções e ver o que consigo melhorar.
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.
Por que vocês criou essa lista urls ao invés de utilizar apenas o start_urls?
Você faz uma requisição à URL que está dentro do start_urls e simplesmente ignora o seu conteúdo para então mandar as requisições da sua lista urls .
Use apenas o start_urls e faça o que precisa ser feito no parse, assim você economiza requisições.
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.
Essa lista hard-coded de anos não é uma boa ideia, já que como a @anapaulagomes comentou, em 2019 você vai precisar fazer uma alteração novamente no código (considerando que eles apenas vão incluir um novo item na seção de Edições Anteriores).
Nessa URL você consegue obter todos os links necessários de todos os anos:
start_urls = ['http://www.uberlandia.mg.gov.br/?pagina=Conteudo&id=39', ]
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.
@juniorcarvalho , usar um seletor ('//*[@id="home"]/table/tbody/tr/td[2]/table/tbody/tr[2]/td[1]/p[2]/span/span/a') não é tornar o código hard-coded, já que se eles incluirem mais links, isso não iria mudar. Mas nesse seu caso, você realmente está deixando tudo específico demais, por isso parece ser algo hard-coded.
try: | ||
date = dt.datetime.strptime(dates[i], '%d/%m/%Y') | ||
except: | ||
date = None |
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.
for v in variants: | ||
url = response.xpath(v).extract() | ||
if len(url) > 0: | ||
for u in url: |
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.
Seria legal nomear as variáveis com nomes mais expressivos. v
e u
deixam mais difícil ler o código. :)
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.
v de variants e u de url não ? me de uma dica!
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.
O extract() retorna uma lista vazia caso não encontre nada, então não é necessário fazer a checagem do tamanho da lista.
for variant in variants:
for url in response.xpath(variant).extract():
pass
Seja explícito no nome das suas variáveis. Nesse caso, como as utilizações de v estão a uma, duas linhas de distância do for, é fácil inferir o que elas significam, mas se o seu bloco tivesse algumas dezenas de linhas, ia ser ruim para entender o que aquela variável quer dizer.
Não tenha medo de digitar um pouco mais e ter uma variável com nome longo mas que seja explicito :-)
urls.append(u) | ||
return urls | ||
|
||
def list_dates(self, response): |
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.
Ficou bacana a implementação! 👍🏽
variants = ['//p/span/text()', | ||
'//p/span/span/text()', | ||
'//p/text()' | ||
] |
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.
Seria bom dar uma passada no black pra deixar o código bonitinho. Acho que aqui, por exemplo, ficaria assim:
variants = [
'//p/span/text()',
'//p/span/span/text()',
'//p/text()',
]
@juniorcarvalho , na página http://www.uberlandia.mg.gov.br/?pagina=Conteudo&id=39 você consegue obter todos os links que você precisa. Abra ela com o Developer Tools do Firefox (ou Inspector do Chrome): Você vai perceber que todo o conteúdo (os links) que você precisa estão contidos dentro da No Scrapy você consegue buscar informações usando selectores em CSS (https://doc.scrapy.org/en/latest/topics/selectors.html#selectors) Nesse caso, se você quiser obter o selector dessa div, você pode fazer: Dentro do Developer Tools você pode ver que os links que você quer, estão todos dentro de alguma Como te interessa apenas os hrefs: Agora você tem a lista das URLs desta página. Algumas são links para download e outras são links para as outras páginas dos anos anteriores. Agora só fazer os requests das páginas de ano e retornar os items das páginas de download. |
@rennerocha @anapaulagomes obrigado pelas dicas! fiz as alterações. |
|
||
def parse(self, response): | ||
urls = response.css( | ||
"#home table[align*=right] td[style*=vertical-align] a::attr(href)" |
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.
Eu não gosto de definir seletores usando informações de alinhamento. Caso eles mudem levemente a página e comecem a usar uma classe para alinhamento ao invés do align (o que seria o certo, por sinal :-) ), seu spider quebra. Sempre prefira usar o id do elemento (que é menos provável que mude com o tempo) ou uma classe que tenha algum sentido semântico (nesse caso não existe).
Ao invés, você pode pegar todos os links dessa página e filtrar só os que te interessam:
urls = response.css('#home table a::attr(href)').extract()
Nesse caso você só quer as URLs do seguinte formato:
http://www.uberlandia.mg.gov.br/?pagina=Conteudo&id={ALGUM_NUMERO}
Porém existem URLs desse formato:
http://www.uberlandia.mg.gov.br/uploads/cms_b_arquivos/19147.pdf
Como o padrão é bem claro, você pode filtrar nessa lista só as que estão no formato que você quer. Uma ideia seria usar uma regex para filtrar isso (https://doc.scrapy.org/en/latest/topics/selectors.html#using-selectors-with-regular-expressions):
urls = response.css('#home table a::attr(href)').re('.*Conteudo.*')
Aqui você tem a lista de todas as URLs que você realmente quer trabalhar nesta página.
urls = response.css( | ||
"#home table[align*=right] td[style*=vertical-align] a::attr(href)" | ||
).extract() | ||
urls = self.last_four_years(urls) |
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.
Eu não tentaria filtrar por data nesse momento. Você está pegando as 4 primeiras URLs, mas quem garante que elas realmente são dos últimos 4 anos?
Estamos vendo de fazer a filtragem por data de outra maneira (veja #39 e #23), então acho que não faz nenhum mal deixar o spider pegar tudo o que encontrar por enquanto.
) | ||
return items | ||
|
||
def links_months(self, response): |
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.
Seletores muito específicos dentro de um HTML são ruins, já que qualquer pequena mudança na tela (por exemplo, um ser removido) faz com que ele pare de funcionar.
Abra o Developer Tools na página e dê uma analisada no HTML. Nesse caso por exemplo, dentro da div com id="home", os únicos links são aqueles que você precisa. Então ao invés de fazer essa função toda, você pode simplesmente pegar todas as tags a dentro da div:
response.css('#home a::attr(href)').extract()
Assim você pode excluir essa função inteira e você diminui a necessidade de manutenção futura.
urls_return.append(url) | ||
return urls_return | ||
|
||
def list_dates(self, response): |
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.
O mesmo comentário que fiz no seu método links_months, vale para esse. Não use seletores tão específicos.
Assim você obtém os blocos de cada uma das edições:
edicoes = response.css('#home div')
Que seria isso por exemplo:
<div>
<strong><a href="http://www.uberlandia.mg.gov.br/uploads/cms_b_arquivos/13792.pdf" target="_blank">Edição 4754</a></strong> - 21/10/2015</div>
Aqui você tem o link do download E a data da edição.
Você pode iterar nesses seletores e obter as informações que precisa:
for edicao in edicoes:
url = edicao.css('a::attr(href)').extract_first()
data = re.findall("\d{2}/\d{2}/\d{4}", edicao.extract()) # Ou edicao.re("\d{2}/\d{2}/\d{4}")
Don't forget to update the cities.md |
@juniorcarvalho are you working on this? :) |
@juniorcarvalho precisa de alguma ajuda com o trabalho ? Esse final de semana, fiz o fork para começar a implementação e fiz a parte inicial de buscar os links dos arquivos. A sua implementação já esta bem adiantada. |
Boa tarde pessoal. estou retomando essa atividade. Já fiz o levantamento das paginas que o spider deverá utilizar para fazer a raspagem.
Mas estou em dúvida em como deixar a execução de forma que ele sempre busque o que foi incrementado no portal e não baixe todos os diários novamente. Dúvida já sanada. |
Boa tarde pessoal. Precisando de ajuda na tarefa. Seguinte estou usando o shell do scrapy para testar meus seletores que irei utilizar no spider. Com xpath, consegui filtrar apenas os elementos a da pagina, mas havia outros elementos que não eram do diário e não consegui filtrar eles. Vi que é possível aplicar uma expressão regular ao css. |
|
Obrigado pelo retorno, agora que vi aqui sua resposta Eduardo. Mas era coisa de noob mesmo, vlw pela explicação. |
@rafaelhfreitas pensa em continuar essa PR ainda? |
Bom dia. Penso sim, até me inscrevi no curso que esta sendo oferecido agora em agosto para finalizar o spider. |
Atividade retornada, já fiz um novo fork e estou vendo as aulas do @giuliocc na escola de dados para entregar a primeira contruibuição com o projeto ! |
Closing stale PR. |
Raspagem inicial para Uberlândia-MG
Estou com dificuldade de pegar a data do diário:
https://pt.stackoverflow.com/questions/296567/scrapy-xpath-href-ou-span-dentro-da-div
Pagina inicial do diário: http://www.uberlandia.mg.gov.br/?pagina=Conteudo&id=39
(PS: Este PR corrige o anterior onde fiz junto com mg_belohorizonte)