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
相关产品推荐
相关产品推荐

