Discord机器人仅在爬取数据更新时发送消息的实现问题求助
解决Discord机器人仅在数据更新时发送消息的问题
我来帮你搞定这个需求!你的核心目标是让机器人启动时发一次初始价格数据,之后只在物品价格变化时推送更新——咱们先梳理下当前代码的问题,再给出优化方案。
当前代码的核心问题
- 没有初始化对比用的旧数据字典:
dict_names_second完全没定义,第一次对比直接会报错 - 爬取逻辑在循环外:只会在启动时爬一次数据,之后循环里一直对比同一个旧字典,永远不会检测到更新
- 列表未清空:
list_prices每次爬取前没清空,会导致数据越积越多,物品和价格的对应关系很快就乱了 - 缺少初始消息发送:启动时不会主动推送第一版数据
- 消息太冗余:就算有更新,也会发送整个字典,用户看不到具体哪项变了
优化后的实现代码
我把代码重构得更清晰、健壮,完全符合你的需求:
import requests import asyncio from bs4 import BeautifulSoup from discord.ext import tasks, commands client = commands.Bot(command_prefix="!", intents=discord.Intents.default()) # 全局变量存储旧价格数据 old_prices = {} def fetch_current_prices(): """封装爬取逻辑,返回当前价格字典""" try: URL = "https://eu.tamrieltradecentre.com/pc/Trade" # 添加超时避免卡住,添加状态码检查确保请求成功 page = requests.get(URL, timeout=10) page.raise_for_status() soup = BeautifulSoup(page.text, 'html.parser') list_prices = [] for item in soup.find_all(class_="gold-amount"): list_prices.append(item.get_text(strip=True)) # 构建价格字典(如果网页结构允许,建议后续改成根据物品名自动匹配,避免索引错位) price_dict = { "Dreugh Wax": list_prices[0], "Style Page: Pirate Skeletons Mask": list_prices[1], "Style Page: Opal Engine Guardian Staff": list_prices[2], "Tempering Alloy": list_prices[3], "Chromium Plating": list_prices[4], "Zircon Plating": list_prices[5], "Perfect Roe": list_prices[6], "Style Page: Opal Engine Guardian Dagger": list_prices[7], "Kuta": list_prices[8], "Heartwood": list_prices[9], "Mundane Rune": list_prices[10], "Style Page: Opal Engine Guardian Greatsword": list_prices[11], "Hakeijo": list_prices[12], "Style Page: Opal Ilambris Sword": list_prices[13], "Potent Nirncrux": list_prices[14], "Aetherial Dust": list_prices[15], "Sealed Woodworking Writ": list_prices[16], "Columbine": list_prices[17], "Platinum Ounce": list_prices[18], "Rosin": list_prices[19] } return price_dict except Exception as e: print(f"爬取数据失败:{str(e)}") return None @tasks.loop(seconds=60) async def price_monitor(): """定时监控价格变化的任务""" channel = client.get_channel(1024849506258407454) if not channel: print("无法找到目标频道,请检查频道ID是否正确!") return current_prices = fetch_current_prices() if not current_prices: # 爬取失败时跳过本次循环,避免报错 await asyncio.sleep(10) return global old_prices # 第一次运行(机器人启动):发送初始数据 if not old_prices: initial_msg = "📊 初始物品价格数据:\n" initial_msg += "\n".join([f"• {item}: {price}" for item, price in current_prices.items()]) await channel.send(initial_msg) old_prices = current_prices.copy() return # 对比新旧数据,找出变化的项 changes = [] for item, new_price in current_prices.items(): old_price = old_prices.get(item) if old_price and old_price != new_price: changes.append(f"🔄 {item}: {old_price} → {new_price}") # 有变化时发送更新消息 if changes: update_msg = "📈 物品价格更新:\n" update_msg += "\n".join(changes) await channel.send(update_msg) old_prices = current_prices.copy() # 更新旧数据为当前最新数据 @client.event async def on_ready(): print(f"已登录为 {client.user}") # 启动价格监控任务 price_monitor.start() # 替换成你的机器人Token client.run("YOUR_BOT_TOKEN")
关键改进点说明
- 封装爬取逻辑:把数据获取放到独立函数里,代码更易维护,还加了异常处理避免爬取失败导致任务崩溃
- 使用官方任务装饰器:
@tasks.loop比手动写while循环更符合Discord.py的最佳实践,支持暂停、重启等操作 - 精准对比变化:只找出价格变动的物品,发送针对性的更新消息,用户体验更好
- 初始化与数据同步:启动时自动发送初始数据,每次更新后同步旧数据,确保后续对比准确
- 容错处理:增加了频道不存在、爬取失败的判断,避免机器人崩溃
额外优化建议
如果网页结构允许,强烈建议不要用索引匹配物品和价格——可以找到每个物品的父容器,同时提取物品名称和价格,比如:
def fetch_current_prices(): try: # ... 省略请求部分 ... price_dict = {} # 假设每个物品的容器class是"trade-item-row",需要根据实际网页结构调整 for item_row in soup.find_all(class_="trade-item-row"): item_name = item_row.find(class_="item-name").get_text(strip=True) item_price = item_row.find(class_="gold-amount").get_text(strip=True) price_dict[item_name] = item_price return price_dict except Exception as e: print(f"爬取失败:{e}") return None
这种方式即使网页上物品顺序变化,也能正确匹配名称和价格,稳定性更高。
内容的提问来源于stack exchange,提问作者Yassin Bahri
相关产品推荐
相关产品推荐

