当前位置:首页>python>第21篇|用 Python 写一个网页数据监控脚本:降价了自动通知我

第21篇|用 Python 写一个网页数据监控脚本:降价了自动通知我

  • 2026-04-13 08:47:39
第21篇|用 Python 写一个网页数据监控脚本:降价了自动通知我

这篇文章把爬虫、数据存储、价格对比、邮件通知、定时任务五个能力合并成一个完整项目——商品降价监控器。代码可直接运行,场景真实,技术门槛不高。


前言

前两篇我们分别学了爬虫(第 15 篇)和自动化通知(第 20 篇)。这一篇把它们组合起来,做一个真正能解决现实问题的东西:商品降价监控器

逻辑很简单:

  1. 定时爬取目标商品页面的价格
  2. 和上次的价格对比
  3. 如果降价了,发邮件通知你
  4. 把每次的价格记录存到 JSON 文件,顺便画个趋势图

整个项目不超过 250 行代码,用到的技术全是前面讲过的:requestsBeautifulSoupjsonsmtplibschedulematplotlib


一、项目设计

price_monitor/
├── monitor.py          # 主程序
├── config.json         # 监控配置(监控哪些商品、通知设置)
├── price_history.json  # 价格历史记录(自动维护)
├── .env                # 邮箱密码等敏感信息
└── requirements.txt

requirements.txt:

requests
beautifulsoup4
schedule
matplotlib
python-dotenv

二、配置文件设计

// config.json
{
  "targets": [
    {
      "id": "book_python",
      "name": "Python编程从入门到实践",
      "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
      "selector": "p.price_color",
      "threshold": 0.05,
      "comment": "价格下降超过 5% 才通知"
    }
  ],
  "notify": {
    "email": "your_email@qq.com"
  },
  "check_interval_minutes": 60
}

用配置文件而不是硬编码的好处:想监控新商品只需改 JSON,不用动代码。


三、核心模块:价格抓取

# monitor.py
import json
import os
import time
import smtplib
import schedule
import matplotlib
matplotlib.use("Agg")   # 无界面环境下生成图片
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import requests
from bs4 import BeautifulSoup
from pathlib import Path
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from email.header import Header
from dotenv import load_dotenv

load_dotenv()

# ── 配置 ──
CONFIG_FILE = "config.json"
HISTORY_FILE = "price_history.json"
FROM_EMAIL = os.getenv("FROM_EMAIL")
EMAIL_PASSWORD = os.getenv("EMAIL_PASSWORD")


def load_config() -> dict:
    with open(CONFIG_FILE, encoding="utf-8") as f:
        return json.load(f)


def load_history() -> dict:
    """加载价格历史,文件不存在则返回空字典"""
    path = Path(HISTORY_FILE)
    if not path.exists():
        return {}
    with open(path, encoding="utf-8") as f:
        return json.load(f)


def save_history(history: dict) -> None:
    with open(HISTORY_FILE, "w", encoding="utf-8") as f:
        json.dump(history, f, indent=2, ensure_ascii=False)


def fetch_price(url: str, selector: str) -> float | None:
    """
    抓取目标页面的价格

    Args:
        url: 商品页面 URL
        selector: CSS 选择器,指向价格元素

    Returns:
        价格数字,失败返回 None
    """
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
                      "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    }
    try:
        response = requests.get(url, headers=headers, timeout=15)
        response.raise_for_status()

        soup = BeautifulSoup(response.text, "html.parser")
        element = soup.select_one(selector)

        if not element:
            print(f"  ⚠️ 找不到价格元素:{selector}")
            return None

        # 提取数字(去掉货币符号、空格、换行)
        price_text = element.get_text(strip=True)
        price_text = price_text.replace("£", "").replace("¥", "").replace(",", "").strip()

        return float(price_text)

    except requests.RequestException as e:
        print(f"  ❌ 网络请求失败:{e}")
    except ValueError as e:
        print(f"  ❌ 价格解析失败:{price_text!r} → {e}")

    return None

四、价格对比与告警判断

def check_price_drop(
    item_id: str,
    item_name: str,
    current_price: float,
    history: dict,
    threshold: float = 0.05
) -> dict | None:
    """
    检查价格是否下降

    Returns:
        如果触发通知,返回通知信息字典;否则返回 None
    """
    # 初始化该商品的历史记录
    if item_id not in history:
        history[item_id] = {
            "name": item_name,
            "records": []
        }

    records = history[item_id]["records"]
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")

    # 记录本次价格
    records.append({
        "time": timestamp,
        "price": current_price
    })

    # 只有超过 1 条记录才能比较
    if len(records) < 2:
        print(f"  📝 首次记录价格:{current_price}")
        return None

    # 获取上次价格
    last_price = records[-2]["price"]
    change = (current_price - last_price) / last_price   # 负数表示降价

    print(f"  价格变化:{last_price} → {current_price}({change*100:+.1f}%)")

    # 历史最低价
    all_prices = [r["price"] for r in records]
    min_price = min(all_prices[:-1])   # 不含本次
    is_historical_low = current_price < min_price

    # 触发通知条件:降价幅度超过阈值,或达到历史最低
    if change <= -threshold or is_historical_low:
        return {
            "name": item_name,
            "last_price": last_price,
            "current_price": current_price,
            "change_pct": change * 100,
            "min_price": min(all_prices),
            "is_historical_low": is_historical_low,
            "timestamp": timestamp
        }

    return None

五、生成价格趋势图

def generate_price_chart(item_id: str, item_name: str, records: list) -> str | None:
    """
    用 matplotlib 生成价格趋势折线图

    Returns:
        生成的图片路径,失败返回 None
    """
    if len(records) < 2:
        return None

    try:
        times = [datetime.strptime(r["time"], "%Y-%m-%d %H:%M") for r in records]
        prices = [r["price"] for r in records]
        min_price = min(prices)
        min_idx = prices.index(min_price)

        fig, ax = plt.subplots(figsize=(10, 4))
        fig.patch.set_facecolor("
#f8f9fa")
        ax.set_facecolor("#ffffff")

        # 主折线
        ax.plot(times, prices, color="#3b82f6", linewidth=2, marker="o",
                markersize=4, zorder=3, label="价格")

        # 填充区域(折线下方)
        ax.fill_between(times, prices, alpha=0.1, color="#3b82f6")

        # 标记最低点
        ax.scatter([times[min_idx]], [min_price], color="#ef4444", s=100,
                   zorder=5, label=f"历史最低 {min_price}")
        ax.annotate(
            f"历史最低\n{min_price}",
            xy=(times[min_idx], min_price),
            xytext=(15, 15),
            textcoords="offset points",
            fontsize=9,
            color="#ef4444",
            arrowprops=dict(arrowstyle="->", color="#ef4444")
        )

        # 标注最新价格
        ax.annotate(
            f"当前\n{prices[-1]}",
            xy=(times[-1], prices[-1]),
            xytext=(-40, 15),
            textcoords="offset points",
            fontsize=9,
            color="#1d4ed8"
        )

        # 样式
        ax.set_title(f"{item_name} 价格趋势", fontsize=13, pad=12,
                     fontproperties="SimHei" if os.name == "nt" else None)
        ax.set_xlabel("时间", fontsize=10)
        ax.set_ylabel("价格", fontsize=10)
        ax.xaxis.set_major_formatter(mdates.DateFormatter("%m/%d"))
        ax.xaxis.set_major_locator(mdates.AutoDateLocator())
        plt.xticks(rotation=30)
        ax.grid(axis="y", linestyle="--", alpha=0.5)
        ax.legend(fontsize=9)
        plt.tight_layout()

        # 保存图片
        chart_dir = Path("charts")
        chart_dir.mkdir(exist_ok=True)
        chart_path = str(chart_dir / f"{item_id}_trend.png")
        plt.savefig(chart_path, dpi=120, bbox_inches="tight")
        plt.close()

        return chart_path

    except Exception as e:
        print(f"  ⚠️ 图表生成失败:{e}")
        return None

六、发送降价通知邮件

def send_alert_email(
    to_addr: str,
    alerts: list[dict],
    chart_paths: list[str]
) -> bool:
    """发送降价通知邮件(含趋势图附件)"""

    # ── 构建 HTML 邮件正文 ──
    alert_cards = ""
    for alert in alerts:
        badge = "🏆 历史最低!" if alert["is_historical_low"] else "📉 价格下降"
        change_color = "#dc2626"
        alert_cards += f"""
        <div style="border:1px solid #e5e7eb;border-radius:8px;padding:16px;margin:12px 0;background:#fff;">
            <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
                <strong style="font-size:15px;color:#111;">{alert['name']}</strong>
                <span style="background:#fef2f2;color:#dc2626;padding:2px 8px;border-radius:12px;font-size:12px;">{badge}</span>
            </div>
            <div style="display:flex;gap:24px;font-size:14px;">
                <div><span style="color:#6b7280;">上次价格</span><br><strong>{alert['last_price']}</strong></div>
                <div style="font-size:20px;line-height:40px;color:#9ca3af;">→</div>
                <div><span style="color:#6b7280;">当前价格</span><br><strong style="color:{change_color};font-size:18px;">{alert['current_price']}</strong></div>
                <div><span style="color:#6b7280;">降价幅度</span><br><strong style="color:{change_color};">{alert['change_pct']:.1f}%</strong></div>
                <div><span style="color:#6b7280;">历史最低</span><br><strong>{alert['min_price']}</strong></div>
            </div>
        </div>
        """

    html_body = f"""
    <!DOCTYPE html><html><body style="font-family:system-ui,sans-serif;max-width:600px;margin:0 auto;padding:20px;background:#f9fafb;">
        <div style="background:#fff;border-radius:12px;padding:24px;box-shadow:0 1px 3px rgba(0,0,0,0.1);">
            <h2 style="margin:0 0 4px;color:#dc2626;">🔔 降价提醒</h2>
            <p style="color:#6b7280;font-size:13px;margin:0 0 20px;">
                监控到 {len(alerts)} 个商品价格下降 · {datetime.now().strftime('%Y-%m-%d %H:%M')}
            </p>
            {alert_cards}
            <p style="color:#9ca3af;font-size:12px;margin-top:20px;border-top:1px solid #f3f4f6;padding-top:12px;">
                价格趋势图已作为附件发送 · 由 Python 价格监控助手自动发送
            </p>
        </div>
    </body></html>
    """

    try:
        msg = MIMEMultipart("mixed")
        msg["From"] = Header(f"价格监控助手 <{FROM_EMAIL}>", "utf-8")
        msg["To"] = to_addr
        msg["Subject"] = Header(f"🔔 降价提醒:{alerts[0]['name']} 等 {len(alerts)} 件商品", "utf-8")

        msg.attach(MIMEText(html_body, "html", "utf-8"))

        # 附加趋势图
        for chart_path in chart_paths:
            if chart_path and Path(chart_path).exists():
                with open(chart_path, "rb") as f:
                    part = MIMEBase("image", "png")
                    part.set_payload(f.read())
                encoders.encode_base64(part)
                part.add_header(
                    "Content-Disposition",
                    f"attachment; filename={Path(chart_path).name}"
                )
                msg.attach(part)

        with smtplib.SMTP_SSL("smtp.qq.com", 465) as server:
            server.login(FROM_EMAIL, EMAIL_PASSWORD)
            server.sendmail(FROM_EMAIL, [to_addr], msg.as_string())

        print(f"  ✅ 降价通知已发送 → {to_addr}")
        return True

    except Exception as e:
        print(f"  ❌ 邮件发送失败:{e}")
        return False

七、主监控循环

def run_check() -> None:
    """执行一轮价格检查"""
    print(f"\n{'='*50}")
    print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 开始价格巡检")

    config = load_config()
    history = load_history()
    alerts = []
    chart_paths = []

    for target in config["targets"]:
        item_id = target["id"]
        item_name = target["name"]
        print(f"\n🔍 检查:{item_name}")

        # 抓取当前价格
        price = fetch_price(target["url"], target["selector"])
        if price is None:
            print(f"  ⚠️ 价格获取失败,跳过")
            continue

        print(f"  💰 当前价格:{price}")

        # 检查是否需要告警
        alert = check_price_drop(
            item_id=item_id,
            item_name=item_name,
            current_price=price,
            history=history,
            threshold=target.get("threshold", 0.05)
        )

        if alert:
            alerts.append(alert)
            # 生成趋势图
            chart = generate_price_chart(
                item_id, item_name,
                history[item_id]["records"]
            )
            if chart:
                chart_paths.append(chart)
                print(f"  📊 趋势图已生成:{chart}")

        # 礼貌间隔
        time.sleep(2)

    # 保存更新后的历史记录
    save_history(history)
    print(f"\n💾 价格历史已更新")

    # 发送通知
    if alerts:
        print(f"\n🚨 触发 {len(alerts)} 条降价通知,准备发送邮件...")
        send_alert_email(
            to_addr=config["notify"]["email"],
            alerts=alerts,
            chart_paths=chart_paths
        )
    else:
        print(f"\n✅ 巡检完成,价格无异常变动")


def main() -> None:
    config = load_config()
    interval = config.get("check_interval_minutes", 60)

    print("🚀 价格监控助手已启动")
    print(f"   监控商品:{len(config['targets'])} 件")
    print(f"   检查间隔:每 {interval} 分钟")
    print(f"   通知邮箱:{config['notify']['email']}")

    # 立即执行一次
    run_check()

    # 设置定时
    schedule.every(interval).minutes.do(run_check)
    print(f"\n⏰ 定时任务已设置,下次检查:{schedule.next_run()}")

    while True:
        schedule.run_pending()
        time.sleep(30)


if __name__ == "__main__":
    main()

八、运行效果

# 启动监控
python monitor.py

# 输出:
# 🚀 价格监控助手已启动
#    监控商品:1 件
#    检查间隔:每 60 分钟
#    通知邮箱:your@email.com
# [2025-03-07 08:00:00] 开始价格巡检
# 🔍 检查:Python编程从入门到实践
#   💰 当前价格:42.5
#   价格变化:47.0 → 42.5(-9.6%)
# 🚨 触发 1 条降价通知,准备发送邮件...
#   📊 趋势图已生成:charts/book_python_trend.png
#   ✅ 降价通知已发送 → your@email.com
# ⏰ 定时任务已设置,下次检查:2025-03-07 09:00:00

小结

这个项目把前面多篇的技术串联起来:

技术
用途
来自哪篇
requests
 + BeautifulSoup
抓取商品价格
第 13、15 篇
json
 文件读写
持久化价格历史
第 07 篇
smtplib
发送降价通知邮件
第 20 篇
schedule
定时执行检查
第 20 篇
matplotlib
生成价格趋势图
新增
python-dotenv
管理敏感配置
第 20 篇

扩展思路:

  • 把监控目标扩展到多个电商平台(需要适配各自的 CSS 选择器)
  • 增加微信通知(用 Server 酱,免费)
  • 把价格数据存到 SQLite 数据库替代 JSON
  • 部署到服务器,实现 7×24 小时监控

下篇预告

第 22 篇:Python 项目打包与部署:让别人也能用你写的工具

本地跑通只是第一步,下一篇讲怎么把 Python 项目打包、容器化、部署到云服务器,让任何人都能通过浏览器访问你的工具。


最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-15 14:55:28 HTTP/2.0 GET : https://f.mffb.com.cn/a/486161.html
  2. 运行时间 : 0.087551s [ 吞吐率:11.42req/s ] 内存消耗:4,765.80kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=0d4f6d3d79c88644cc581d1c7d547995
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000509s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000836s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000324s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000252s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000462s ]
  6. SELECT * FROM `set` [ RunTime:0.000211s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000614s ]
  8. SELECT * FROM `article` WHERE `id` = 486161 LIMIT 1 [ RunTime:0.000534s ]
  9. UPDATE `article` SET `lasttime` = 1776236128 WHERE `id` = 486161 [ RunTime:0.011829s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000364s ]
  11. SELECT * FROM `article` WHERE `id` < 486161 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000603s ]
  12. SELECT * FROM `article` WHERE `id` > 486161 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000454s ]
  13. SELECT * FROM `article` WHERE `id` < 486161 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002062s ]
  14. SELECT * FROM `article` WHERE `id` < 486161 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001596s ]
  15. SELECT * FROM `article` WHERE `id` < 486161 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.001548s ]
0.089101s