标签: Python

  • 使用Python3+Nginx写一个监控补货站点

    第一阶段:基础环境准备

    在开始之前,确保你的 Linux 服务器(Ubuntu/Debian 推荐)环境是干净且完备的。

    • 安装必要系统组件
    # 更新系统
    apt update && apt upgrade -y
    # 安装 Python 环境及工具
    apt install -y python3 python3-pip google-chrome-stable nginx curl psmisc
    • 安装 Python 依赖库
    # DrissionPage: 网页自动化
    # Flask/Flask-CORS: 订阅接口
    # pytz: 上海时区处理
    pip3 install DrissionPage Flask flask_cors pytz --break-system-packages
    

    1.5权限与防火墙配置

    在脚本运行前,必须打通 80 端口访问通道:

    • 创建目录并赋权(使用 nano 编辑时可直接执行以下命令):
    mkdir -p /var/www/html
    chown -R www-data:www-data /var/www/html
    chmod -R 755 /var/www/html
    
    • 放行端口(确保云服务器防火墙和系统防火墙双重放行):
    ufw allow 80/tcp    # 网页访问
    ufw allow 5000/tcp  # 订阅接口
    ufw allow 587/tcp   # 邮件发送

    第二阶段:核心代码部署

    我们需要在 /root 目录下创建两个 Python 文件。

    1. 订阅后端 (sub_server.py)

    nano /root/sub_server.py

    用于接收用户在网页上填写的邮箱。

    • 代码要点:监听 5000 端口,将邮箱写入 subscribers.txt
    • 启动命令nohup python3 /root/sub_server.py > /root/sub_server.log 2>&1 &

    2. 库存监测器 (spartan_monitor.py)

    nano /root/spartan_monitor.py

    这是最核心的脚本。

    • 代码要点
      • MONITOR_CONFIG: 填入你要监控的 PID(ProductID)。
      • MAIL_SETTINGS: 填入你的 QQ 邮箱和 16位授权码
      • SERVER_IP: 必须填入你的公网 IP,否则网页点击订阅会没反应。
      • SAVE_PATH: 默认 /var/www/html/index.html

    第三阶段:文件与目录结构检查

    请确认你的服务器文件布局如下:

    文件路径作用说明备注
    /root/spartan_monitor.py监测脚本手动运行一次测试逻辑
    /root/sub_server.py订阅 API必须保持后台持续运行
    /root/subscribers.txt邮箱数据库权限建议 644,每行一个邮箱
    /root/stock_status.json状态机缓存脚本自动生成,对比上次库存
    /var/www/html/index.html前端展示页确保 Nginx 拥有读取权限

    第四阶段:自动化与持久化 (关键点)

    • 让订阅接口永不掉线

    使用 nohup 启动后,即使你关闭 SSH 窗口,它也会运行。

    nohup python3 /root/sub_server.py > /root/sub_server.log 2>&1 &
    • 让监测脚本定时运行

    我们使用 Crontab 来实现每 5 分钟自动巡检一次。

    crontab -e

    在文件最下方加入:

    # 每5分钟运行一次监测脚本,并输出日志到 cron_log.log 方便排错
    */5 * * * * /usr/bin/python3 /root/spartan_monitor.py >> /root/cron_log.log 2>&1

    第五阶段:如何进行一次完整的“补货测试”?

    为了确认邮件能不能发出来,你不需要等官方补货,可以手动触发:

    • 添加测试邮箱:手动在 /root/subscribers.txt 填入你的测试邮箱。
    • 修改缓存骗过脚本
      • 打开 /root/stock_status.json
      • 找一个现在状态是 AVAILABLE 的产品,把值改成 "SOLD OUT"
    • 手动运行脚本
    python3 /root/spartan_monitor.py

    查看结果:脚本会发现状态从 “SOLD OUT” 变回了真实网页的 “AVAILABLE”,从而判定为“刚刚补货”,立刻发信。

    第六阶段:常见问题排查 (故障手册)

    • 网页时钟不走:检查网页源代码里的 iso_time 是否正确生成,且浏览器没报错。
    • 订阅显示“后端未连接”:检查服务器 5000 端口是否在防火墙(如腾讯云/阿里云的安全组)放行。
    • 邮件报 SSL 错误:确认代码里使用的是 server.starttls() 而不是 SMTP_SSL(对于 587 端口)。
    • DrissionPage 启动失败:通常是因为残留的 Chrome 进程太多,代码里的 pkill -9 chrome 会尝试清理,如果还不行,请增加服务器 Swap 交换内存。

    日常维护与测试

    • 文件与路径一览表
    文件路径作用运维操作
    /root/sub_server.py订阅 API若点击订阅没反应,重启此脚本
    /root/subscribers.txt邮箱数据库可用 nano 手动增删邮箱
    /root/stock_status.json库存记录删掉此文件可强制脚本触发一次提醒
    /var/www/html/index.html前端展示页直接浏览器访问 http://你的IP 查看
    • 如何重启订阅后端?

    如果你改了代码或者发现后端死掉了,先杀掉它再重启:

    pkill -f sub_server.py
    nohup python3 /root/sub_server.py > /root/sub_server.log 2>&1 &

    核心逻辑复习

    本系统采用 “状态机对比” 模式:

    • 第 1 步:从 stock_status.json 获取上一次状态。
    • 第 2 步:爬取 Spartan 官网获取当前状态。
    • 第 3 步:对比。只有 [之前是 SOLD OUT][现在是 AVAILABLE] 时,才会发送邮件。
    • 第 4 步:更新 JSON 文件和网页。

    这套流程现在已经刻在你的服务器里了。下一步:建议你在浏览器打开你的 IP 地址,亲自测试一下订阅功能是否能正常将邮箱写入 /root/subscribers.txt

    注意❗

    Traceback (most recent call last):
    
      File "/root/spartan_monitor.py", line 234, in <module>
    
        check_stock()
    
      File "/root/spartan_monitor.py", line 96, in check_stock
    
        page = ChromiumPage(co)
    
               ^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_pages/chromium_page.py", line 23, in __new__
    
        browser = Chromium(addr_or_opts=addr_or_opts)
    
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_base/chromium.py", line 41, in __new__
    
        is_headless, browser_id, is_exists, ws_only = run_browser(opt)
    
                                                      ^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_base/chromium.py", line 501, in run_browser
    
        is_exists = connect_browser(chromium_options)
    
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_functions/browser.py", line 38, in connect_browser
    
        raise BrowserConnectError(_S._lang.BROWSER_CONNECT_ERR1_, port, port, ADDRESS=address)
    
    DrissionPage.errors.BrowserConnectError: 
    
    Browser connect failed, check whether the port 9222 is a browser and "--remote-debugging-port=9222" startup item is added.
    
    Address: 127.0.0.1:9222
    
    Version: 4.1.1.2
    

    这个错误 DrissionPage.errors.BrowserConnectError 是自动化脚本中最常见的报错之一。它意味着 DrissionPage 无法连接到 Chrome 浏览器

    由于你是在 Linux 服务器上以 root 身份运行,通常由以下三个原因造成:

    1. 核心原因:Chrome 无法在 root 模式下正常启动

    Chrome 默认禁止以 root 身份运行,除非添加 --no-sandbox 参数。虽然代码里写了,但如果后台有残留的 Chrome 进程(死锁状态),DrissionPage 尝试启动新实例时会失败。

    解决方法:彻底清理残留进程 在终端运行:

    # 强制杀死所有 Chrome 进程
    pkill -9 chrome
    pkill -9 chromium

    2. 环境兼容性:缺少运行依赖

    在 Linux (Ubuntu/Debian) 上运行 Headless Chrome 需要一些底层的图形库支持(即使你不使用显示器)。

    解决方法:补充安装依赖

    apt-get update
    apt-get install -y libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 libasound2

    3. 代码微调:显式指定无沙盒模式

    请使用 nano /root/spartan_monitor.py 检查你的 check_stock 函数,确保 co (ChromiumOptions) 的设置部分是这样的:

    def check_stock():
        # 强制清理(双重保险)
        os.system("pkill -9 chrome > /dev/null 2>&1")
        
        co = ChromiumOptions()
        co.set_browser_path(CHROME_PATH)
        
        # 必须包含以下几项
        co.set_argument('--no-sandbox')            # root 运行必备
        co.set_argument('--headless')              # 服务器无界面必备
        co.set_argument('--disable-gpu')           # 减少资源占用
        co.set_argument('--disable-dev-shm-usage') # 防止内存不足导致的崩溃
        co.set_argument('--remote-debugging-port=9222') # 显式指定端口
        
        # 如果还是报错,尝试添加这一行,指定一个干净的用户数据目录
        co.set_user_data_path('/tmp/chrome_user_data')
        
        page = ChromiumPage(co)
        # ... 后续代码

    4. 权限问题:/tmp 目录

    有时候 Chrome 无法在默认的 /tmp 目录下创建临时文件。 你可以尝试手动创建刚才指定的目录并赋权:

    mkdir -p /tmp/chrome_user_data
    chmod -R 777 /tmp/chrome_user_data
    

    验证步骤

    修改完代码并清理进程后,直接手动运行:

    python3 /root/spartan_monitor.py

    如果看到以下输出,说明修好了: [2025-xx-xx] 检查中...