Bot do discord para extrair informações de um site e postar em um canal utilizando python

Ronie NeubauerRonie Neubauer
5 min read

A ideia deste post é compartilhar a base de um script que criei algum tempo atrás para monitorar o site de um jogo e informar o momento exato que os jogadores do clan inimigo entravam e saiam do jogo, avisando qualquer ação no discord do nosso clan.
"PS: Nenhuma regra foi quebrada, o próprio jogo já disponibilizava essa informação, apenas automatizei ela para o discord =P."

Para fins educativos, neste post vamos criar um bot do discord do zero, instalar em um grupo/servidor, deixar ele online e fazer com que ele leia o valor de um livro em um site e poste o valor e a hora extraida em um canal do discord a cada 30 segundos, servindo de base para novos projetos.

Para quem quiser pegar o código fonte e testar, o projeto completo está em:
https://github.com/RonieNeubauer/scrape-discord-bot

Criando um bot no discord

Primeiro acessamos o Discord Developers
https://discord.com/developers/applications

Criamos um novo bot clicando em:
New Application -> Preencher nome do Bot -> Aceitar termos -> Clicar em Create.

Após ser redirecionado para a página de configurações do bot:

Clicar no menu da esquerda chamado BOT, em seguida vamos gerar e guardar o Token para usar futuramente, clicando em:

Reset Token -> Yes, Do It -> Preencher a senha. Após isso, copiar e guardar o token gerado conforme a imagem abaixo:

Instalando o bot em um grupo do discord

Após gerar o token, basta clicar no menu da esquerda em OAuth2 e selecionar em OAuth2 URL Generator somente a opção Bot

Com isso abre um novo setor chamado Bot Permissions, onde basta selecionar as seguintes permissões necessárias para nosso bot, selecionando somente Send Message

No final da página selecione Guild Install e clique em Copy para obter o link de instalação

Acesse o link em qualquer browser, logue no discord caso necessário, selecione o servidor para instalar o bot, clique em Continuar e depois Autorizar

O bot deve aparecer na lista de usuários offline do seu grupo do discord

Para finalizar vamos crie um canal chamado book onde iremos postar o preço do livro que será extraído pelo web scraping consultando uma página web.
Basta no menu da esquerda em Canais de Texto clicar no + -> Selecionr Texto -> Colocar book como nome do canal -> Criar Canal

Pronto, a primeira parte foi finalizada, temos nosso bot do discord instalado em um grupo e um canal onde iremos postar as mensagens automáticas ao ler o site.

Colocando o bot online

Primeiro de tudo vamos criar um arquivo chamado requirements.txt com todas as bibliotecas de python que iremos utilizar no script, e colocar o código abaixo:

aiohttp==3.9.5
asyncio==3.4.3
attrs==23.1.0
beautifulsoup4==4.12.3
discord.py==2.4.0
requests==2.31.0
pytz==2023.3.post1

Considerando que já temos python e pip instalados na máquina, abra o terminal e execute o comando abaixo para instalar todas as bibliotecas necessárias

pip install -r requirements.txt

Crie um arquivo chamado bot.py onde iremos conectar o bot e deixa-lo somente online sem fazer nada.

import discord
import asyncio

TOKEN = 'UTILIZAR-AQUI-O-TOKEN-DO-BOT-DO-DISCORD-GERADO-ACIMA'

intents = discord.Intents.default()
intents.guilds = True
intents.messages = True
client = discord.Client(intents=intents)

@client.event

async def main():
    async with client:
        await client.start(TOKEN)

if __name__ == '__main__':
    asyncio.run(main())

O código acima define um cliente com permissões básicas (intents) para acessar guildas (grupos / servidores) e mensagens no Discord. A função main() é responsável por iniciar o bot de forma assíncrona, conectando-o ao servidor do Discord com o token fornecido. Ao executar o código, o bot é iniciado, entrando nos servidores para interagir com eles de acordo com as permissões configuradas.

Execute o código abaixo para deixar o bot online

python bot.py

E com isso, podemos ver que em nosso grupo do discord onde o bot foi instalado, agora ele está na lista de usuários online.

Obter o valor do site e postar no canal do discord

Primeiro de tudo vamos usar um site feito para testes de extração de dados, chamado toscrape.com, onde iremos utilizar a página do livro
1,000 Places to See Before You Die
https://books.toscrape.com/catalogue/1000-places-to-see-before-you-die_1/index.html

Para extrair o valor do livro da página acima, vamos adicionar mais alguns blocos de código em nosso bot.py utilizando principalmente a biblioteca BeautifulSoup do python.
https://www.crummy.com/software/BeautifulSoup/bs4/doc/

Farendo uma request para o site carregando o html bruto e na sequencia utilizar o html.parser para transforma esse HTML em uma árvore de elementos que pode ser navegada e pesquisada de forma simples.

Inspecionando o html do site, podemos ver que o preço do livro fica dentro de uma tag html onde a classe é a price_color

<p class="price_color">£26.08</p>

Com isso isso basta procurar o elemento html onde a classe seja igual a price_color e utilizamos o .get_text() para obter o texto.

Colocaremos também um intervalo para buscar novamente o preço do livro a cada 30 segundos e postamos o preço obtido do site e o horário extraído no canal book em todos os grupos do discord que possuem o bot instalado, com isso temos o código completo do nosso script abaixo

import discord
import requests
from bs4 import BeautifulSoup
import asyncio
from datetime import datetime
import pytz

TOKEN = 'UTILIZAR-AQUI-O-TOKEN-DO-BOT-DO-DISCORD-GERADO-ACIMA'
BASE_URL = 'https://books.toscrape.com/catalogue/1000-places-to-see-before-you-die_1/index.html'
DISCORD_CHANNEL = 'book'
CHECK_INTERVAL = 30
REQUEST_TIMEOUT = 10

intents = discord.Intents.default()
intents.guilds = True
intents.messages = True
client = discord.Client(intents=intents)

async def send_status_update(guild, message, channel_name=DISCORD_CHANNEL):
    channel = discord.utils.get(guild.text_channels, name=channel_name)
    if channel:
        await channel.send(message)

def get_price():
    try:
        response = requests.get(BASE_URL, timeout=REQUEST_TIMEOUT)
        response.raise_for_status()
        soup = BeautifulSoup(response.content, 'html.parser')

        return soup.find(class_='price_color').get_text()
    except requests.RequestException as e:
        return None

async def check_status():
    await client.wait_until_ready()

    while not client.is_closed():
        price = get_price()

        if price is not None:
            time = datetime.now(pytz.timezone('America/Sao_Paulo')).strftime('%d/%m/%Y %H:%M:%S')

            for guild in client.guilds:
                await send_status_update(guild, f"{price} - {time}")

        await asyncio.sleep(CHECK_INTERVAL)

@client.event
async def on_ready():
    client.loop.create_task(check_status())

async def main():
    async with client:
        await client.start(TOKEN)

if __name__ == '__main__':
    asyncio.run(main())

Como modificamos o código, precisamos parar o bot que estava rodando e rodar novamente, com isso temos agora a cada 30 segundos o valor do livro extraído do site e postado em nosso canal book.

Como nossa base foi feita em cima de um site estático, veremos sempre o mesmo valor postado, mas como base de estudo queria deixar um exemplo funcional para todos. Mas você pode usar isso para inúmeros projetos e enviar por exemplo somente quando o valor extraído mudar, basta utilizar a imaginação agora =)

O post acabou demorando mais tempo do que a criação do script hehe, mas a idéia era compartilhar um pouco do contexto, a forma e os passos de como tudo isso foi criado.

Espero que gostem.

13
Subscribe to my newsletter

Read articles from Ronie Neubauer directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ronie Neubauer
Ronie Neubauer