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: 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.
bs4Usada 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
Categoria Noticias
  • 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
De seguida criamos algumas constantes:
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!