You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Runtime error排查:asyncio.run无法在运行的事件循环中调用

问题描述

我写了一段Python代码,用来爬取BestBuy网站的每日优惠信息,每隔指定时间推送到Discord频道,但运行时出现错误:Runtime error: asyncio.run cannot be called from running event loop。相关代码如下:

import discord
import os
import schedule
import time
import requests
from bs4 import BeautifulSoup
from discord.ext import commands

intents = discord.Intents.default()
intents.members = True
intents.message_content = True
client = commands.Bot(intents=intents, command_prefix="!")

@client.event
async def on_ready():
    print(f'{client.user} is now online!')

print('eho')
run = False

async def task():
    while not client.is_closed():

        embeds = []

        headers = {'User-Agent':'Mozilla/5.0 (Linux; Android 10; SM-G980F Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.96 Mobile Safari/537.36'}

        response = requests.get('https://www.bestbuy.com/site/misc/deal-of-the-day/pcmcat248000050016.c?id=pcmcat248000050016', headers=headers)

        content = response.content

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

        wf = soup.find('div', class_='wf-wrapper')

        ofs = wf.findAll('div', class_='wf-offer')

        if ofs == None:

            return

        for of in ofs:

            title = of.find('a', class_="wf-offer-link v-line-clamp ").text

            l = of.find('a', class_='wf-offer-link')

            link = "https://bestbuy.com"+l.get('href')

            image = of.find('img', class_="wf-image img-responsive").get('src')

            p = of.find('div', class_="priceView-hero-price priceView-customer-price")

            price = p.find('span').text

            was = of.find('div', class_="pricing-price__regular-price")

            if was == None:

                was = 'NAN'

            else:

                was = was.text

            discount = of.find('div', class_="pricing-price__savings")

            if discount == None:

                discount = 'NAN'

        
            embed=discord.Embed(title=f'{title}', url=link, color=0xff9a03)

            embed.set_thumbnail(url=image)

            embed.add_field(name="Name", value=f'{title}', inline=False)

            embed.add_field(name="Price", value=f'{price}', inline=True)

            embed.add_field(name="Was", value=f'{was}', inline=True)

            embed.add_field(name="Discounted", value=f'{discount}', inline=True)

            embeds.append(embed)

        for embed in embeds:

            channel_id = '1041263119814631436';

            channel = discord.utils.get(client.get_all_channels(), id=channel_id)

            await channel.send(embed=embed)

        time.sleep(20)

async def main():
    async with client:
        client.loop.create_task(task())
        await client.run('***')

asyncio.run(main())
#client.run(os.environ['token'])

注:代码中的token已替换为***,期望实现每隔30分钟/秒推送一次优惠信息。


解决方案

错误核心原因

asyncio.run()会创建全新的事件循环,但Discord.py的client.run()本身已经负责启动并管理事件循环,嵌套调用就会触发该错误。此外代码还有几个影响功能的问题:

  • time.sleep()会阻塞异步事件循环,导致Bot无法响应其他指令或心跳
  • 引入的schedule库未实际使用,无法实现可靠定时
  • channel_id用字符串类型,Discord要求频道ID为整数

修复后的完整代码

import discord
import os
import requests
from bs4 import BeautifulSoup
from discord.ext import commands, tasks

intents = discord.Intents.default()
intents.message_content = True
# 不需要成员权限可关闭,减少权限申请
# intents.members = True
client = commands.Bot(intents=intents, command_prefix="!")

@client.event
async def on_ready():
    print(f'{client.user} 已上线!')
    # 启动定时任务,minutes=30为30分钟,改成seconds=30就是30秒
    scrape_bestbuy_deals.start()

@tasks.loop(minutes=30)
async def scrape_bestbuy_deals():
    embeds = []
    headers = {
        'User-Agent': 'Mozilla/5.0 (Linux; Android 10; SM-G980F Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.96 Mobile Safari/537.36'
    }

    try:
        response = requests.get('https://www.bestbuy.com/site/misc/deal-of-the-day/pcmcat248000050016.c?id=pcmcat248000050016', headers=headers)
        response.raise_for_status()  # 捕获HTTP请求错误
        soup = BeautifulSoup(response.content, 'html.parser')
        
        wf = soup.find('div', class_='wf-wrapper')
        if not wf:
            print("未找到优惠容器")
            return
        
        ofs = wf.find_all('div', class_='wf-offer')
        if not ofs:
            print("当前无优惠信息")
            return

        for of in ofs:
            # 容错处理:防止元素不存在导致报错
            title_tag = of.find('a', class_="wf-offer-link v-line-clamp ")
            title = title_tag.text.strip() if title_tag else "无标题"
            
            link_tag = of.find('a', class_='wf-offer-link')
            link = "https://bestbuy.com" + link_tag.get('href') if link_tag else "#"
            
            image_tag = of.find('img', class_="wf-image img-responsive")
            image = image_tag.get('src') if image_tag else ""
            
            price_tag = of.find('div', class_="priceView-hero-price priceView-customer-price")
            price = price_tag.find('span').text.strip() if price_tag and price_tag.find('span') else "无价格"
            
            was_tag = of.find('div', class_="pricing-price__regular-price")
            was = was_tag.text.strip() if was_tag else "无原价"
            
            discount_tag = of.find('div', class_="pricing-price__savings")
            discount = discount_tag.text.strip() if discount_tag else "无折扣信息"

            embed = discord.Embed(title=f'{title}', url=link, color=0xff9a03)
            if image:
                embed.set_thumbnail(url=image)
            embed.add_field(name="商品名称", value=f'{title}', inline=False)
            embed.add_field(name="现价", value=f'{price}', inline=True)
            embed.add_field(name="原价", value=f'{was}', inline=True)
            embed.add_field(name="折扣", value=f'{discount}', inline=True)
            embeds.append(embed)

        # 频道ID需为整数类型
        channel_id = 1041263119814631436
        channel = client.get_channel(channel_id)
        if not channel:
            print("未找到指定频道")
            return
        
        for embed in embeds:
            await channel.send(embed=embed)
            
    except Exception as e:
        print(f"爬取或推送出错: {str(e)}")

# 直接启动Bot,无需asyncio.run嵌套
client.run(os.environ.get('token', '***'))

关键修改说明

  • 用Discord.py自带的tasks.loop实现定时,替代手动循环+time.sleep(),不会阻塞事件循环
  • 移除asyncio.run()调用,直接使用client.run()启动Bot,避免事件循环冲突
  • 修正channel_id为整数类型,用client.get_channel()替代discord.utils.get(),查询更高效
  • 增加异常捕获和元素查找容错,避免单次爬取失败导致Bot崩溃
  • 移除未使用的schedule库和冗余变量

内容的提问来源于stack exchange,提问作者A.O.T GAMERZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 17:35:33