O primeiro passo é extrair as informações do site. Eu optei por utilizar o framework Scrapy (
https://scrapy.org/), por alguns motivos que ficarão bem claros abaixo e por ter bastante experiência escrevendo web crawlers com o Scrapy, eu não pretendo escrever um tutorial a respeito neste artigo, isso ficará para uma próxima oportunidade.
Antes de tudo eu preciso definir o que eu quero extrair; isso é feito definindo uma class com N campos (
https://doc.scrapy.org/en/latest/topics/items.html#scrapy.item.Field) herdando de scrapy.Item (
https://doc.scrapy.org/en/latest/topics/items.html)
class Entry(Item):
uid = Field()
spider = Field()
timestamp = Field()
content = Field()
Como é possível notar a aranha, ou crawler ficou bem simples, mas como pode ser visto abaixo, vou explicar cada parte a seguir.
class RegisSpider(CrawlSpider):
name = 'regis'
allowed_domains = ['
autopistaregis.com.br']
start_urls = ['
http://www.autopistaregis.com.br/?link=noticias.todas']
rules = (
Rule(LinkExtractor(allow=r'\?link=noticias.?ver*'), callback='parse_news'),
)
def parse_news(self, response):
loader = EntryLoader(item=Entry(), response=response)
loader.add_xpath('content', '//*[@id="noticia"]/p[not(position() > last() -3)]//text()')
return loader.load_item()
A propriedade start_urls indica onde a aranha vai iniciar a varredura de páginas
Após isso, definimos algumas regras. Vou usar um LinkExtractor (
https://doc.scrapy.org/en/latest/topics/link-extractors.html), que, como o próprio nome diz é um componente para extrair links seguindo uma regra das páginas encontradas. Nesse caso eu usei uma expressão regular que bate com todas as URLS de notícias do site, e defino um callback que será chamado para cada página chamado parse_news
LinkExtractor(allow=r'\?link=noticias.?ver*'), callback='parse_news')
Então é aqui que a mágica toda acontece: passei algum tempo analisando o código fonte da página e usando o inspetor web para poder gerar um xpath que bata com notícia, excluindo as informações extras na página.
XPath
O XPath (
https://www.w3schools.com/xml/xml_xpath.asp) é uma forma de atravessar o html e extrair alguma informação específica. É uma linguagem bem poderosa, neste caso eu usei a expressão [not(position() > last() -3)] para excluir os últimos 3 parágrafos marcados pela tag , que o site sempre coloca como uma forma de rodapé. Infelizmente, nem sempre os sites seguem boas práticas, o que me facilitaria e muito a extração dos dados!
loader.add_xpath('content', '//*[@id="noticia"]/p[not(position() > last() -3)]//text()')
Os outros campos, como ID da noticía e timestamp são “extraídos” usando um middleware chamado scrapy-magicfields (
https://github.com/scrapy-plugins/scrapy-magicfields), desta maneira:
MAGIC_FIELDS = {
'uid': "$response:url,r'id=(\d+)'",
'spider': '$spider:name',
'timestamp': "$time",
}
O próximo passo é rodar o web crawler periodicamente. Eu usei o sistema de cloud do scrapinghub, que é a empresa que desenvolve o Scrapy e outras ferramentas de scraping, nele eu posso configurar para rodar de tempos em tempos o crawler. No meu caso eu configurei para rodar a cada 30 minutos,
Mesmo que possível, eu não posso publicar diretamente, apenas as novas notícias, caso contrário toda vez que o crawler rodar eu estaria poluindo o canal com as notícias repetidas. Então eu decidi salvar num banco de dados intermediário para conseguir distinguir o que é novo do que já foi indexado.
Persistência
Eis que entra o Firebase (
https://firebase.google.com/), e sua nova funcionalidade chamada de functions (
https://firebase.google.com/docs/functions), com o qual, eu posso escrever uma função que reage a determinados eventos no banco de dados, como por exemplo, quando um novo dado é inserido.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const request = require('request-promise');