Página de Noticias Atualizadas com Raspberry e Flask
Neste artigo daremos continuação ao post anterior desta vez, temos como objetivo gerar uma página que atualize sempre que seja acedida com as noticias mais recentes.
Para seguir este projeto caso ainda não possua o material mínimo necessário, recomendamos que adquira o seguinte conjunto:
Imagem | Produto | Comprar |
---|---|---|
Kit Completo Raspberry Pi 4 Desktop |
Como vai funcionar?
Daremos inicio ao projeto por criar o ficheiro main.py que será responsável por controlar todas as ações que iremos efetuar no nosso servidor. Iremos contruir um módulo capaz de colecionar as noticias mais recentes de um site de noticias., no nosso caso iremos utilizar o Jornal de Noticias.
Para complementar o nosso servidor, precisamos de gerar 3 páginas HTML e 1 página CSS:
- base.html – Pagina que será usada como base para as restantes páginas;
- home.html – Página de aterragem ao servidor;
- noticias.html – Página para visualizar as noticias;
- style.css – Editar o estilo das restantes páginas.
Preparar Ambiente
Sendo que estamos a utilizar o Flask para hospedar o nosso servidor existem um conjunto de regras na preparação do ambiente.
O seu ambiente deve seguir a seguinte estrutura:
- Pasta: Servidor
- Ficheiro: main.py
- Pasta: templates
- Ficheiro: base.html
- Ficheiro: noticias.html
- Ficheiro: home.html
- Pasta: static
- Pasta: styles
- Ficheiro: style.css
- Pasta: styles
- Pasta: modules:
- Ficheiro: getNoticias.py
Adicionalmente, com recuso ao gerenciador de pacotes do python3 (pip3) iremos instalar a biblioteca bs4, para isso execute o seguinte comando no seu terminal:
sudo python3 -m pip install bs4
Preparar Servidor
getNoticias.py
A função deste ficheiro é fazer scrapping ao site de noticias escolhido de forma a obter a informação que achamos necessária.
Atenção
Alguns websites não autorizam web scraping nas suas plataformas, por isso, pedimos que consulte ficheiro robots.txt presente em cada site antes de avançar com estas praticas. Para aceder a este ficheiro tem de colocar /robots.txt em frente ao domínio – website.com/robots.txt. Sendo que o jornal de noticias permite webscraping iremos usa-lo para obter as noticias atualizadas.
Sobre o código
Começamos o ficheiro por importar as bibliotecas necessárias:
import requests from bs4 import BeautifulSoup as sp
requests – Usada para efetuar pedido de request a qualquer página web.
bs4 – Usada para webscraping
def getNoticias(): arrCategoriasJN = ['nacional','justica', 'mundo', 'economia', 'desporto', 'inovacao', 'artes', 'opiniao'] arrNoticias = [] firstTime = True
Cria a função getNoticias, e declara variáveis 3 variáveis:
- arrCategoriasJN – Array que contém as categorias
- arrNoticias – Array para armazenar noticias
- firstTime – Caso seja primeira vez é True ou Falso caso não seja.
for cat in arrCategoriasJN: if firstTime == True: active = "active" firstTime = False else: active = "" linkNoticia = f"https://www.jn.pt/{cat}.html" r = requests.get(linkNoticia) soup = sp(r.content, "html.parser") mainDivNoticia = soup.find(class_ = "t-grid-1-featured-2") tituloNoticia = mainDivNoticia.find("h2").text linkNoticia = "https://www.jn.pt" + str(mainDivNoticia.find( 'h2').find('a')['href']) linkImagemNoticia = mainDivNoticia.find('figure').find('source')['data-srcset'] descNoticia = mainDivNoticia.find('h4').text noticia = {"titulo":tituloNoticia, "link":linkNoticia, "desc":descNoticia, "img":linkImagemNoticia, "actv":active} arrNoticias.append(noticia) return arrNoticias
Está última etapa consiste num loop que faz um Get Request a cada categoria, de seguida procura no código fonte da página e armazena na variável mainDivNoticia a div que cumpra com a condição class_ = “t-grid-1-featured-2”
Nessa div encontra-se a informação de cada noticia de forma a facilitar o nosso trabalho no momento da criação da pagina web no nosso servidor, armazenamos a informação de cada noticia em variáveis diferentes.
- tituloNoticia – Titulo das Noticias
- linkNoticia – Links das Noticias
- linkImagemNoticia – Links das Imagens das Noticias
- descNoticia – Descrições das Noticias
Estas variáveis são de seguida convertidas num dicionário, noticia, que por sua vez é adicionado a um array com as noticias, arrNoticias.
Código completo
# Ficheiro Principal Servidor # Website: https://www.electrofun.pt # Blog https://www.electrofun.pt/blog/ # Author: ElectroFun from bs4 import BeautifulSoup as sp import requests def getNoticias(): arrCategoriasJN = ['nacional','justica', 'mundo', 'economia','desporto', 'inovacao', 'artes', 'opiniao'] arrNoticias = [] firstTime = True for cat in arrCategoriasJN: if firstTime == True: active = "active" firstTime = False else: active = "" linkNoticia = f"https://www.jn.pt/{cat}.html" r = requests.get(linkNoticia) soup = sp(r.content, "html.parser") mainDivNoticia = soup.find(class_ = "t-grid-1-featured-2") tituloNoticia = mainDivNoticia.find("h2").text linkNoticia = "https://www.jn.pt" + str(mainDivNoticia.find( 'h2').find('a')['href']) linkImagemNoticia = mainDivNoticia.find('figure').find('source')['data-srcset'] descNoticia = mainDivNoticia.find('h4').text noticia = {"titulo":tituloNoticia, "link":linkNoticia, "desc":descNoticia, "img":linkImagemNoticia, "actv":active} arrNoticias.append(noticia) return arrNoticias
Base.HTML
Sobre o código
Este é o código base para as nossas paginas do servidor, este código pode ser alterado com base nas suas preferências, apenas as referências a jinja2 devem ser mantidas (partes dentro de chavetas)
Código
<!doctype html> <html lang="pt"> <head> <!--Meta Tags--> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!--BootStrap 4--> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> <!--Google Fonts--> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Glory&display=swap" rel="stylesheet"> <!--Block para editar Titulo--> <title> {% block title %} {% endblock %} </title> <!--Link Documento CSS--> <link rel="stylesheet" type="text/css" href= "{{ url_for('static',filename='styles/style.css') }}"> <!--Icon Separador--> <link rel="icon" href="https://www.electrofun.pt/img/favicon.ico?1528822022" > </head> <body> <!--Barra de Navegação Superio: https://getbootstrap.com/docs/4.0/components/navbar/--> <nav class="navbar navbar-expand-lg navbar-dark"> <a class="navbar-brand" href="{{ url_for('home') }}">Servidor</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item active"> <a class="nav-link" href="{{ url_for('home') }}">Home <span class="sr-only">(current)</span></a> </li> <li class="nav-item"> <a class="nav-link" href="{{ url_for('noticias') }}">Noticias</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Sensores</a> </li> <li class="nav-item"> <a class="nav-link" target="_blank" href="https://www.electrofun.pt/">ElectroFun</a> </li> <li class="nav-item"> <a class="nav-link" target="_blank" href="https://www.electrofun.pt/blog/">+ Projetos</a> </li> <li class="nav-item"> <a class="nav-link" target="_blank" href="https://www.instagram.com/electrofun.pt/">Instagram</a> </li> <li class="nav-item"> <a class="nav-link" target="_blank" href="https://www.youtube.com/channel/UCwD7RHUJu6z1WX8PRb99iYQ">Youtube</a> </li> </ul> </div> </nav> <!--Conteudo de Unico de cada pagina--> {%block content%} {% endblock %} <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> <script src='https://kit.fontawesome.com/a076d05399.js'></script> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script> </body> </html>
main.py
O ficheiro main.py como mencionado anteriormente é o arquivo principal do nosso projeto.
Sobre o Código
Começamos por importar as bibliotecas necessárias
from flask import Flask, render_template from modules import getNoticias from time import sleep
HOST = "IP_DO_SEU_RASPBERRY" PORT = 80
Deverá substituir o HOST pelo IP do seu Raspberry
De seguida criamos objeto para armazenar o nosso servidor:
app = Flask(__name__)
Seguidamente criamos as páginas da nossa plataforma
@app.route("/") def home(): return render_template('home.html') @app.route("/noticias") def noticias(): items = getNoticias.getNoticias() return render_template('noticias.html', items = items)
Na função noticias criamos um variável items que sempre que a função noticias() for executada, isto é, sempre que a página /noticias for aberta será executado o script criado em getNoticias.py retribuindo as noticias mais recentes.
Finalmente,
if __name__ == "__main__": app.run(host = HOST, port = PORT)
Têm como funcionalidade iniciar o servidor.
Código Completo
from flask import Flask, render_template from modules import getNoticias from time import sleep HOST = "IP_DO_SEU_RASPBERRY" PORT = 80 app = Flask(__name__) @app.route("/") def home(): return render_template('home.html') @app.route("/noticias") def noticias(): items = getNoticias.getNoticias() return render_template('noticias.html', items = items) @app.route("/nodemcu-text") def nodemcu(): noticias = getNoticias.getNoticias() items = [] for noticia in noticias: noticiaClean = noticia.get('titulo') noticiaClean = unidecode.unidecode(noticiaClean) items.append(noticiaClean) return render_template('nodemcu.html', items = items) if __name__ == "__main__": app.run(host = HOST, port = PORT)
noticias.html
De seguida pode consultar o nosso código para a página noticias.html
{% extends 'base.html' %} {% block title %} Home Page {% endblock %} {% block content %} <section id="news"> <h2 id="tituloSection">Últimas Notícias </h2> <div id="carouselExampleControls" class="carousel slide" data-ride="carousel"> <div class="carousel-inner"> {% for item in items %} <div class="carousel-item {{item.actv}}"> <img class="news-img rounded-circle news-img" src="{{item.img}}" alt="noticia"> <h2 id="tituloNoticia">{{item.titulo}}</h2> <p id="descricaoNoticia">{{item.desc}}</p> <a class="linkContinuarLer" href="{{item.link}}">Continuar a Ler</a> </div> {% endfor %} <a class="carousel-control-prev" href="#carouselExampleControls" role="button" data-slide="prev"> <span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="sr-only">Previous</span> </a> <a class="carousel-control-next" href="#carouselExampleControls" role="button" data-slide="next"> <span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="sr-only">Next</span> </a> </div> </section> {% endblock %}
Como funciona
A forma que optamos por mostrar as nóticias no nosso servidor foi com o uso de um Carousel fornecido nos exemplos do bootstrap.
Caso pretenda construir o sua própria página HTML deverá apenas usar as variaveis jinja {% for item in items %}, {{item.elemento}}, {% endfor %}, etc.. de forma irem ao encontro do seu design.
estilos.css
Os estilos usados no nosso servidor foram os seguintes – Caso decida criar a sua própria pagina HTML este código deverá ser igualmente alterado para ir ao encontro dos seus objetivos.
body{ background-color:#212121; color:#a6a6a7; } /* Estilos NavBar*/ .navbar{ font-family: 'Glory', sans-serif; } .nav-item { padding: 0 18px; } .nav-link { font-size: 1.2rem; font-family: 'Glory', sans-serif; } /*Estilos de noticias.htmk*/ #tituloSection { padding-top:10px; color: #ffffff; font-size: 26px; font-family: 'Glory', sans-serif; } /* Titulo Noticia*/ #tituloNoticia { color: #f0a926; text-align: center; font-size: 24px; font-weight: bold; width: 50%; margin-left: 24%; font-family: 'Glory', sans-serif; } /*Desc. Noticia*/ #descricaoNoticia { color: #ffffff; } .linkContinuarLer { color: #ececec; } .linkContinuarLer:hover { color: #ffffff; font-style: none; font-weight: bold; text-decoration: none; } /*Containers*/ #news { position: static; padding: 0 15% 0 15%; text-align: center; } #news p { text-align: center; font-size: 16px; width: 50%; margin-left: 24%; position: static; } .news-img { margin: 15px; margin-bottom: 30px; }
Finalizar
Com os códigos terminados e colocados na respetiva pasta, abra o terminal na pagina do seu servidor e execute a seguinte linha no terminal:
sudo python3 main.py
Finalmente basta aceder ao endereço de Ip apresentado e navegar até /noticias ou no como no nosso exemplo, clicar em noticias.
Importante
Este método de obtenção de conteúdo e criação de páginas web pode ser facilmente adaptado para mostrar outro tipo de conteúdo a sua escolha – lançamentos de filmes, novas promoções, etc… portanto recomendamos que estude bem cada etapa deste projeto.
Veja o Nosso Video Sobre este Projeto!
Para mais projetos, percorram o nosso blog, onde podem encontrar vários artigos interessantes relacionados com eletrónica, robótica e muito mais! Visitem também o nosso site, onde encontram tudo para eletrónica e robótica!