Python Brasil – Telegram
Python Brasil
25.7K subscribers
607 photos
12 videos
15 files
3.57K links
Canal para compartilhamento de links, cursos vagas e eventos sobre Python.

Links, contato e freelas: https://linktr.ee/python.brasil

@laenderoliveira
Download Telegram
RT @pythonbrasil: Não podia faltar ela...nossa querida pylady Paola Katherine( @pk_pacheco) também será keynote na #pybr13... https://t.co/
Forwarded from A P
Olá, pessoal!

A Myreks está aberta para quem quiser conhecer e fazer parte do time.


Estamos com a posição para Operations Engineer aberta, para ajudar a construir nossa cultura devops, colaboração entre as pessoas e evolução contínua do produto.


A descrição da vaga está aqui:

https://www.myreks.com/v3/vagas....­


Quem quiser conversar, pode me procurar direto, andre@myreks.com, skype:andre.pastore, telegram:apast, twitter:apast, ou pode escrever nos canais do site.
Criando um bot de notícias para o Telegram usando Scrapy e Firebase
http://feedproxy.google.com/~r/NullOnError/~3/tphk9yKZ8bE/

Problema

Eu costumo pegar com frequência a rodovia Régis Bittencourt (http://www.autopistaregis.com.br/) e o que acontece com frequência é o trânsito parar completamente no meio do nada e sem acesso à internet, então eu fico sem a mínima noção do que está acontecendo e em quanto tempo conseguirei chegar ao meu destino.

Pensando nisso, decidi escrever um pequeno bot para o Telegram que publica num canal as notícias da estrada! Como de costume no NULL on error, vou explicar como fiz.

Web scraping
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');
const buildUrl = require('build-url');

admin.initializeApp(functions.config().firebase);


exports.notifyChannel = functions.database.ref('/news/{what}/{uid}')
.onCreate(event => {
const config = functions.config().telegram;
const url = buildUrl('https://api.telegram.org', {
path: `bot${config.bot.token}/sendMessage`,
queryParams: {
chat_id: config.channel.chat_id,
}
});

return request({
method: 'POST',
uri: url,
resolveWithFullResponse: true,
body: {
text: event.data.val().content
},
json: true
}).then(response => {
if (response.statusCode === 200) {
return response.body.id;
}
throw response.body;
}
);
});



Essa função é bem simples; basicamente, em qualquer evento de onCreate ela é chamada, eu faço uma chamada POST na API do Telegram com o nome do canal, token do bot e conteúdo, que no caso é o texto da notícia.
E como os itens são salvos no Firebase?

Resposta: Recentemente o Firebase lançou uma API para acessar a SDK usando Python, então eu escrevi um item pipeline chamado scrapy-firebase (https://github.com/skhaz/scrapy-firebase) que usa essa API para escrever no banco de dados do Firebase, a cada item coletado do Scrapy, o método process_item (https://github.com/skhaz/scrapy-firebase/blob/master/scrapy_firebase.py#L35) do pipeline é incado, nesse método eu salvo no Firebase.

class FirebasePipeline(BaseItemExporter):

def load_spider(self, spider):
self.crawler = spider.crawler
self.settings = spider.settings

def open_spider(self, spider):
self.load_spider(spider)

configuration = {
'credential': credentials.Certificate(filename),
'options': {'databaseURL': self.settings['FIREBASE_DATABASE']}
}

firebase_admin.initialize_app(**configuration)
self.ref = db.reference(self.settings['FIREBASE_REF'])

def process_item(self, item, spider):
item = dict(self._get_serialized_fields(item))

child = self.ref.child('/'.join([item[key] for key in self.keys]))
child.set(item)
return item



Próximos passos

Ao mesmo tempo em que eu notifico o canal (https://news.1rj.ru/str/RegisBittencourt) do Telegram, estou usando o Cloud Natural Language API (https://cloud.google.com/natural-language/) para classificar a notícia, e, em seguida, salvo no BigQuery (https://bigquery.cloud.google.com/). Após algum tempo, acredito que será possível usar o BigQuery para determinar quais trechos, quando e o quê costuma dar mais problemas à rodovia, através de data mining!
RT @PythonJamaica: 🐍🇯🇲 #pyconjamaica2017 🐍🇯🇲
🐍🇯🇲 🐍November 16-18🐍🇯🇲 🐍
🐍🇯🇲 🐍🐍save the date🐍🐍🇯🇲 🐍
👍1
Forwarded from Fernando @ffreitasalves
Oi pessoal, estamos com vagas para Python e Django, presencial aqui em SP na Tikal Tech.

Quem estive interessado, é só se candidatar por esse formulário, mais informações neste link também:

https://tikaltech.typeform.com/to/TcZu1Z
👍1
Forwarded from Letícia Portella
Pessoal, fiz um projetinho com guidelines pra quem quer estudar ciencia de dados
https://github.com/leportella/data-science-roadmap

E pra colocar as empresas no brasil que trabalham e buscam cientista de dados
https://github.com/leportella/data-science-roadmap/blob/master/empresas.md

Por favor, contribuam :)
Novamente a PacktPub está com a seção "Forever Free eBooks". Lá você vai encontrar diversos títulos de graça para sua coleção, dentro das categorias:
- Web Development
- Big Data
- Application Development
- Dev Tools
https://www.packtpub.com/packt/offers/free-learning
Peewee - Um ORM Python minimalista
http://pythonclub.com.br/peewee-um-orm-python-minimalista.html

Peewee (http://peewee.readthedocs.io/en/latest/index.html) é um ORM destinado a criar e gerenciar tabelas de banco de dados relacionais através de objetos Python. Segundo a wikipedia (https://pt.wikipedia.org/wiki/Mapeamento_objeto-relacional), um ORM é:



Mapeamento objeto-relacional (ou ORM, do inglês: Object-relational mapping) é uma técnica de desenvolvimento utilizada para reduzir a impedância da programação orientada aos objetos utilizando bancos de dados relacionais. As tabelas do banco de dados são representadas através de classes e os registros de cada tabela são representados como instâncias das classes correspondentes.



O que o ORM faz é, basicamente, transformar classes Python em tabelas no banco de dados, além de permitir construir querys usando diretamente objetos Python ao invés de SQL.
O Peewee é destinado a projetos de pequeno/médio porte, se destacando pela simplicidade quando comparado a outros ORM mais conhecidos, como o SQLAlchemy. Uma analogia utilizada pelo autor da API e que acho muito interessante é que Peewee está para o SQLAlchemy assim como SQLite está para o PostgreSQL.
Em relação aos recursos por ele oferecidos, podemos citar que ele possui suporte nativo a SQLite, PostgreSQL e MySQL, embora seja necessário a instalação de drivers para utilizá-lo com PostgreSQL e MySQL e suporta tanto Python 2.6+ quanto Python 3.4+.
Neste tutorial, utilizaremos o SQLite, por sua simplicidade de uso e por não precisar de nenhuma configuração.
Instalação
O Peewee pode ser facilmente instalado com o gerenciador de pacotes pip:
pip install peewee



Criando o banco de dados
Para criar as tabelas é bem simples. Inicialmente passamos o nome do nosso banco de dados (a extensão *.db indica um arquivo do SQLite).
import peewee

db = peewee.SqliteDatabase('codigo_avulso.db')



Diferente de outros bancos de dados que funcionam através um servidor, o SQLite cria um arquivo de extensão *.db, onde todos os nossos dados são armazenados.
DICA: caso deseje ver as tabelas existentes no arquivo codigo_avulso.db, instale o aplicativo SQLiteBrowser. Com ele fica fácil monitorar as tabelas criadas e acompanhar o tutorial.
sudo apt-get install sqlitebrowser



A título de exemplo, vamos criar um banco destinado a armazenar nomes de livros e de seus respectivos autores. Comecemos primeiro com a classe que representa os autores.
import peewee

db = peewee.SqliteDatabase('codigo_avulso.db')

class Author(peewee.Model):
"""
Classe que representa a tabela Author
"""

# A tabela possui apenas o campo 'name', que
# receberá o nome do autor
name = peewee.CharField()

class Meta:
# Indica em qual banco de dados a tabela
# 'author' sera criada (obrigatorio). Neste caso,
# utilizamos o banco 'codigo_avulso.db' criado anteriormente.
database = db



Em seguida, criamos a classe que representa os livros. Ela possui uma relação de "muitos para um" com a tabela de autores, ou seja, cada livro possui apenas um autor, mas um autor pode possuir vários livros.
import peewee

db = peewee.SqliteDatabase('codigo_avulso.db')

class Book(peewee.Model):
"""
Classe que representa a tabela Book
"""

# A tabela possui apenas o campo 'noscript', que
# receberá o nome do livro
noscript = peewee.CharField()

# Chave estrangeira para a tabela Author
author = peewee.ForeignKeyField(Author)

class Meta:
# Indica em qual banco de dados a tabela
# 'author' sera criada (obrigatorio). Neste caso,
# utilizamos o banco 'codigo_avulso.db' criado anteriormente.
database = db



Agora, vamos reunir tudo em um único arquivo model.py. Como exemplo, eu criei um arquivo main.py para utilizarmos as classes que acabamos de criar.
import peewee
from model import Author, Book


if __name__ == '__main__':
try:
Author.create_table()