Raspagem de dados de um site de notícias em pt-BR
Tags: tutorial, braziliandevs, python, beginners
Published: 2024-05-13 01:20:22

Neste post realizamos a raspagem de dados (_web scraping_) de um site de notícias em português do Brasil (pt-BR). Utilizando a biblioteca `Requests` do Python 3, coletamos o conteúdo HTML do site. Em seguida, extraímos as <u>notícias mais lidas e seus respectivos links</u> com o `BeautifulSoup`. Introduzimos alguns aspectos legais do _scraping_ e apresentamos casos de uso que estão atualmente rodando na Internet.

<a id='sumario'></a>
## Sumário ☕
  - [Pré-requisitos](#pre_requisitos)
  - [Coleta do conteúdo do HTML](#coleta_html)
  - [Inspecionando o trecho HTML de interesse](#inspeciona_html)
  - [Busca pela tag com o BeautifulSoup](#procura_tag)
  - [Sobre o `robots.txt`](#robots)
  - [Exemplos de uso da raspagem de dados rodando em produção](#exemplos)
  - [Conclusão e próximos passos](#conclusao)

---

<small>

📻 Sugestão de disco para acompanhar o tutorial:

- [Miles Davis - _Round About Midnight (1957) Full Album_](https://www.youtube.com/watch?v=w6PfwriXJbs)
</small>

---

<a id='pre_requisitos'></a>
## Pré-requisitos <a href="#sumario" "Voltar no Sumário">^</a>

Para rodar os códigos apresentados, é necessário instalar as seguintes bibliotecas do Python 3:

- `Beautiful Soup` (`pip install beautifulsoup4`)

- `Requests` (`pip install requests`)

---

<a id='coleta_html'></a>
## Coleta do conteúdo do HTML <a href="#sumario" "Voltar no Sumário">^</a>

O primeiro passo para extrair as notícias mais lidas é obter o HTML do site de interesse. Para isso usamos a biblioteca [`Requests`](https://docs.python-requests.org/en/latest/index.html). Com ela é possível executar requisições HTTP como `GET`, `POST`, `PUT` e `DELETE`.

![Homepage site UOL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/llnzo18im4g05s88vp86.png)
<center><small><small>Homepage do site <a href="www.uol.com.br">www.uol.com.br</a></small></small></center>

A página escolhida para realizarmos a raspagem é a tela inicial do site de notícias UOL ([https://www.uol.com.br](https://www.uol.com.br)). O código abaixo realiza a coleta do conteúdo HTML deste site:

```Python
import requests
url = ('https://www.uol.com.br')
content_html = requests.get(url).text

# descomente a linha abaixo para ver o conteúdo do HTML
# print(content_html)
```

![html homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pi2hanbfppe9i0rqx9qk.png)
<center><small><small>Imagem do HTML coletado com a biblioteca Requests.</small></small></center>

Após coletar o HTML, usamos o `BeautifulSoup` para extrair os dados de interesse. Essa biblioteca disponibiliza funções para a navegação e extração de elementos das tags HTML.  

Para transformar o conteúdo de texto HTML coletado (`content_html`) em um objeto `BeautifulSoup`, é necessário realizar o _parsing_ desse texto, convertendo ele numa estrutura de dados navegável.

```Python
from bs4 import BeautifulSoup

soup = BeautifulSoup(content_html, 'html.parser')
```

Após realizar o _parsing_ do conteúdo HTML (`content_html`), temos um objeto que organiza as tags, e todo conteúdo do HTML, em forma de árvore. As relações das tags (pais, filhos, etc,) é mantida, o que facilita a navegação nos elementos HTML. No código acima, esse objeto é chamado de `soup`.

### Exemplo

Como teste inicial, vamos extrair o título (tag `<title>`) do HTML coletado. Para isso basta acessar o elemento `title` do objeto `soup`:

```Python
print(soup.title)

# saída esperada: 
# <title>UOL - Seu universo online</title>
```

É possível extrair outros elementos de forma semelhante. Por exemplo, para obter o primeiro link (tag `<a>`) do HTML, usamos o elemento `a`:

```Python
print(soup.a)

'''
saída esperada: 
<a class="hyperlink" href="https://www.ingresso.com/?partnership=home&amp;utm_source=uol.com.br&amp;utm_medium=barrauol&amp;utm_campaign=linkfixo_barrauol&amp;utm_content=barrauol-link-ingressocom&amp;utm_term=barrauol-ingressocom" title="Ingresso.com">
Ingresso.com
</a>
'''
```

Podemos coletar todos os links do HTML com o método `find_all('a')`:

```Python
print(soup.find_all('a'))

'''saída esperada: uma lista com todos os links da página

[<a class="hyperlink" href="https://www.ingresso.com/?partnership=home&amp;utm_source=uol.com.br&amp;utm_medium=barrauol&amp;utm_campaign=linkfixo_barrauol&amp;utm_content=barrauol-link-ingressocom&amp;utm_term=barrauol-ingressocom" title="Ingresso.com">
Ingresso.com
</a>,
 <a class="hyperlink" href="https://batepapo.uol.com.br/?utm_source=uol.com.br&amp;utm_medium=barrauol&amp;utm_campaign=linkfixo_barrauol&amp;utm_term=barrauol-uolplay&amp;utm_content=barrauol" title="Bate-Papo">
Bate-Papo
</a>,
...
]
'''
```

**Documentação:** Uma lista completa dos comandos do `BeautifulSoup` pode ser encontrada em sua documentação. Clique [aqui](https://www.crummy.com/software/BeautifulSoup/bs4/doc.ptbr/# "Site da documentação do BeautifulSoup") para acessá-la.

---

<a id='inspeciona_html'></a>
## Inspecionando o trecho HTML de interesse <a href="#sumario" "Voltar no Sumário">^</a>

Agora veremos como coletar a seção das <u>notícias mais lidas</u> do site de notícias.

![Mais lidas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wgyposaa0jr3wsggclc.png)

Atualmente (maio/2024), essa seção fica mais a direita da página. Para obter o elemento HTML referente a ela, [inspecionamos a página ](https://pt.wikihow.com/Inspecionar-um-Elemento-no-Chrome "Tutorial sobre o tema"). Posicionamos o cursor do mouse sobre a região da seção 'Mais lidas', clicamos com o botão direito do mouse e, em seguida, clicamos em <u>Inspecionar</u>.

![Inspecionar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zms7pl0i2kt6jkm3yflf.png)

**Atalho:** Também é possível acessar o _Inspect_ (Inspetor) do navegador web com o atalho `Ctrl + Shift + c`.

Com o Inspetor é possível identificar a tag HTML que caracteriza o elemento HTML desejado.

![Inspetor check](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uc1dm137puae3gb23yvx.png)

A figura acima mostra essa etapa. Note que o trecho destacado, mostra que a seção "Mais Lidas" está na tag `<li>` e sua classe (`class`) é igual a `mostRead__item`.

---

<a id='procura_tag'></a>
## Busca pela tag com o BeautifulSoup <a href="#sumario" "Voltar no Sumário">^</a>

Agora que identificamos a tag HTML correspondente as notícias mais lidas, basta procurarmos por ela no objeto `soup`.

O código abaixo procura por todas tags to tipo `<li class= "mostRead__item">` e exibe o resultado.

```Python
all_most_read = soup.find_all('li', class_='mostRead__item')

print(all_most_read)

'''saída esperada:
[<li class="mostRead__item"><a class="mostRead__item__hyperlink hyperlink" href="https://noticias.uol.com.br/cotidiano/ultimas-noticias/2024/05/12/quatro-membros-da-mesma-familia-sao-encontrados-mortos-abracados-no-rs.htm" title="Quatro membros da mesma família são encontrados mortos abraçados no RS"><h3 class="title__element mostRead__title">
Quatro membros da mesma família são encontrados mortos abraçados no RS
<!-- --></h3><!-- --></a></li>, <li class="mostRead__item"><a class="mostRead__item__hyperlink hyperlink" href="https://noticias.uol.com.br/cotidiano/ultimas-noticias/2024/05/12/nunca-vi-um-rapaz-ele-conta-como-e-ser-marido-de-presa.htm" title="Ele narra a rotina de 'marido' de presa: 'Nunca vi um rapaz nas visitas'"><h3 class="title__element mostRead__title">
Ele narra a rotina de 'marido' de presa: 'Nunca vi um rapaz nas visitas'
<!-- --></h3><!-- --></a></li>, <li class="mostRead__item"><a class="mostRead__item__hyperlink hyperlink" href="https://noticias.uol.com.br/cotidiano/ultimas-noticias/2024/05/12/rs-prefeito-de-canoas-pede-evacuacao-imediata-de-quem-retornou-as-casas.htm" title="Com novo alerta de cheias, prefeitos do RS pedem que moradores deixem casas"><h3 class="title__element mostRead__title">
Com novo alerta de cheias, prefeitos do RS pedem que moradores deixem casas
<!-- --></h3><!-- --></a></li>, <li class="mostRead__item"><a class="mostRead__item__hyperlink hyperlink" href="https://noticias.uol.com.br/opiniao/coluna/2024/05/12/minha-indignacao.htm" title="'Confesso que perdi a paciência com mentirosos', diz ministro Paulo Pimenta"><h3 class="title__element mostRead__title">
'Confesso que perdi a paciência com mentirosos', diz ministro Paulo Pimenta
<!-- --></h3><!-- --></a></li>, <li class="mostRead__item"><a class="mostRead__item__hyperlink hyperlink" href="https://www.uol.com.br/esporte/futebol/colunas/futebol-pelo-mundo/2024/05/12/como-mensagem-de-rodrygo-para-o-rs-virou-fake-news-politica-na-europa.htm" title="Mensagem de Rodrygo para o RS vira fake news política na Europa"><h3 class="title__element mostRead__title">
Mensagem de Rodrygo para o RS vira fake news política na Europa
<!-- --></h3><!-- --></a></li>]
'''
```

Para melhorar a organização dos dados coletados, podemos separar o título e o link da notícia mais lida com o `get_text()`.

### Código completo

Incluindo essa etapa o código completo para coleta e exibição das notícias na seção mais lida fica:

```Python
import requests

url = ('https://www.uol.com.br')
html = requests.get(url).text

soup = BeautifulSoup(html, 'html.parser')

news_most_read = []
i=0
for row in soup.find_all('li', class_='mostRead__item'):
    print(f'\n{i}. {row.get_text().strip()}')
    for a in row.find_all('a', href=True):
        print(f'>>> {a["href"]}')
    i+=1

'''saída esperada:
0. Quatro membros da mesma família são encontrados mortos abraçados no RS
>>> https://noticias.uol.com.br/cotidiano/ultimas-noticias/2024/05/12/quatro-membros-da-mesma-familia-sao-encontrados-mortos-abracados-no-rs.htm

1. Ele narra a rotina de 'marido' de presa: 'Nunca vi um rapaz nas visitas'
>>> https://noticias.uol.com.br/cotidiano/ultimas-noticias/2024/05/12/nunca-vi-um-rapaz-ele-conta-como-e-ser-marido-de-presa.htm

2. Com novo alerta de cheias, prefeitos do RS pedem que moradores deixem casas
>>> https://noticias.uol.com.br/cotidiano/ultimas-noticias/2024/05/12/rs-prefeito-de-canoas-pede-evacuacao-imediata-de-quem-retornou-as-casas.htm

3. 'Confesso que perdi a paciência com mentirosos', diz ministro Paulo Pimenta
>>> https://noticias.uol.com.br/opiniao/coluna/2024/05/12/minha-indignacao.htm

4. Mensagem de Rodrygo para o RS vira fake news política na Europa
>>> https://www.uol.com.br/esporte/futebol/colunas/futebol-pelo-mundo/2024/05/12/como-mensagem-de-rodrygo-para-o-rs-virou-fake-news-politica-na-europa.htm
'''
```

**Nota:** Uma raspagem de dados como a que fizemos acessa os elementos HTML disponíveis no <u>momento atual</u>. Como os sites podem mudar ao longo do seu desenvolvimento, sempre que houver alterações, os nomes utilizados nas buscas e os resultados devem ser atualizados conforme necessidade. O site da UOL, em particular, tem mantido sua estrutura de HTML [há um bom tempo](#mini_app "Clique para ver abaixo")<a id="continua1"></a>, mudando apenas o conteúdo.

---

<a id='robots'></a>
## Sobre o `robots.txt` <a href="#sumario" "Voltar no Sumário">^</a>

Alguns sites não autorizam a raspagem de dados e informam isso no arquivo `robots.txt`. Para verificar o conteúdo deste arquivo basta acessar `www.<nome-do-site.com.br>/robots.txt`.

No caso do site de notícias aqui escolhido, ao acessar [www.uol.com.br/robots.txt](www.uol.com.br/robots.txt) temos o seguinte retorno:

![Print robots](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8mm9ucmqyzj7z18cpupl.png)

O asterisco em `User-agent: *` indica que todos os agentes (softwares/bots) podem acessar todo conteúdo do site (`Allow: /`) com exceção do conteúdo de `/carros/dev/` (`Disallow: /carros/dev/`).

Ainda há outras restrições. Os agentes `GPTBot` e `Google-Extended` não podem acessar o site (`Disalow: /`).

Uma lista de agentes e mais informações sobre o `robots.txt` estão disponíveis em [https://www.robotstxt.org/db.html](https://www.robotstxt.org/db.html).

**Atenção:** Embora o `robots.txt` possua recomendações do que o site permite ou não acessar via software, algumas vezes o webmaster responsável não inclui o `robots.txt`, pois não é algo obrigatório. O que de fato regulamenta a coleta, armazenamento, uso, etc. dos dados vindo da raspagem é a legislação do país. Geralmente, para projetos pessoais, sem fins lucrativos, sem requisições massivas, costuma ser tranquilo.

De toda forma, vale avaliar como os dados coletados serão usados, visto que dependendo do caso pode haver implicações legais.

---

<a id='exemplos'></a>
## Exemplos de uso da raspagem de dados rodando em produção <a href="#sumario" "Voltar no Sumário">^</a>

A fim de ilustrar as possibilidades de uso do _web scraping_, deixamos dois exemplos de uso.

### Querido Diário (https://queridodiario.ok.org.br)

A plataforma Querido Diário disponibiliza vários conteúdos de interesse público, muitas vezes coletados através da raspagem de dados. Nela o usuário pode obter informações relacionadas ao Diário Oficial de inúmeros municípios brasileiros.

Os dados do Querido Diário muitas vezes servem de matéria-prima para reportagens, pesquisas e análises de diferentes tipos.

Para contribuir com o esse projeto open source acesse seu Github: [https://github.com/okfn-brasil/querido-diario-comunidade](https://github.com/okfn-brasil/querido-diario-comunidade)

### Mini-app <a id='mini_app'></a><a href="#continua1" "Continuar a leitura ...">^</a>

Outro exemplo que usa a raspagem de dados é esse mini-app experimental que fizemos algum tempo atrás:

[https://newsaggapp-1-j9368482.deta.app/](https://newsaggapp-1-j9368482.deta.app/)

Ele foi disponibilizado em produção de forma 100% gratuita com o Deta Space: [https://deta.space/](https://deta.space/).

----

<a id='conclusao'></a>
## Conclusão e próximos passos <a href="#sumario" "Voltar no Sumário">^</a>
Assim como fizemos o _scraping_ da seção "mais lidas", podemos coletar outras seções do site da UOL ou de outro de preferência. Basta usar a etapa de análise com o Inspetor do navegador web para conhecer a estrutura do HTML. Além disso, o que vimos sobre o `robots.txt` pode servir de base caso haja dúvidas sobre a viabilidade ou não de realizar a raspagem de algum site. Acreditamos que o conteúdo visto pode contribuir de alguma forma para iniciantes no assunto ou interessados. Dicas ou sugestões são bem-vindas.

<center><h3>Agradecemos a leitura! ☕ 💻 🗞️</h3></center>