Python Playwright实战:淘宝自动登录与订单数据抓取完整方案
1. 项目概述与核心价值最近在跟几个做电商数据分析的朋友聊天他们最头疼的就是定期手动登录淘宝后台导出订单数据来做分析。这事儿听起来简单但真做起来每天重复登录、点选、下载不仅枯燥还容易出错特别是遇到验证码或者登录状态失效的时候更是让人抓狂。他们之前也试过一些传统的自动化工具比如Selenium但总感觉在应对淘宝这种动态加载、反爬机制复杂的现代Web应用时有点力不从心要么是脚本不稳定要么是维护成本太高。这让我想起了Playwright这个后起之秀。它不像Selenium那样“年事已高”而是微软专门为现代Web应用打造的自动化测试框架。我花了些时间深入研究并用PythonPlaywright完整地走了一遍淘宝自动登录和订单抓取的流程发现它确实在稳定性、执行速度和应对复杂场景方面有显著优势。今天我就把这个实战过程拆解开来分享给你。无论你是想解放双手、批量获取自己的订单数据做个人分析还是为公司的电商运营提供数据支持这套方案都能提供一个稳定、高效的自动化思路。整个过程我会从环境搭建、核心逻辑设计到每一步的代码实现和避坑指南毫无保留地呈现出来。2. 环境准备与Playwright核心优势解析2.1 为什么选择Playwright而非Selenium在开始动手之前我们得先搞清楚为什么选Playwright。这就像你要去爬山是选一双老牌但稍显笨重的登山鞋还是一双针对新地形做了全面优化的新款我的选择是后者。首先原生支持多浏览器。Playwright为ChromiumChrome/Edge内核、Firefox和WebKitSafari内核都提供了高度一致的原生API。这意味着你写一套脚本可以几乎不加修改地在三种浏览器上运行。而Selenium需要为不同浏览器下载并维护不同的驱动WebDriver版本兼容性问题是个永恒的痛。其次自动等待机制。这是Playwright最让我省心的特性之一。在Selenium里你经常需要写一堆WebDriverWait和expected_conditions来等待元素加载否则脚本很容易因为页面没加载完而报错。Playwright则聪明得多它的绝大多数操作如click,fill,text_content都内置了智能等待。它会自动等待元素变得可操作可见、可点击、可输入后才执行大大减少了因时机问题导致的失败。第三强大的网络拦截与模拟。Playwright可以轻松地拦截和修改网络请求这对于处理动态加载的数据比如淘宝订单列表的滚动加载或者模拟移动端设备访问至关重要。你可以直接监听特定的XHR/Fetch请求拿到原始的JSON数据这比从渲染后的HTML里解析要稳定和高效得多。第四执行速度与资源占用。Playwright启动浏览器是无头headless或带界面的其通信协议比Selenium的WebDriver协议更高效。在实际测试中相同操作下Playwright的脚本执行速度通常更快且内存占用更可控。最后对现代Web技术的友好性。Playwright生来就是为了应对单页面应用SPA、Shadow DOM、复杂的CSS选择器等现代Web开发特性。淘宝的前端技术栈非常先进Playwright在处理这些场景时更加得心应手。注意虽然Playwright优势明显但Selenium拥有更庞大的社区和更悠久的历史某些极其冷门的浏览器或企业级遗留系统可能仍需Selenium。但对于像淘宝这样的主流现代网站Playwright无疑是当前更优的选择。2.2 Python环境与Playwright安装工欲善其事必先利其器。我们先来把“兵器”准备好。第一步确保Python环境你需要一个Python 3.7或更高版本的环境。我强烈建议使用虚拟环境venv来管理项目依赖避免污染全局环境。# 创建项目目录并进入 mkdir taobao-automation cd taobao-automation # 创建虚拟环境Windows用 python -m venv venv python3 -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate激活后你的命令行提示符前应该会出现(venv)字样。第二步安装Playwright for Python使用pip安装Playwright的Python库非常简单。pip install playwright这个命令会安装核心的Playwright库。第三步安装浏览器Playwright需要它自己管理的浏览器二进制文件。安装完库之后你需要运行以下命令来下载Chromium、Firefox和WebKit。虽然我们主要用Chromium但一次性安装完也无妨。playwright install这个步骤可能会花费一些时间因为它需要下载几百MB的浏览器文件。如果网络较慢可以考虑使用镜像源或者只安装Chromiumplaywright install chromium第四步验证安装创建一个简单的测试脚本test_install.pyimport asyncio from playwright.async_api import async_playwright async def main(): async with async_playwright() as p: # 启动Chromium浏览器headlessFalse表示显示界面 browser await p.chromium.launch(headlessFalse) page await browser.new_page() await page.goto(https://www.baidu.com) print(await page.title()) await browser.close() asyncio.run(main())运行这个脚本python test_install.py如果能看到浏览器打开并访问百度且控制台打印出“百度一下你就知道”那么恭喜你环境配置成功实操心得在国内网络环境下playwright install可能会很慢甚至失败。一个有效的解决方法是设置环境变量使用国内的镜像源来下载浏览器。例如在运行安装命令前先执行set PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright(Windows) 或export PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright(macOS/Linux)。这能极大提升下载速度。3. 淘宝自动登录策略与实现细节自动登录是整个过程的第一步也是最关键、最易失败的一步。淘宝的登录页面防护机制非常完善包括滑块验证、短信验证、扫码登录等多种方式。我们的目标是模拟最自然的用户行为安全地通过用户名密码登录并处理好可能出现的验证码。3.1 登录流程分析与页面元素定位首先我们需要手动走一遍登录流程观察页面结构。打开淘宝登录页https://login.taobao.com你会发现默认是扫码登录。我们需要切换到“密码登录”选项卡。使用Playwright的录制工具可以快速生成定位代码但我更推荐手动分析因为这样理解更深刻。打开浏览器开发者工具F12使用元素选择器查看“密码登录”这个链接。你会发现它的HTML结构可能类似这样div classlogin-switch a hrefjavascript:; classforget-pwd J_Quick2Static密码登录/a /div对于Playwright我们有多种定位方式文本定位page.locator(‘text密码登录’)。这是最直观的但要注意文本内容必须完全匹配且页面语言不能变。CSS选择器定位page.locator(‘.J_Quick2Static’)。通过class定位通常更稳定前提是这个class不是动态生成的。XPath定位page.locator(‘//a[contains(class, “J_Quick2Static”)]’)。功能强大但可能较慢且易受DOM结构变化影响。我的策略是优先使用具有唯一性和语义化的CSS选择器或属性。对于淘宝这种大型网站关键交互元素通常会有稳定的、用于自动化测试的>import asyncio from playwright.async_api import async_playwright, TimeoutError as PlaywrightTimeoutError class TaobaoAutoLogin: def __init__(self, username, password, headlessFalse): self.username username self.password password self.headless headless # 是否无头模式运行 self.browser None self.context None self.page None async def setup_browser(self): 启动浏览器和上下文 playwright await async_playwright().start() # 使用Chromium可替换为 firefox 或 webkit self.browser await playwright.chromium.launch( headlessself.headless, # 减慢操作速度使其更像真人有助于绕过一些简单的行为检测 slow_mo100, ) # 创建一个新的浏览器上下文可以独立设置cookies、权限等 self.context await self.browser.new_context( viewport{width: 1920, height: 1080}, # 设置用户代理模拟真实浏览器 user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ) self.page await self.context.new_page() async def login(self): 执行登录流程 try: # 1. 导航到登录页 await self.page.goto(https://login.taobao.com, wait_untilnetworkidle) # 等待到网络空闲 print(已打开登录页面) # 2. 切换到密码登录 (使用更稳定的选择器组合) # 可能的选择器通过文本或者通过特定的class/id password_login_btn self.page.locator(a:has-text(密码登录), .J_Quick2Static, #J_Quick2Static).first await password_login_btn.click() print(已切换到密码登录) await self.page.wait_for_timeout(1000) # 等待一下选项卡切换动画 # 3. 输入用户名 (查找id或name包含‘loginId’的输入框) # 淘宝的输入框id可能会变但name通常较稳定 username_input self.page.locator(input[namefm-login-id], #fm-login-id) await username_input.fill(self.username) print(f已输入用户名: {self.username}) # 4. 输入密码 password_input self.page.locator(input[namefm-login-password], #fm-login-password) await password_input.fill(self.password) print(已输入密码) # 5. 点击登录按钮 submit_btn self.page.locator(button:has-text(登录), #J_SubmitStatic) await submit_btn.click() print(已点击登录按钮) # 6. 处理可能的验证码 - 这里是一个关键难点 # 我们需要检测验证码区域是否出现 await self._handle_verification() # 7. 等待登录成功通常跳转到淘宝首页或我的淘宝页 # 通过检测URL变化或特定元素出现来判断 try: # 等待导航到包含‘login.taobao.com’的URL消失或者‘taobao.com’首页出现 await self.page.wait_for_url(**://*.taobao.com/*, timeout30000) # 同时可以等待一个登录后才会出现的元素比如‘我的淘宝’链接 await self.page.wait_for_selector(a[href*mytaobao], statevisible, timeout10000) print(登录成功) return True except PlaywrightTimeoutError: print(登录可能未成功或跳转超时。) # 可以在这里保存当前页面截图用于调试 await self.page.screenshot(pathlogin_timeout.png) return False except Exception as e: print(f登录过程中发生错误: {e}) await self.page.screenshot(pathlogin_error.png) return False async def _handle_verification(self): 处理验证码的占位函数后续扩展 # 这里需要实现具体的验证码识别逻辑 # 例如检测滑块验证码图片计算滑块距离模拟拖动 # 或者检测点选验证码调用OCR服务识别文字并点击 # 由于验证码策略复杂且多变这里先留空仅做超时等待 print(正在检查验证码...) # 等待5秒观察是否有验证码弹出。实际应用中这里需要更智能的检测。 await self.page.wait_for_timeout(5000) # 可以添加检测代码例如if await self.page.locator(‘#nc_1_wrapper’).is_visible(): # 处理滑块 pass async def close(self): 关闭浏览器 if self.browser: await self.browser.close() async def main(): # 替换为你的淘宝账号和密码出于安全考虑建议从环境变量或配置文件中读取 USERNAME “your_taobao_username” PASSWORD “your_taobao_password” bot TaobaoAutoLogin(USERNAME, PASSWORD, headlessFalse) # 调试时设为False看界面 try: await bot.setup_browser() success await bot.login() if success: print(“登录成功可以继续后续操作如抓取订单。”) # 这里可以保存登录状态cookies供后续使用 cookies await bot.context.cookies() # 将cookies保存到文件 import json with open(‘taobao_cookies.json’, ‘w’) as f: json.dump(cookies, f) print(“Cookies已保存。”) else: print(“登录失败。”) finally: await bot.close() if __name__ ‘__main__’: asyncio.run(main())这段代码构建了一个基本的登录类。有几个关键点需要注意选择器策略代码中使用了locator(‘selector1, selector2’).first的写法。这是一个小技巧它表示“按顺序尝试这些选择器使用第一个匹配到的”。这提高了脚本的容错性因为淘宝前端的class或id可能会微调。等待策略page.goto使用了wait_until‘networkidle’这比默认的load事件更严格会等待页面基本没有网络请求时才继续更适合SPA。wait_for_url和wait_for_selector用于确认登录成功。错误处理与调试使用try…except捕获超时和其他异常并在出错时自动截图page.screenshot这对于远程调试无人值守的脚本至关重要。状态保存登录成功后我们通过context.cookies()获取了所有cookies并保存为JSON文件。这样下次脚本运行时可以直接加载这个cookies文件到新的浏览器上下文避免重复登录前提是cookies未过期。重要警告将账号密码硬编码在脚本中是极不安全的在生产环境中务必通过环境变量、加密的配置文件或密钥管理服务来传递敏感信息。例如USERNAME os.getenv(‘TAOBAO_USERNAME’)。3.3 验证码处理思路与进阶方案验证码是自动化登录最大的拦路虎。淘宝常用的有滑块验证和点选文字验证。基础方案适用于简单情况或学习手动处理。将headless设为False当验证码出现时脚本会暂停因为我们在_handle_verification里只是等待你可以手动完成验证然后脚本继续。这适合偶尔运行的个人脚本。进阶方案需要投入开发滑块验证需要识别滑块缺口位置。步骤截取带缺口的背景图和无缺口的完整图有时可以通过修改页面元素样式获得。识别使用OpenCV等图像处理库通过模板匹配或边缘检测算法计算缺口位置。模拟拖动使用Playwright的page.mouseAPI模拟人类拖动轨迹先加速后减速将滑块拖到缺口位置。轨迹模拟是关键过于机械化的直线拖动容易被识别。点选验证需要识别图中文字并点击。步骤截取验证码图片。识别调用第三方OCR API如百度OCR、腾讯OCR等通常有免费额度识别图中的文字。模拟点击根据识别出的文字在图片对应位置进行点击。商业方案使用专业的打码平台如超级鹰、图鉴等。这些平台提供API你上传验证码图片它们返回识别结果或操作坐标。这是最稳定但需要付费的方案。由于验证码对抗是一个持续升级的过程且实现完整的识别代码篇幅巨大上述代码中_handle_verification仅作为框架预留。在实际项目中你需要根据遇到的验证码类型选择并集成上述一种或多种方案。4. 订单数据抓取策略与实战成功登录后我们就进入了“已买到的宝贝”页面接下来是如何高效、稳定地抓取订单数据。4.1 导航至订单页面与页面结构分析登录后我们可以直接导航到订单页面。淘宝的订单页面URL通常是https://buyertrade.taobao.com/trade/itemlist/list_bought_items.htm。async def navigate_to_orders(self): 导航到‘已买到的宝贝’页面 order_url ‘https://buyertrade.taobao.com/trade/itemlist/list_bought_items.htm’ await self.page.goto(order_url, wait_until‘networkidle’) print(“已进入订单列表页面”) # 等待订单列表容器加载 await self.page.wait_for_selector(‘.trade-order-main’, state‘visible’, timeout10000)订单列表是动态滚动加载的。你需要分析其网络请求。打开开发者工具的“网络”(Network)选项卡筛选XHR/Fetch请求然后滚动订单列表观察哪些请求是获取订单数据的。你很可能会发现一个返回JSON数据的API请求其URL包含类似asyncBought.htm的参数。直接解析这个API返回的JSON数据是比解析HTML更优的选择因为数据结构化不易受前端样式改动影响。4.2 拦截网络请求直接获取JSON数据Playwright的page.on(‘request’或page.on(‘response’)事件监听器可以拦截请求。我们可以在导航到订单页面前设置一个监听器捕获特定的API响应。async def capture_order_api(self): 监听并捕获订单API的响应 order_data [] def handle_response(response): # 根据实际观察到的API URL特征来过滤 if ‘asyncBought.htm’ in response.url and response.status 200: print(f”捕获到订单API: {response.url}“) # 这里我们只是打印URL实际处理需要在异步上下文中进行 # 我们可以将响应对象存储起来稍后处理 order_data.append(response) # 添加响应监听器 self.page.on(‘response’, handle_response) # 导航到订单页并手动滚动触发加载 await self.navigate_to_orders() # 模拟滚动触发分页加载 await self._scroll_to_load_all_orders() # 移除监听器避免影响后续操作 self.page.remove_listener(‘response’, handle_response) # 注意handle_response是同步函数不能直接await response.json()。 # 更好的方式是在异步上下文中使用 page.wait_for_response return order_data async def _scroll_to_load_all_orders(self): 模拟滚动加载所有订单 import math last_height await self.page.evaluate(‘document.body.scrollHeight’) scroll_attempts 0 max_attempts 10 # 防止无限滚动 while scroll_attempts max_attempts: # 滚动到底部 await self.page.evaluate(‘window.scrollTo(0, document.body.scrollHeight)’) await self.page.wait_for_timeout(2000) # 等待新内容加载 new_height await self.page.evaluate(‘document.body.scrollHeight’) if new_height last_height: # 高度未变可能已加载完毕或需要点击“加载更多” load_more_btn self.page.locator(‘text加载更多’).first if await load_more_btn.is_visible(): await load_more_btn.click() await self.page.wait_for_timeout(3000) else: break last_height new_height scroll_attempts 1 print(“滚动加载完成或达到最大尝试次数。”)然而上述方法中handle_response是同步函数处理异步的response.json()比较麻烦。更优雅的方式是使用page.wait_for_response在已知API模式的情况下主动等待并获取数据或者使用page.expect_response。更推荐的方案使用page.wait_for_response配合循环假设我们通过分析知道点击“下一页”或滚动后会触发一个特定的请求。我们可以更主动地捕获它。async def get_orders_via_api(self, max_pages5): 通过拦截API获取多页订单数据 all_orders [] current_page 1 # 首先导航到订单列表第一页 await self.navigate_to_orders() while current_page max_pages: print(f”正在获取第 {current_page} 页订单...“) # 在每次可能触发API请求的操作如滚动、点击下一页前设置一个等待承诺 # 我们需要知道API请求的URL模式这里用 ‘asyncBought.htm’ 作为示例 api_url_pattern ‘**asyncBought.htm**’ # 启动一个异步任务来等待响应 response_promise self.page.wait_for_response(api_url_pattern) # 然后执行触发请求的操作对于第一页可能已经加载对于后续页需要点击‘下一页’或滚动 if current_page 1: # 第一页数据可能已在加载页面时获取或者需要手动触发一次滚动 await self.page.evaluate(‘window.scrollBy(0, 100)’) else: # 查找并点击‘下一页’按钮 next_btn self.page.locator(‘a:has-text(“下一页”), .pagination-next’).first if await next_btn.is_enabled(): await next_btn.click() else: print(“没有下一页了。”) break # 等待并获取API响应 try: response await response_promise json_data await response.json() # 解析json_data提取订单列表添加到all_orders page_orders self._parse_order_json(json_data) all_orders.extend(page_orders) print(f”第 {current_page} 页获取到 {len(page_orders)} 条订单。”) except PlaywrightTimeoutError: print(f”等待第 {current_page} 页API响应超时。”) break except Exception as e: print(f”解析第 {current_page} 页数据时出错: {e}”) break current_page 1 await self.page.wait_for_timeout(1000) # 页间间隔 return all_orders def _parse_order_json(self, json_data): 解析API返回的JSON提取订单信息 orders [] # 这里的解析逻辑完全取决于API返回的实际数据结构 # 你需要通过开发者工具仔细查看响应体 # 示例结构假设 # main_order_list json_data.get(‘data’, {}).get(‘mainOrders’, []) # for order in main_order_list: # order_info { # ‘order_id’: order.get(‘id’), # ‘create_time’: order.get(‘createTime’), # ‘seller_nick’: order.get(‘seller’, {}).get(‘nick’), # ‘total_amount’: order.get(‘payInfo’, {}).get(‘actualFee’), # ‘status’: order.get(‘statusText’), # ‘items’: [{ # ‘title’: item.get(‘title’), # ‘price’: item.get(‘price’), # ‘quantity’: item.get(‘quantity’) # } for item in order.get(‘subOrders’, [])] # } # orders.append(order_info) # return orders print(“需要根据实际API响应结构实现 _parse_order_json 方法。”) return orders # 返回空列表需自行实现4.3 数据解析与存储解析函数_parse_order_json是整个抓取的核心。你需要用浏览器开发者工具仔细研究订单API返回的JSON结构。通常它会包含一个订单列表mainOrders或类似字段每个订单对象里又有子订单subOrders即商品项、支付信息、卖家信息、物流信息等。提取出所需字段后我们可以将数据存储起来。对于小批量数据JSON或CSV文件就足够了。import csv import json from datetime import datetime def save_orders_to_json(self, orders, filenameNone): 保存订单数据到JSON文件 if filename is None: filename f”taobao_orders_{datetime.now().strftime(‘%Y%m%d_%H%M%S’)}.json” with open(filename, ‘w’, encoding‘utf-8’) as f: json.dump(orders, f, ensure_asciiFalse, indent2) print(f”订单数据已保存到 {filename}”) def save_orders_to_csv(self, orders, filenameNone): 保存订单数据到CSV文件扁平化处理 if filename is None: filename f”taobao_orders_{datetime.now().strftime(‘%Y%m%d_%H%M%S’)}.csv” # 需要将嵌套的订单结构扁平化这里假设每个主订单只有一个子订单简化处理 flattened [] for order in orders: base_info { ‘order_id’: order.get(‘order_id’), ‘create_time’: order.get(‘create_time’), ‘seller’: order.get(‘seller_nick’), ‘total_amount’: order.get(‘total_amount’), ‘status’: order.get(‘status’), } # 如果有多件商品可能需要拆分成多行 for item in order.get(‘items’, []): row base_info.copy() row[‘item_title’] item.get(‘title’) row[‘item_price’] item.get(‘price’) row[‘item_quantity’] item.get(‘quantity’) flattened.append(row) if flattened: keys flattened[0].keys() with open(filename, ‘w’, newline‘’, encoding‘utf-8-sig’) as f: # utf-8-sig for Excel writer csv.DictWriter(f, fieldnameskeys) writer.writeheader() writer.writerows(flattened) print(f”订单数据已保存到 {filename} (CSV)”)在主函数中登录成功后调用get_orders_via_api并保存数据即可。5. 常见问题排查与脚本优化在实际运行中你肯定会遇到各种各样的问题。下面我总结了一些常见坑点和优化技巧。5.1 元素定位失败与超时处理问题脚本报错TimeoutError提示找不到元素。排查页面未加载完增加wait_until的条件或使用page.wait_for_selector在操作前显式等待关键元素。选择器失效淘宝前端可能更新了class或结构。使用更宽松的选择器如包含部分文本:has-text(“部分文字”)或使用XPath通过相对位置定位。开启headlessFalse模式配合page.pause()方法可以让你在脚本运行时暂停并打开开发者工具检查元素这是调试定位问题最有效的方法。iframe嵌套登录框或某些组件可能嵌套在iframe里。你需要先定位到iframe再在iframe的上下文中查找元素。frame page.frame_locator(‘iframe选择器’).first element_in_frame frame.locator(‘按钮选择器’) await element_in_frame.click()5.2 反爬机制与行为检测淘宝有完善的反爬系统。过于规律、快速的请求会被识别。优化策略模拟人类行为在关键操作间添加随机延迟await page.wait_for_timeout(random.randint(1000, 3000))。使用slow_mo参数让所有操作变慢。使用真实的浏览器上下文new_context时可以加载真实的用户数据目录user_data_dir让浏览器看起来更像一个长期使用的真实用户环境。但注意不要泄露个人数据。随机化操作轨迹对于滑块验证拖动轨迹不能是匀速直线。合理设置请求头确保User-Agent、Accept-Language等请求头与真实浏览器一致。代理IP如果需要大规模抓取考虑使用代理IP池来轮换IP地址。5.3 登录状态维持与Cookies复用每次运行都重新登录效率低且容易触发风控。解决方案成功登录后保存cookies。下次运行时先尝试加载cookies恢复会话。async def try_load_cookies_and_refresh(self, cookie_file‘taobao_cookies.json’): “”“尝试加载cookies并刷新页面恢复登录状态”“” import os if os.path.exists(cookie_file): with open(cookie_file, ‘r’) as f: cookies json.load(f) await self.context.add_cookies(cookies) # 刷新页面或跳转到任意淘宝页面使cookies生效 await self.page.goto(‘https://www.taobao.com’) # 检查是否已登录例如查找‘我的淘宝’链接 try: await self.page.wait_for_selector(‘a[href*“mytaobao”]’, timeout5000) print(“通过Cookies恢复登录成功”) return True except PlaywrightTimeoutError: print(“Cookies已失效需要重新登录。”) return False return False在主逻辑中可以先调用这个方法如果返回False再执行完整的登录流程。5.4 脚本健壮性提升异常重试机制对于网络波动或临时性错误可以实现重试逻辑。import tenacity tenacity.retry(stoptenacity.stop_after_attempt(3), waittenacity.wait_fixed(2)) async def safe_click(self, selector): await self.page.locator(selector).click()日志记录使用Python的logging模块替代print记录不同级别INFO, WARNING, ERROR的日志便于追踪问题。配置文件将URL、选择器、重试次数、账号信息通过环境变量引用等抽离到配置文件中提高可维护性。5.5 性能与资源管理问题脚本运行后浏览器进程未关闭导致内存泄漏。解决确保在finally块或使用异步上下文管理器关闭浏览器。async with async_playwright() as p: browser await p.chromium.launch() # … 你的代码 … # 退出async with块后browser会自动关闭无头模式生产环境运行时将headless设为True可以节省资源且更稳定。将以上所有模块整合你就拥有了一套从自动登录、状态管理、数据抓取到持久化存储的完整淘宝订单自动化抓取方案。记住Web自动化是一个“道高一尺魔高一丈”的领域淘宝的页面和接口可能会变化你需要定期维护你的选择器和API解析逻辑。保持代码的模块化和可读性将使维护工作轻松得多。最后务必在遵守淘宝Robots协议和相关法律法规的前提下合理、有限度地使用自动化工具。