使用discord.py开发mcoc.py cog命令时触发RuntimeError: This event loop is already running错误的原因与修复方案咨询
解决discord.py命令中requests-html渲染时的RuntimeError问题
错误原因
你碰到的这个RuntimeError: This event loop is already running,核心问题在于discord.py本身已经在运行一个asyncio事件循环,而requests-html的render()方法底层会尝试调用loop.run_until_complete()去启动另一个独立的事件循环。但asyncio的规则是同一个线程里不能同时运行多个事件循环,这就直接导致了冲突报错。
另外顺便提一句,你代码里有个小笔误:champname=="blackwidow_timely"是比较操作,不是赋值,应该改成champname = "blackwidow_timely",不然这个别名替换根本不会生效。
修复方案
方案1:用nest_asyncio允许嵌套事件循环(快速解决)
这是最省事的临时解决方案,只需要引入nest_asyncio库来让asyncio支持嵌套运行事件循环:
- 先安装库:
pip install nest_asyncio
- 在你的
mcoc.py文件开头添加:
import nest_asyncio nest_asyncio.apply()
把这段代码放在所有导入语句之后、命令定义之前就行。它会修改asyncio的默认行为,允许在已运行的事件循环里再启动新的循环,从而解决冲突。
方案2:改用纯异步的网页处理流程(更稳定推荐)
requests-html的异步封装有时候和discord.py的事件循环兼容性不佳,更稳妥的方式是直接用pyppeteer(无头浏览器)做异步渲染,再用BeautifulSoup解析内容。这样完全贴合discord.py的异步生态,不会有循环冲突问题:
首先安装依赖:
pip install pyppeteer beautifulsoup4 aiohttp
然后修改你的命令代码:
import pyppeteer from bs4 import BeautifulSoup @commands.command() async def mcoc(self, ctx, tier_str:int,*,champname:str): champname = champname.strip().lower() # 修正笔误:把比较改成赋值 if champname == "bwcv": champname = "blackwidow_timely" url = f"https://auntm.ai/champions/{champname}/tier/{tier_str}" # 异步启动无头浏览器并渲染页面 browser = await pyppeteer.launch(headless=True) page = await browser.newPage() await page.goto(url) await page.waitForTimeout(1000) # 等待1秒让内容加载 await page.evaluate('window.scrollTo(0, document.body.scrollHeight)') # 滚动到底部 html_content = await page.content() await browser.close() # 解析HTML内容 soup = BeautifulSoup(html_content, 'html.parser') # 注意:网站的动态类名可能会变,建议后续换成更稳定的选择器(比如元素属性/层级) info_element = soup.find("div", class_="sc-hiSbYr XqbgT") name_element = soup.find('div', class_="sc-cxFLnm izBVou") # 处理可能的元素未找到情况 champ_display_name = name_element.text.strip() if name_element else champname.title() tier_info = info_element.text.strip() if info_element else "无法获取Tier信息" # 发送Embed(修正typing的语法错误:要加括号) async with ctx.typing(): embed = discord.Embed(title=f"{champ_display_name}", description=f"More Information about {champ_display_name} can be found [here]({url})") # 可以把Tier信息加到Embed里 embed.add_field(name="Tier信息", value=tier_info, inline=False) await ctx.send(embed=embed)
额外注意事项
- 网站的动态生成类名(比如
sc-hiSbYr XqbgT)很可能会在网站更新后失效,建议后续用更稳定的选择方式,比如基于元素的id、data-*属性,或者元素的层级结构来定位。 - 你原来的代码里
ctx.typing()少了括号,应该写成async with ctx.typing():,否则会报错。 - 方案2虽然代码多一点,但完全基于异步原生实现,长期来看更稳定,不容易出现奇怪的异步冲突问题。
内容的提问来源于stack exchange,提问作者Dheeraj-Tech
相关产品推荐
相关产品推荐

