Esse projeto contém uma série de informações sobre o que eu aprendi aqui na Trybe ao longo do curso de desenvolvimento web da Trybe.
O objetivo principal desse projeto é fazer consultas em notícias sobre tecnologia.
As notíciasforam obtidas através da raspagem do blog da Trybe.
🚵 Habilidades que foram trabalhadas:
- Utilizar o terminal interativo do Python
- Escrever seus próprios módulos e importá-los em outros códigos
- Aplicar técnicas de raspagem de dados
- Extrair dados de conteúdo HTML
- Armazenar os dados obtidos em um banco de dados
Esse projeto foi proposto pelo curso de desenvolvimento web da Trybe.
Esse projeto foi desenvolvido no bloco de ciências da computação. Pude desenvolver habilidades de arquitetura de redes e raspagens de dados.
Os commits foram feitos de acordo com os requisitos finalizados.
Todo o projeto foi feita na branch 'juliana-oliveira-tech-news'.
Antes de rodar o projeto, é necessário criar o ambiente virtual e instalar as dependências através dos comandos:
python3 -m venv .venv && source .venv/bin/activate
python3 -m pip install -r dev-requirements.txt
🧱 Estrutura do Projeto
Este repositório já contém um template com a estrutura de diretórios e arquivos, tanto de código quanto de teste criados. Veja abaixo:
Legenda:
🔸Arquivos que não podem ser alterados
🔹Arquivos a serem alterados para realizar os requisitos.
.
├── tech_news
│ ├── analyzer
│ │ ├── 🔹ratings.py
│ │ └── 🔹search_engine.py
│ ├── 🔸database.py
│ └── 🔹menu.py
│ └── 🔹scraper.py
├── tests
│ ├── 🔸assets/*
│ ├── 🔸__init__.py
│ ├── 🔸generate_fixture.py
│ ├── 🔸news.py
│ ├── 🔸test_menu.py
│ ├── 🔸test_ratings.py
│ ├── 🔸test_scraper.py
│ ├── 🔸test_search_engine.py
│ └── 🔸utils.py
├── 🔸dev-requirements.txt
├── 🔸docker-compose.yml
├── 🔸Dockerfile
├── 🔸pyproject.toml
├── 🔸README.md
├── 🔸requirements.txt
├── 🔸setup.cfg
├── 🔸setup.py
└── 🔸trybe.yml
Apesar do projeto já possuir uma estrutura base, você perceberá que possui arquivos vazios, ou seja, neles você quem deve implementar as classes. Novos arquivos e funções podem ser criados conforme a necessidade da sua implementação, porém não remova arquivos já existentes.
Essas notícias foram salvas no banco de dados utilizando as funções python que já vêm prontas no módulo `database.py`
Para a realização deste projeto, utilizaremos um banco de dados chamado tech_news
.
As notícias serão armazenadas em uma coleção chamada news
.
Já existem algumas funções prontas no arquivo tech_news/database.py
que te auxiliarão no desenvolvimento.
Não altere as funções deste arquivo; mudanças nele não serão executadas no avaliador automático.
Rodar MongoDB via Docker:
docker-compose up -d mongodb
no terminal.
Para mais detalhes acerca do mongo com o docker, olhe o arquivo docker-compose.yml
Caso queira instalar e rodar o servidor MongoDB nativo na máquina, siga as instruções no tutorial oficial:
Ubuntu: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/ MacOS: https://docs.mongodb.com/guides/server/install/
Com o banco de dados rodando, o nosso módulo conseguirá acessá-lo sem problemas. Importe o módulo tech_news/database.py
e chame as funções contidas nele.
Lembre-se de que o mongoDB utilizará por padrão a porta 27017. Se já houver outro serviço utilizando esta porta, considere desativá-lo.
Esse foi um projeto individula.
Foi usado Visual Studio Code, além do Trello que auxiliou na organização das tarefas.
No trabalho do desenvolvimento de software a gente sempre tem prazos, muitas vezes os prazos são apertados.
Por outro lado, eu não quero criar algo que não entendo perfeitamente, como também fazer códigos rápidos pode levar a erros que podem demorar muito pra corrigir.
Por isso, usei e sempre uso o método Baby Steps, que é uma estratégia de abordar o desafio passo à passo, defensivamente.
Baby steps é um termo em inglês que quer dizer passos de bebê. Refere-se a fazer as coisas, quaisquer que sejam, devagar, com calma, passo a passo.
local: tech_news/scraper.py
Antes de fazer scrape, precisamos de uma página! Esta função será responsável por fazer a requisição HTTP ao site e obter o conteúdo HTML. Alguns cuidados deverão ser tomados: como a nossa função poderá ser utilizada várias vezes em sucessão, na nossa implementação devemos nos assegurar que um Rate Limit será respeitado.
- A função deve receber uma URL
- A função deve fazer uma requisição HTTP
get
para esta URL utilizando a funçãorequests.get
- A função deve retornar o conteúdo HTML da resposta.
- A função deve respeitar um Rate Limit de 1 requisição por segundo; Ou seja, caso chamada múltiplas vezes, ela deve aguardar 1 segundo entre cada requisição que fizer.
Dica: Uma forma simples de garantir que cada requisição seja feita com um intervalo mínimo de um segundo é utilizar
time.sleep(1)
antes de cada requisição. (Existem outras formas mais eficientes.) - Caso a requisição seja bem sucedida com
Status Code 200: OK
, deve ser retornado seu conteúdo de texto; - Caso a resposta tenha o código de status diferente de
200
, deve-se retornarNone
; - Caso a requisição não receba resposta em até 3 segundos, ela deve ser abandonada (este caso é conhecido como "Timeout") e a função deve retornar None.
📌 Você vai precisar definir o header user-agent
para que a raspagem do blog da Trybe funcione corretamente. Para isso, preencha com o valor "Fake user-agent"
conforme exemplo abaixo:
{ "user-agent": "Fake user-agent" }
✍️ Teste manual
Abra um terminal Python importando estas funções através do comando:
python3 -i tech_news/scraper.py
Agora invoque as funções utilizando diferentes parâmetros. Exemplo:
html = fetch(url_da_noticia)
scrape_noticia(html)
🤖 O que será verificado pelo avaliador
-
A função utiliza o método get() da biblioteca requests
-
A função executada com uma URL correta retorna o conteúdo html
-
A função, sofrendo timeout, retorna None
-
A função retorna None quando recebe uma resposta com código diferente de 200
-
A função respeita o rate limit
local: tech_news/scraper.py
Para conseguirmos fazer o scrape da página de uma notícia, primeiro precisamos de links para várias páginas de notícias. Estes links estão contidos na página inicial do blog da Trybe (https://blog.betrybe.com).
Esta função fará o scrape da página Novidades para obter as URLs das páginas de notícias. Vamos utilizar as ferramentas que aprendemos no curso, como a biblioteca Parsel, para obter os dados que queremos de cada página.
- A função deve receber uma string com o conteúdo HTML da página inicial do blog
- A função deve fazer o scrape do conteúdo recebido para obter uma lista contendo as URLs das notícias listadas.
⚠️ Atenção: NÃO inclua a notícia em destaque da primeira página, apenas as notícias dos cards.
- A função deve retornar esta lista.
- Caso não encontre nenhuma URL de notícia, a função deve retornar uma lista vazia.
✍️ Teste manual
Abra um terminal Python importando estas funções através do comando:
python3 -i tech_news/scraper.py
Agora invoque as funções utilizando diferentes parâmetros. Exemplo:
html = fetch(url_da_noticia)
scrape_novidades(html)
🤖 O que será verificado pelo avaliador
-
A função retorna os dados esperados quando chamada com os parâmetros corretos
-
A função retorna uma lista vazia quando chamada com parâmetros incorretos
local: tech_news/scraper.py
Para buscar mais notícias, precisaremos fazer a paginação, e para isto, vamos precisar do link da próxima página. Esta função será responsável por fazer o scrape deste link.
- A função deve receber como parâmetro uma
string
contendo o conteúdo HTML da página de novidades (https://blog.betrybe.com) - A função deve fazer o scrape deste HTML para obter a URL da próxima página.
- A função deve retornar a URL obtida.
- Caso não encontre o link da próxima página, a função deve retornar
None
🤖 O que será verificado pelo avaliador
-
A função retorna os dados esperados quando chamada com os parâmetros corretos
-
A função retorna None quando chamada com os parâmetros incorretos
local: tech_news/scraper.py
Agora que sabemos pegar páginas HTML, e descobrir o link de notícias, é hora de fazer o scrape dos dados que procuramos!
-
A função deve receber como parâmetro o conteúdo HTML da página de uma única notícia
-
A função deve, no conteúdo recebido, buscar as informações das notícias para preencher um dicionário com os seguintes atributos:
url
- link para acesso da notícia.title
- título da notícia.timestamp
- data da notícia, no formatodd/mm/AAAA
.writer
- nome da pessoa autora da notícia.comments_count
- número de comentários que a notícia recebeu.- Se a informação não for encontrada, salve este atributo como
0
(zero)
- Se a informação não for encontrada, salve este atributo como
summary
- o primeiro parágrafo da notícia.tags
- lista contendo tags da notícia.category
- categoria da notícia.
-
Exemplo de um retorno da função com uma notícia fictícia:
{
"url": "https://blog.betrybe.com/novidades/noticia-bacana",
"title": "Notícia bacana",
"timestamp": "04/04/2021",
"writer": "Eu",
"comments_count": 4,
"summary": "Algo muito bacana aconteceu",
"tags": ["Tecnologia", "Esportes"],
"category": "Ferramentas",
}
📌 Muita atenção aos tipos dos campos, por exemplo, tags
é uma lista, enquanto que comments_count
é numérico e category
é uma string.
📌 Os textos coletados em title
e summary
podem conter alguns caracteres vazios ao final. O teste espera que esses caracteres sejam removidos.
📌 Como nem todas as páginas têm tags
, você pode utilizar esta notícia como base para conseguir raspar essa informação.
📌 Para o campo comments_count
, como há poucas notícias com comentários, utilizem esta notícia como referência para scrape desta informação.
📌 É bom saber que ao fazer scraping na vida real, você está sempre "refém" de quem construiu o site. Por exemplo, pode ser que nem toda notícia tenha exatamente o mesmo HTML/CSS e você precise de criatividade para contornar isso.
🤖 O que será verificado pelo avaliador
- Será verificado se a função retorna o conteúdo correto e no formato correto, dada uma página de notícia exemplo.
Parabéns! Este é o requisito mais longo do projeto, e também a funcionalidade central do nosso tech-news. Faça um break, tome uma água, e #vamoquevamo para os próximos requisitos!
local: tech_news/scraper.py
Agora, chegou a hora de aplicar todas as funções que você acabou de fazer. Com estas ferramentas prontas, podemos fazer nosso scraper mais robusto com a paginação.
- A função deve receber como parâmetro um número inteiro
n
e buscar as últimasn
notícias do site. - Utilize as funções
fetch
,scrape_novidades
,scrape_next_page_link
escrape_noticia
para buscar as notícias e processar seu conteúdo. - As notícias buscadas devem ser inseridas no MongoDB; Para acessar o banco de dados, importe e utilize as funções que já temos prontas em
tech_news/database.py
- Após inserir as notícias no banco, a função deve retornar estas mesmas notícias.
📌 De aqui em diante, usaremos o MongoDB.
Rodar MongoDB via Docker: docker-compose up -d mongodb
no terminal.
Para mais detalhes acerca do mongo com o docker, olhe o arquivo docker-compose.yml
Caso queira instalar e rodar o servidor MongoDB nativo na máquina, siga as instruções no tutorial oficial:
Ubuntu: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
MacOS: https://docs.mongodb.com/guides/server/install/
Com o banco de dados rodando, o nosso módulo conseguirá acessá-lo sem problemas. Importe o módulo tech_news/database.py
e chame as funções contidas nele.
Não altere as funções deste módulo; elas serão utilizadas nos testes.
🤖 O que será verificado pelo avaliador
-
A função
create_news
dotech_news/database.py
foi chamada corretamente -
A função retorna a quantidade correta de notícias
local: tech_news/analyzer/search_engine.py
Agora que temos meios de popular nosso banco de dados com notícias, podemos começar a fazer as buscas! Esta função irá fazer buscas por título.
- A função deve receber uma string com um título de notícia
- A função deve buscar as notícias do banco de dados por título
- A função deve retornar uma lista de tuplas com as notícias encontradas nesta busca. Exemplo:
[
("Título1_aqui", "url1_aqui"),
("Título2_aqui", "url2_aqui"),
("Título3_aqui", "url3_aqui"),
]
- A busca deve ser case insensitive
- Caso nenhuma notícia seja encontrada, deve-se retornar uma lista vazia.
📌 Lembre-se; para acesso ao banco de dados importe db
definido no módulo tech_news/database.py
.
✍️ Teste manual
Abra um terminal Python importando esta função através do comandopython3 -i tech_news/analyzer/search_engine.py
Agora invoque a função utilizando diferentes parâmetros. Exemplo:
search_by_title("Algoritmos")
.
🤖 O que será verificado pelo avaliador
-
Será validado que é possível buscar uma notícia pelo título com sucesso
-
Será validado que ao buscar por um título que não existe, o retorno deve ser uma lista vazia
-
Será validado que é possível buscar uma notícia com sucesso, tanto pelo título em maiúsculas como em minúsculas.
local: tech_news/analyzer/search_engine.py
Esta função irá buscar as notícias do banco de dados por data.
- A função deve receber como parâmetro uma data no formato ISO
AAAA-mm-dd
- A função deve buscar as notícias do banco de dados por data.
- A função deve ter retorno no mesmo formato do requisito anterior.
- Caso a data seja inválida, ou esteja em outro formato, uma exceção
ValueError
deve ser lançada com a mensagemData inválida
. - Caso nenhuma notícia seja encontrada, deve-se retornar uma lista vazia.
📌 Lembre-se: A função recebe uma data no formato ISO AAAA-mm-dd
, mas no banco a data está salva no formato dd/mm/AAAA
. Dica: Lembrem-se de como trabalhamos com datas nos projetos anteriores.
✍️ Teste manual
Abra um terminal Python importando esta função através do comandopython3 -i tech_news/analyzer/search_engine.py
Agora invoque a função utilizando diferentes parâmetros. Exemplo:
search_by_date("2021-04-04")
🤖 O que será verificado pelo avaliador
-
Será validado que é possível buscar uma notícia pela data com sucesso
-
Será validado que ao buscar por uma data que não existe, o retorno deve ser uma lista vazia
-
Sera validado que ao buscar por uma data com formato inválido, deve lançar um erro
ValueError
com a mensagemData inválida
.
local: tech_news/analyzer/search_engine.py
Esta função irá buscar as notícias por tag.
- A função deve receber como parâmetro o nome da tag completo.
- A função deve buscar as notícias do banco de dados por tag.
- A função deve ter retorno no mesmo formato do requisito anterior.
- Caso nenhuma notícia seja encontrada, deve-se retornar uma lista vazia.
- A busca deve ser case insensitive
✍️ Teste manual
Abra um terminal Python importando esta função através do comando:python3 -i tech_news/analyzer/search_engine.py
Agora invoque a função utilizando diferentes parâmetros. Exemplo:
search_by_tag("Tecnologia")
.
🤖 O que será verificado pelo avaliador
-
Será validado que é possível buscar uma notícia pela tag com sucesso
-
Será validado que ao buscar por uma tag que não existe, o retorno deve ser uma lista vazia
-
Será validado que é possível buscar uma notícia tanto pela tag em maiúsculas como em minúsculas
local: tech_news/analyzer/search_engine.py
Esta função irá buscar as notícias por categoria.
- A função deve receber como parâmetro o nome da categoria completo.
- A função deve buscar as notícias do banco de dados por categoria.
- A função deve ter retorno no mesmo formato do requisito anterior.
- Caso nenhuma notícia seja encontrada, deve-se retornar uma lista vazia.
- A busca deve ser case insensitive
✍️ Teste manual
Abra um terminal Python importando esta função através do comando:
python3 -i tech_news/analyzer/search_engine.py
Agora invoque a função utilizando diferentes parâmetros. Exemplo:
search_by_category("Ferramentas")
.
🤖 O que será verificado pelo avaliador
-
Será validado que é possível buscar uma notícia pela categoria com sucesso
-
Será validado que ao buscar por uma categoria que não existe, o retorno deve ser uma lista vazia
-
Será validado que é possível buscar uma notícia tanto pela categoria em maiúsculas como em minúsculas
Esta função irá listar as cinco notícias mais populares; nosso critério de popularidade será a quantidade de comentários.
- A função deve buscar as notícias do banco de dados e calcular a sua "popularidade" com base no número de comentários.
- A função deve ordenar as notícias por ordem de popularidade.
- Em caso de empate, o desempate deve ser por ordem alfabética de título.
- A função deve ter retorno no mesmo formato do requisito anterior, porém limitado a 5 notícias.
- Caso haja menos de cinco notícias, no banco de dados, deve-se retornar todas as notícias existentes;
- Caso não haja notícias disponíveis, deve-se retornar uma lista vazia.
✍️ Teste manual
Abra um terminal Python importando esta função através do comando:python3 -i tech_news/analyzer/ratings.py
Agora invoque a função utilizando diferentes parâmetros. Exemplo:
top_5_news()
.
O que será verificado:
- Será validado que é possível buscar as cinco top notícias
- Será validado que é possível buscar as cinco top notícias e retornar vazio caso não tenha nenhuma notícia
- Caso houver menos de 5 notícias, serão retornadas quantas houverem