简介:这是一份面向Python爬虫开发者与数据采集人员的淘宝Cookie自动化获取工具,针对淘宝数据平台请求中登录态易失效、需人工反复登录的痛点,通过定时登录方式持续产出有效Cookie,适合具备一定Python与自动化脚本基础的中级开发者使用。资源包共5个文件,以Python脚本与ctp模板为主,另含许可证与说明文档,压缩包约12KB,体积轻量便于快速部署。核心入口为login_robot.py,对外提供Restful服务,缺省监听9080端口;配置模板基本无需改动,爬虫脚本模板经配置后可生成以店铺名命名的登录脚本,API请求方法在TaobaoLoginAPI类中已附注释说明。运行依赖Python 2.6+与CasperJS 1.0+,后者需PhantomJS 1.8.2以上版本支撑。目前已有3403人学习下载,读者可据此搭建稳定的Cookie供给服务,减少重复登录成本,并为后续淘宝数据采集任务提供持续可用的登录凭证。
1. 定时登录与 Cookie 保活:一个 Python 自动化脚本的落地拆解
电商数据采集、店铺后台巡检、价格监控这类活儿,绕不开一个前置条件——你得先有一个“活着”的登录态。手动登录一次拿到的 Cookie,短则几小时、长则几天就失效,半夜掉线第二天任务全红是常事。taobao_cookieman 这个 Python 脚本包解决的就是这件事:它用定时任务驱动浏览器自动完成登录流程,把有效 Cookie 落盘保存,供下游采集或接口调用直接读取。适合做数据采集、自动化巡检、店铺运营辅助的从业者,尤其是被登录态过期反复折磨过的人。它不是什么黑科技,核心就是 Selenium 驱动 + 定时调度 + Cookie 序列化三件事的组合,但组合方式里有几个细节决定了它能不能稳定跑下去。
2. 环境搭建与依赖选型:为什么是 Selenium 而不是 requests
2.1 登录流程的本质决定了技术栈
很多人第一反应是用 requests 模拟登录接口,POST 账号密码拿 Cookie。这条路在早期电商站点上可行,但现在基本走不通。原因不复杂:登录请求里带了大量动态参数——加密后的密码、时间戳、设备指纹、滑块验证结果——这些参数由前端 JavaScript 运行时生成,纯 HTTP 请求复现成本极高,而且平台风控策略一变,逆向出来的参数逻辑就废了。
Selenium 的思路不同:它驱动一个真实浏览器,让页面自己跑完 JavaScript,脚本只负责填表单、点按钮、等跳转。登录成功后从浏览器上下文里导出 Cookie,拿到的就是浏览器实际使用的完整凭证。代价是资源占用高、速度慢,但稳定性远好于逆向接口。常见做法是 Chrome + ChromeDriver 组合,无头模式(headless)可以跑在服务器上,但要注意部分站点的风控会检测无头特征,必要时切回有头模式或用虚拟显示。
2.2 依赖安装与目录结构
脚本包的核心依赖不多,一条命令搞定:
pip install selenium schedule pyyamlselenium:浏览器自动化驱动,负责打开页面、填表单、取 Cookieschedule:轻量定时调度库,比 crontab 更灵活,适合在 Python 进程内控制调度逻辑pyyaml:读取配置文件,账号、目标 URL、调度间隔都放在 YAML 里,不硬编码
ChromeDriver 的版本必须和本机 Chrome 浏览器版本匹配,这是新手翻车率最高的一步。查看 Chrome 版本:地址栏输入chrome://version,然后去 ChromeDriver 官方下载对应版本。版本差一个大版本就可能报SessionNotCreatedException。
目录结构建议这样组织:
taobao_cookieman/ ├── config.yaml # 账号、URL、调度配置 ├── cookieman.py # 主逻辑:登录 + 取 Cookie + 保存 ├── scheduler.py # 定时调度入口 ├── cookies/ # Cookie 落盘目录 │ └── .gitkeep └── logs/ # 运行日志 └── .gitkeep2.3 配置文件设计
把可变参数抽到 YAML 里,改配置不用动代码:
# config.yaml account: username: "your_username" password: "your_password" target: login_url: "https://example.com/login" cookie_domain: ".example.com" browser: headless: true driver_path: "/usr/local/bin/chromedriver" page_load_timeout: 30 schedule: interval_minutes: 30 cookie_ttl_minutes: 25interval_minutes是调度间隔,cookie_ttl_minutes是 Cookie 有效期预估。这两个值的关系很关键:调度间隔必须小于 Cookie 有效期,否则会出现“刚拿到就过期”的空窗期。一般设 TTL 的 80% 作为调度间隔比较稳妥。
3. 登录与 Cookie 提取:核心代码逐段拆
3.1 浏览器初始化与反检测
直接webdriver.Chrome()启动的浏览器有一堆自动化特征,部分站点会直接拒绝登录。需要在启动参数里做基本伪装:
from selenium import webdriver from selenium.webdriver.chrome.options import Options def create_driver(driver_path, headless=True, timeout=30): opts = Options() if headless: opts.add_argument("--headless=new") # 关闭自动化提示栏 opts.add_experimental_option("excludeSwitches", ["enable-automation"]) opts.add_experimental_option("useAutomationExtension", False) # 设置窗口尺寸,避免移动端布局 opts.add_argument("--window-size=1920,1080") opts.add_argument("--disable-gpu") opts.add_argument("--no-sandbox") # 关键:覆盖 navigator.webdriver 属性 opts.add_argument("--disable-blink-features=AutomationControlled") driver = webdriver.Chrome(executable_path=driver_path, options=opts) driver.set_page_load_timeout(timeout) # 进一步抹除 webdriver 痕迹 driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})" }) return driver逻辑说明:--disable-blink-features=AutomationControlled是 Chrome 88 之后隐藏自动化特征的关键参数;execute_cdp_cmd通过 Chrome DevTools Protocol 注入脚本,在页面加载前就把navigator.webdriver改成 undefined。这两步做完,大部分基础检测能过,但高级风控还会看鼠标轨迹、打字节奏等行为特征,那就不是脚本层面能完全解决的了。
参数方面,headless=new是新版无头模式,比旧版--headless更接近真实浏览器行为。page_load_timeout设 30 秒是经验值,网络慢可以调到 60,但别设太大,否则页面卡住时脚本会一直等。
3.2 登录表单填充与提交
登录页面的元素定位是最容易翻车的环节。不同站点的表单结构不同,但通用思路是:等元素出现 → 清空 → 输入 → 点击提交 → 等待跳转。
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def do_login(driver, username, password, login_url): driver.get(login_url) wait = WebDriverWait(driver, 15) # 等待用户名输入框出现 user_input = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='username'], input#username, input[type='text']")) ) user_input.clear() user_input.send_keys(username) # 密码框 pwd_input = driver.find_element(By.CSS_SELECTOR, "input[type='password']") pwd_input.clear() pwd_input.send_keys(password) # 提交按钮 submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], input[type='submit'], .login-btn") submit_btn.click() # 等待登录成功标志——URL 变化或特定元素出现 try: wait.until(EC.url_changes(login_url)) except Exception: # 如果 URL 没变,检查是否有登录成功后的元素 wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".user-avatar, .dashboard, .home-page"))) return True逻辑说明:CSS 选择器用了逗号分隔的多候选策略,因为不同站点的表单属性不同,多写几个备选能提高兼容性。WebDriverWait配合expected_conditions是显式等待的标准做法,比time.sleep()靠谱得多——后者要么等太久浪费时间,要么等太短元素还没出来。
参数方面,WebDriverWait的超时设 15 秒是折中值。如果站点有滑块验证,这一步会卡住,需要额外处理。滑块验证的自动化涉及图像识别和轨迹模拟,复杂度较高,不在基础脚本范围内。实际使用中如果遇到滑块,建议先手动登录一次,让浏览器记住设备,后续自动登录触发滑块的频率会降低。
3.3 Cookie 提取与序列化保存
登录成功后,从 driver 里取出所有 Cookie,序列化成 JSON 落盘:
import json import os import time def save_cookies(driver, cookie_dir, domain_filter=None): cookies = driver.get_cookies() if domain_filter: cookies = [c for c in cookies if domain_filter in c.get("domain", "")] if not cookies: raise ValueError("未获取到任何 Cookie,登录可能未成功") # 补充时间戳,方便下游判断新鲜度 payload = { "cookies": cookies, "saved_at": time.time(), "count": len(cookies) } os.makedirs(cookie_dir, exist_ok=True) filepath = os.path.join(cookie_dir, "cookies.json") with open(filepath, "w", encoding="utf-8") as f: json.dump(payload, f, ensure_ascii=False, indent=2) return filepath逻辑说明:driver.get_cookies()返回的是列表,每个元素是一个字典,包含 name、value、domain、path、expiry 等字段。domain_filter用来过滤掉第三方域名的 Cookie,只保留目标站点的。saved_at时间戳是给下游用的——采集脚本读取 Cookie 时先检查这个时间戳,超过 TTL 就触发重新登录。
参数方面,ensure_ascii=False保证中文域名或值不会被转义成 Unicode 编码,方便人工排查。indent=2让 JSON 可读,生产环境如果在意文件大小可以去掉。
3.4 下游如何消费这份 Cookie
保存的 Cookie 是给下游用的。如果下游是 Selenium,直接driver.add_cookie()逐个注入;如果下游是 requests,需要把 Cookie 列表转成字典:
import json import requests def load_cookies_as_dict(filepath): with open(filepath, "r", encoding="utf-8") as f: payload = json.load(f) return {c["name"]: c["value"] for c in payload["cookies"]} # requests 调用示例 cookie_dict = load_cookies_as_dict("cookies/cookies.json") session = requests.Session() session.cookies.update(cookie_dict) resp = session.get("https://example.com/api/data")注意:requests 使用的 Cookie 不包含浏览器指纹信息,部分接口会校验 User-Agent、Referer 等请求头。建议把浏览器登录时的 User-Agent 一并保存,下游请求时带上。
4. 定时调度与 Cookie 生命周期管理
4.1 调度策略:间隔怎么定
调度间隔的设定取决于两个因素:Cookie 的实际有效期和下游任务的频率。Cookie 有效期可以通过观察expiry字段或实际失效时间来确定。如果 Cookie 里没有expiry(会话 Cookie),那它只在浏览器会话内有效,关掉浏览器就没了——这种情况必须用有头模式保持浏览器常驻,或者接受每次重新登录。
常见做法是设一个保守的调度间隔,比如 20 到 30 分钟跑一次登录,每次登录后覆盖旧 Cookie 文件。这样即使某次登录失败,旧 Cookie 还能撑一段时间,不至于立刻断档。
import schedule import time import logging logging.basicConfig( filename="logs/scheduler.log", level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s" ) def job(): driver = None try: driver = create_driver( driver_path=config["browser"]["driver_path"], headless=config["browser"]["headless"], timeout=config["browser"]["page_load_timeout"] ) do_login(driver, config["account"]["username"], config["account"]["password"], config["target"]["login_url"]) path = save_cookies(driver, "cookies", config["target"]["cookie_domain"]) logging.info(f"Cookie 已更新: {path}") except Exception as e: logging.error(f"登录失败: {e}") finally: if driver: driver.quit() # 启动时先跑一次 job() schedule.every(config["schedule"]["interval_minutes"]).minutes.do(job) while True: schedule.run_pending() time.sleep(1)逻辑说明:finally块里driver.quit()是必须的,否则每次调度都会残留一个 Chrome 进程,跑几个小时内存就爆了。启动时先跑一次job()是为了避免“启动后等半小时才有 Cookie”的空窗期。
4.2 Cookie 新鲜度校验
下游读取 Cookie 时应该先校验新鲜度,而不是盲目使用:
import time import json def is_cookie_fresh(filepath, ttl_minutes=25): try: with open(filepath, "r", encoding="utf-8") as f: payload = json.load(f) age_minutes = (time.time() - payload["saved_at"]) / 60 return age_minutes < ttl_minutes except (FileNotFoundError, KeyError, json.JSONDecodeError): return False这个函数返回 False 时,下游可以选择等待下一次调度、主动触发登录、或者降级到备用方案。把判断逻辑放在下游而不是上游,是因为上游只管“尽力保持 Cookie 新鲜”,下游才知道自己能不能等。
4.3 日志与告警
调度脚本最怕的是“静默失败”——登录挂了但没人知道,直到下游任务大面积报错才发现。日志要记录每次调度的结果,关键字段包括:时间戳、成功/失败、Cookie 数量、耗时。如果连续 N 次失败,应该触发告警。简单做法是写一个计数器,连续失败超过阈值就发邮件或写到一个专门的告警文件里,由外部监控系统读取。
5. 避坑与常见问题排查
5.1 ChromeDriver 版本不匹配
现象:启动时报SessionNotCreatedException: This version of ChromeDriver only supports Chrome version XX。
原因:Chrome 浏览器自动更新后版本变了,但 ChromeDriver 还是旧版。
解决:查看chrome://version确认浏览器版本,去 ChromeDriver 官网下载对应版本替换。更省事的做法是用webdriver-manager库自动管理驱动版本:pip install webdriver-manager,然后from webdriver_manager.chrome import ChromeDriverManager,启动时用webdriver.Chrome(ChromeDriverManager().install(), options=opts)。但注意自动下载需要网络通畅,内网环境可能失败。
5.2 登录成功但 Cookie 为空
现象:driver.get_cookies()返回空列表,或者只有几个无关的 Cookie。
原因:登录流程没真正完成——可能是验证码拦截、页面还没跳转就取了 Cookie、或者登录失败但脚本没检测到。
解决:在取 Cookie 前加一个明确的“登录成功”判断,比如等待某个只有登录后才出现的元素(用户头像、昵称、退出按钮)。不要用time.sleep()硬等,用WebDriverWait等具体元素。如果页面有 iframe,还要先driver.switch_to.frame()切进去。
5.3 无头模式下登录被拒
现象:有头模式能登录,切到 headless 就失败或直接跳验证码。
原因:部分站点的风控会检测无头浏览器特征,即使做了基础伪装也可能被识别。
解决:优先用--headless=new而不是旧版--headless。如果仍然不行,考虑用虚拟显示(Xvfb)在有头模式下跑在无显示器环境。另一种思路是降低登录频率,减少触发风控的概率。
5.4 Cookie 文件写入冲突
现象:下游读取 Cookie 文件时报 JSON 解析错误,或者读到半截内容。
原因:上游正在写文件时下游同时在读,写操作不是原子的。
解决:写入时先写临时文件再重命名,利用文件系统的原子重命名操作:
import tempfile import os def atomic_write_json(data, filepath): dir_name = os.path.dirname(filepath) fd, tmp_path = tempfile.mkstemp(dir=dir_name, suffix=".tmp") try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) os.replace(tmp_path, filepath) # 原子操作 except Exception: os.unlink(tmp_path) raise5.5 调度进程被系统杀掉
现象:脚本跑了一段时间后无声无息停了,日志没有报错。
原因:服务器内存不足触发 OOM Killer,或者 SSH 会话断开导致前台进程被挂断。
解决:用nohup或systemd把脚本做成守护进程。nohup python scheduler.py &是最简单的做法,但更规范的是写一个 systemd unit 文件,设置Restart=always,进程挂了自动拉起。同时监控内存占用,如果每次调度后内存持续增长,说明 driver 没正确退出,检查finally块。
6. 进阶:把 Cookie 保活做成可观测的服务
基础脚本跑通之后,下一步是让它变得可观测、可维护。我一般会加三个东西:健康检查接口、Cookie 有效期探测、以及降级策略。
健康检查接口用一个简单的 HTTP 服务暴露当前 Cookie 状态:
from http.server import HTTPServer, BaseHTTPRequestHandler import json, time class HealthHandler(BaseHTTPRequestHandler): def do_GET(self): if self.path == "/health": try: with open("cookies/cookies.json", "r") as f: payload = json.load(f) age = (time.time() - payload["saved_at"]) / 60 status = { "status": "ok" if age < 25 else "stale", "cookie_count": payload["count"], "age_minutes": round(age, 1) } except Exception as e: status = {"status": "error", "detail": str(e)} self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(json.dumps(status).encode()) else: self.send_response(404) self.end_headers() HTTPServer(("0.0.0.0", 8899), HealthHandler).serve_forever()这个接口可以接入监控系统,设置告警规则:age_minutes > 25持续 5 分钟就发通知。比翻日志高效得多。
Cookie 有效期探测是另一个实用技巧。与其猜 TTL,不如主动探测:拿当前 Cookie 发一个轻量请求(比如获取用户信息的接口),如果返回 401 或跳转登录页,说明 Cookie 已失效,立即触发重新登录。这个探测可以放在调度循环里,每次调度前先探测一次,有效就跳过登录,无效才走完整登录流程。这样能减少不必要的浏览器启动,降低资源消耗。
降级策略是最后一道防线。如果连续多次登录失败(比如账号被临时限制),脚本应该停止重试并发出告警,而不是无限循环撞墙。实现方式很简单:维护一个失败计数器,连续失败超过 3 次就暂停调度,写一个告警标记文件,等人工介入。
从那以后我每次部署这类脚本,都会先把健康检查接口和失败计数器加上,再跑正式任务。看起来多花了半小时,但省下的是半夜被叫起来排查的时间。希望帮到你。
本文还有配套的精品资源,点击获取