实操落地|Python 7×24小时不间断电商价格监控,代理IP全自动切换

做电商数据分析、竞品监控、低价捡漏的朋友,大概率都踩过同一个大坑:批量爬取商品价格,跑一会就被平台风控封禁IP

我之前试过单IP轮询采集、手动换代理,要么监控中断、要么人工成本太高,完全没法实现7×24小时无人值守监控。折腾了很久,最终打磨出这套全自动代理切换的电商价格监控脚本

全程实测可落地、代码健壮性拉满,自带代理自动更新、失效剔除、异常重启、容错重试机制,挂机运行数月稳定不崩,今天完整开源分享给大家。

先说核心痛点 & 方案优势

主流电商(淘宝、京东、拼多多)风控都很严格,单一IP高频请求,轻则限流、返回空数据,重则直接拉黑IP,价格监控直接中断。

这套方案完美解决以上问题,我实测总结出几个核心亮点:

  • 全程无人值守:全局异常捕获+自动重启,真正实现724小时不间断采集

  • 代理全自动托管:对接代理API,自动拉取、校验、更新、切换IP,失效IP即时剔除

  • 高容错稳运行:单次采集失败自动换IP重试,杜绝单点报错停机

  • 轻量化低消耗:无需复杂环境,服务器、本地、虚拟机均可挂机

  • 可自由拓展:支持多商品批量监控,可对接数据库、价格预警、消息推送

整体实现思路

我整套架构采用 定时轮询采集 + 动态代理池 + 异常自愈重启 的逻辑,流程非常清晰:

  1. 定时调用代理API,批量获取新鲜代理IP资源

  2. 对所有代理做可用性校验,过滤超时、失效、无效IP,维护优质代理池

  3. 每次商品价格请求,随机抽取代理池IP,避免固定IP高频访问

  4. 捕获风控、超时、请求异常,自动剔除失效IP并重试采集

  5. 全程日志记录,留存采集数据、异常信息,方便复盘排错

  6. 程序异常崩溃自动重启,实现永久挂机运行

环境依赖准备

整个脚本仅依赖两个第三方库,无冗余依赖,安装极简:

pip install requests schedule

代理方面,脚本内置站大爷代理API对接逻辑,大家只需替换成自己的专属API链接,即可自动批量提取代理IP,适配长期高频采集场景。

完整可落地源码

这段代码是我多次迭代后的最终版本,修复了网上很多开源脚本的通病:代理堆积不清理、报错直接崩、无重试机制、代理校验失效等问题。复制改配置即可直接运行

import requests
import schedule
import time
import random
import logging
from typing import List, Optional

# ====================== 自定义配置区(仅需改这里)======================
# 站大爷代理API链接(替换为自己的站大爷API地址)
PROXY_API_URL = "https://www.zdaye.com/api/xxxxxx"
# 商品价格采集间隔(单位:秒,建议30s以上,规避高频风控)
COLLECT_INTERVAL = 30
# 代理池刷新更新间隔(单位:秒,默认5分钟更新一次)
PROXY_UPDATE_INTERVAL = 300
# 请求超时时间
TIMEOUT = 10
# 待监控的电商商品链接(可无限新增)
MONITOR_URLS = [
    "https://item.jd.com/xxxx.html",
    "https://item.taobao.com/item.htm?id=xxxx"
]
# =====================================================================

# 日志全局配置(控制台+本地双留存,编码防乱码)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s",
    handlers=[
        logging.FileHandler("ec_price_monitor.log", encoding="utf-8"),
        logging.StreamHandler()
    ]
)

# 全局有效代理池(仅留存校验通过的可用IP)
VALID_PROXY_POOL: List[str] = []


def get_proxy_from_api() -> List[str]:
    """
    调用代理API批量获取IP端口
    return: 格式化后的代理列表 [http://ip:port]
    """
    try:
        resp = requests.get(PROXY_API_URL, timeout=TIMEOUT)
        resp.raise_for_status()
        # 适配API换行分隔的返回格式,清洗无效数据
        raw_proxy_list = resp.text.strip().split("\n")
        format_proxy_list = []
        for proxy in raw_proxy_list:
            proxy = proxy.strip()
            if proxy and ":" in proxy:
                format_proxy_list.append(f"http://{proxy}")
        logging.info(f"API拉取代理成功,本次获取:{len(format_proxy_list)} 个")
        return format_proxy_list
    except Exception as e:
        logging.error(f"代理API拉取失败:{str(e)}")
        return []


def check_proxy_valid(proxy: str) -> bool:
    """
    单点代理可用性校验
    访问百度测速,超时/报错直接判定失效
    """
    test_url = "https://www.baidu.com"
    proxy_dict = {"http": proxy, "https": proxy}
    try:
        requests.get(test_url, proxies=proxy_dict, timeout=TIMEOUT)
        return True
    except Exception:
        return False


def refresh_proxy_pool() -> None:
    """刷新全局代理池,过滤无效IP,保证池内全是可用代理"""
    global VALID_PROXY_POOL
    new_proxies = get_proxy_from_api()
    # 批量校验筛选有效代理
    usable_proxies = [p for p in new_proxies if check_proxy_valid(p)]
    VALID_PROXY_POOL = usable_proxies
    logging.info(f"代理池刷新完成,当前有效代理数量:{len(VALID_PROXY_POOL)}")


def get_random_usable_proxy() -> Optional[dict]:
    """随机获取一个有效代理,代理池为空则自动刷新"""
    if not VALID_PROXY_POOL:
        logging.warning("代理池为空,自动触发刷新机制...")
        refresh_proxy_pool()
        if not VALID_PROXY_POOL:
            logging.error("暂无可用代理,等待下次刷新")
            return None
    random_proxy = random.choice(VALID_PROXY_POOL)
    return {"http": random_proxy, "https": random_proxy}


def parse_shop_price(html: str, url: str) -> Optional[str]:
    """
    通用价格解析适配函数
    可根据京东/淘宝/拼多多页面规则自定义拓展
    """
    # 京东价格简易解析规则
    if "jd.com" in url:
        import re
        res = re.search(r'"price":"?(\d+\.\d+)"?', html)
        if res:
            return res.group(1)
    # 可自行添加淘宝、拼多多解析规则
    return None


def collect_single_goods(url: str) -> None:
    """单商品价格采集核心逻辑,带失败重试、IP剔除机制"""
    proxy = get_random_usable_proxy()
    if not proxy:
        logging.error(f"{url}|无可用代理,跳过本次采集")
        return

    # 模拟真实浏览器请求头,规避基础风控
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Referer": "https://www.baidu.com/"
    }

    try:
        # 首次采集请求
        resp = requests.get(url, headers=headers, proxies=proxy, timeout=TIMEOUT)
        resp.raise_for_status()
        price = parse_shop_price(resp.text, url)
        if price:
            logging.info(f"采集成功|{url}|当前价格:{price} 元")
        else:
            logging.info(f"页面访问成功,未解析到价格|{url}")

    except Exception as e:
        # 采集失败,剔除失效代理并重试一次
        bad_proxy = proxy["http"]
        if bad_proxy in VALID_PROXY_POOL:
            VALID_PROXY_POOL.remove(bad_proxy)
            logging.warning(f"代理失效已剔除|{bad_proxy}|错误:{str(e)[:50]}")

        # 二次重试机制
        retry_proxy = get_random_usable_proxy()
        if retry_proxy:
            try:
                resp = requests.get(url, headers=headers, proxies=retry_proxy, timeout=TIMEOUT)
                price = parse_shop_price(resp.text, url)
                logging.info(f"重试成功|{url}|价格:{price if price else '未获取'}")
            except Exception as retry_e:
                logging.error(f"重试采集失败|{url}|{str(retry_e)[:50]}")


def batch_collect_task() -> None:
    """批量采集所有监控商品,随机休眠模拟真人行为"""
    logging.info("========== 开启新一轮价格采集 ==========")
    for goods_url in MONITOR_URLS:
        collect_single_goods(goods_url)
        # 1-3秒随机休眠,规避高频指纹风控
        time.sleep(random.uniform(1, 3))
    logging.info("========== 本轮采集结束 ==========\n")


def main_loop() -> None:
    """主调度循环:定时刷新代理、定时采集价格"""
    # 程序初始化刷新代理池
    refresh_proxy_pool()
    # 注册定时任务
    schedule.every(PROXY_UPDATE_INTERVAL).seconds.do(refresh_proxy_pool)
    schedule.every(COLLECT_INTERVAL).seconds.do(batch_collect_task)

    # 常驻循环执行定时任务
    while True:
        schedule.run_pending()
        time.sleep(1)


if __name__ == "__main__":
    # 全局异常自愈:任何报错不崩溃,自动重启程序
    while True:
        try:
            main_loop()
        except Exception as e:
            logging.critical(f"程序异常中断,5秒后自动重启|错误:{str(e)}")
            time.sleep(5)

核心技术优化点

相比于网上随便找的简陋爬虫脚本,我这套做了很多落地优化,也是能长期稳定挂机的关键:

1. 代理池动态自愈

不是简单拉取代理就完事,每次请求失败自动剔除失效IP,定时批量刷新新IP,始终保证代理池内都是可用资源,避免无效请求浪费。

2. 双层重试容错机制

单次采集失败不会直接跳过,自动换全新代理重试一次,大幅提升采集成功率,适配电商严格的风控环境。

3. 模拟真人访问指纹

配置完整浏览器请求头+随机间隔休眠,规避爬虫指纹,不会因为请求过于规整被平台识别拦截。

4. 全局程序自愈

最外层死循环捕获所有未知异常,不管是网络波动、API报错、页面解析失败,程序都不会挂,5秒后自动重启继续监控。

部署挂机方案(7×24小时不断线)

1. 云服务器部署

将脚本上传至轻量云服务器,使用 nohup 后台常驻,关闭终端也不会中断任务:

nohup python3 price_monitor.py > run_log.txt 2>&1 

2. Windows 挂机

可搭配开机自启工具、后台运行脚本,实现服务器断电重启后自动运行,长期监控。

避坑心得

  • 绝对不要固定单IP采集,电商平台对静态IP封禁极其严格,动态代理切换是刚需

  • 代理池必须定时更新,短效代理过期后不清理,会导致大量请求失效

  • 采集间隔不要过短,配合随机休眠,模拟人工浏览节奏,风控概率大幅降低

  • 一定要加全局异常捕获,微小报错就停机的脚本,完全不具备724小时监控的意义

后续拓展方向

当前脚本是基础稳定版,大家可以根据自己需求自由拓展:

  1. 对接 SQLite/MySQL,持久化存储历史价格,生成价格走势数据

  2. 新增价格阈值预警,低价自动推送微信/钉钉消息

  3. 适配拼多多、抖音电商全平台价格解析

  4. 改造异步多线程,支持上百个商品同时监控不卡顿

整套代码经过长期实测落地,没有花里胡哨的冗余功能,主打稳定、可用、易拓展,需要做电商价格监控的朋友可以直接上手!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值