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

Discord机器人仅在爬取数据更新时发送消息的实现问题求助

解决Discord机器人仅在数据更新时发送消息的问题

我来帮你搞定这个需求!你的核心目标是让机器人启动时发一次初始价格数据,之后只在物品价格变化时推送更新——咱们先梳理下当前代码的问题,再给出优化方案。

当前代码的核心问题

  1. 没有初始化对比用的旧数据字典:dict_names_second完全没定义,第一次对比直接会报错
  2. 爬取逻辑在循环外:只会在启动时爬一次数据,之后循环里一直对比同一个旧字典,永远不会检测到更新
  3. 列表未清空:list_prices每次爬取前没清空,会导致数据越积越多,物品和价格的对应关系很快就乱了
  4. 缺少初始消息发送:启动时不会主动推送第一版数据
  5. 消息太冗余:就算有更新,也会发送整个字典,用户看不到具体哪项变了

优化后的实现代码

我把代码重构得更清晰、健壮,完全符合你的需求:

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")

关键改进点说明

  1. 封装爬取逻辑:把数据获取放到独立函数里,代码更易维护,还加了异常处理避免爬取失败导致任务崩溃
  2. 使用官方任务装饰器:@tasks.loop比手动写while循环更符合Discord.py的最佳实践,支持暂停、重启等操作
  3. 精准对比变化:只找出价格变动的物品,发送针对性的更新消息,用户体验更好
  4. 初始化与数据同步:启动时自动发送初始数据,每次更新后同步旧数据,确保后续对比准确
  5. 容错处理:增加了频道不存在、爬取失败的判断,避免机器人崩溃

额外优化建议

如果网页结构允许,强烈建议不要用索引匹配物品和价格——可以找到每个物品的父容器,同时提取物品名称和价格,比如:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:12:46