1

I write a bot in python, the bot functions: Show the price of a stock, set a limit on the price, and when this limit is reached, sends a message to discord. At this stage, the bot can only monitor one action. How to make it asynchronous, so that it can monitor several stocks at the same time, and when the limit is reached, it sends a particular stock to discord with a message that the price has reached the mark.

from bs4 import BeautifulSoup
import discord
from discord.ext import commands
from config import settings


bot = commands.Bot(command_prefix = settings['prefix'], help_command=None)

@bot.event
async def on_message(message):
    await bot.process_commands(message)
    channel = message.channel
    co = '{0.content}'.format(message).split()
    tes=co[0]
    if tes=='price':
        yo=co[1]
        print(yo)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0'
        }

        r = requests.get(
            ('https://finance.yahoo.com/quote/') + yo + ('?p=') + yo + ('.tsrc=fin-srch'),
            headers=headers)
        soup = BeautifulSoup(r.text, 'lxml')
        content = soup.find('div', {"class": 'My(6px) Pos(r) smartphone_Mt(6px)'}).find('span').text
        print(content)
        await channel.send(f'{yo} - {content}')
        return content

    elif tes=='limit':
        p=co[1]
        su=co[2]
        price=float(su)

        while True:
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0'
            }
            r = requests.get(
                ('https://finance.yahoo.com/quote/') + p + ('?p=') + p + ('.tsrc=fin-srch'),
                headers=headers)
            soup = BeautifulSoup(r.text, 'lxml')

            content = soup.find('div', {"class": 'My(6px) Pos(r) smartphone_Mt(6px)'}).find('span').text
            con=float(content)
            if price<=con:
                await channel.send("достиг")
                break
            print(content)

        return content

bot.run(settings['token'])

1 Answer 1

2

How to make them async

To make them asynchronous use the library aiohttp instead of requests, and sadly there is no alternative for beautifulsoup that is async but we can use run_in_executor for that

the changes you'll have to make:

  • add an import aiohttp at the top of your file (aiohttp is automatically installed when you install discord.py)
  • add a bot.session = aiohttp.ClientSession() after you defined bot (after the bot = commands.Bot... line)
  • change
r = requests.get(
            ('https://finance.yahoo.com/quote/') + yo + ('?p=') + yo + ('.tsrc=fin-srch'),
            headers=headers)

to

async with bot.session.get(
                f'https://finance.yahoo.com/quote/{yo}?p={yo}.tsrc=fin-srch',
                headers=headers
            ) as r:
    # we will also change `r.text` to `await r.text()`

What this basically does is with the session object, gets the raw html of the website. Same job as requests but async

  • now for
soup = BeautifulSoup(r.text, 'lxml')
content = soup.find('div', {"class": 'My(6px) Pos(r) smartphone_Mt(6px)'}).find('span').text

to make it async, first add it inside a function

def scrape(html):
    soup = BeautifulSoup(html, 'lxml')
    content = soup.find('div', {"class": 'My(6px) Pos(r) smartphone_Mt(6px)'}).find('span').text
    return content

This just wraps the beautifulsoup code in a function that takes raw html amd returns the required content. Nothing else now in your on_message, instead of

content = soup.find('div', {"class": 'My(6px) Pos(r) smartphone_Mt(6px)'}).find('span').text

do

content = await bot.loop.run_in_executor(None, scrape, await r.text())

This runs the code in the scrape function and passes the await r.text() to it which is the raw html. Then in the function, we get the raw html, find our data and return it. Here we get the returned value and save it to a variable named content

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.