Tengo un bot de Discord en Python / Discord.py donde la gente puede ingresar comandos, y normalmente el bot responde muy rápidamente.

Sin embargo, el bot también recopila / extrae datos web en cada iteración del bucle principal. Normalmente, el raspado es bastante corto y agradable, por lo que nadie se da cuenta, pero de vez en cuando el código se configura para hacer un raspado más completo que lleva mucho más tiempo. Pero durante estos raspados pesados, el bot no responde a los comandos del usuario.

@bot.command()
async def sample_command(ctx):
    # may actually take a while for this command to respond if we happen to be
    # in the middle of a heavier site scrape
    await ctx.channel.send("Random message, something indicating bot has responded")  

async def main_loop():
    sem = asyncio.Semaphore(60)
    connector = aiohttp.TCPConnector(limit=60)

    async with aiohttp.ClientSession(connector=connector, headers=headers) as session:
        while True:
            # main guts of loop here ... 

            scrapers = [scraper_1(session, sem), scraper_2(session, sem), ...]
            data = list(chain(*await asyncio.gather(*scrapers)))  # this may take a while

            # do stuff with data

¿Hay alguna manera de hacer que funcione? "Oye, quieres hacer un raspado pesado, bien, procesa en otro lugar. Mientras tanto, continuemos con el bucle principal y volveré a conectarme contigo más tarde cuando hayas terminado y procesaremos los datos entonces ", si eso tiene sentido?

Principalmente, quiero separar este paso de raspado para que no impida que las personas interactúen con el resto del bot.

0
user525966 13 mar. 2021 a las 18:14

3 respuestas

La mejor respuesta

Puede usar asyncio.create_task() para generar el raspado en el "fondo":

async def scrape_and_process(...):
    scrapers = [scraper_1(session, sem), scraper_2(session, sem), ...]
    data = list(chain(*await asyncio.gather(*scrapers)))  # this may take a while
    # do stuff with data

async def main_loop():
    sem = asyncio.Semaphore(60)
    connector = aiohttp.TCPConnector(limit=60)

    async with aiohttp.ClientSession(connector=connector, headers=headers) as session:
        while True:
            # main guts of loop here ... 

            # initiate scraping and processing in the background and
            # proceed with the loop
            asyncio.create_task(scrape_and_process(...))
0
user4815162342 13 mar. 2021 a las 21:36

Puedes intentar usar Python Threading.

Obtenga más información aquí.

Básicamente te permite ejecutarlo en diferentes subprocesos.

Ejemplo:

import threading

def 1():
  print("Helo! This is the first thread")

def 2():
  print("Bonjour! This is the second thread")

thread1 = threading.Thread(target=1)
thread2 = Threading.Thread(target=2)

thread1.start()
thread2.start()
0
Eniola Oyewole 13 mar. 2021 a las 16:40

Puedes usar la extensión de tareas discord.py docs .

Por ejemplo:

from discord.ext import tasks

@bot.event()
async def on_ready():
    main_loop.start()

@bot.command()
async def sample_command(ctx):
    await ctx.channel.send("Random message, something indicating bot has responded")  

@tasks.loop(seconds=60)
async def main_loop():
    do_something()

Otro consejo sencillo: puede utilizar await ctx.send() en lugar de await ctx.channel.send()

0
MoaiadFayez 13 mar. 2021 a las 17:40