OpenClaw+Skill实战:跨境电商数据抓取自动化全流程指南

发布时间:2026/10/6 14:30:46
OpenClaw+Skill实战:跨境电商数据抓取自动化全流程指南
算一笔账如果手工打开跨境电商网站复制商品标题、价格、库存和评论数一天最多做几百条还会把自己搞得昏头涨脑。我自己的团队管着几个店铺光是竞品监控和选品数据整理每周就要花两三个下午。后来我把这套流程交给了 OpenClaw 里的 Skill差不多半小时能跑完原本半天的工作量。“OpenClaw Skill” 这个词最近在各技术社区里确实挺热。很多人一上来就问“这个东西能不能像真实员工一样自动干活”其实它没那么玄OpenClaw 是一个本地可部署的智能体运行框架Skill 则是挂在这个框架上的一组可执行脚本和能力封装。你把“抓什么、怎么抓、抓完存哪”写成一个 Skill交给 OpenClaw 按指令或定时任务去跑它就能连续、稳定地完成任务。这篇文章就围绕跨境电商数据抓取这个真实场景展开既是 OpenClaw 的部署记录也是 Skill 从设计、编码到排错的全流程实战分享。适合刚接触 OpenClaw 的初学者也适合已经跑通基础环境、想搞懂“Skill 到底怎么写才靠谱”的进阶玩家。我会把每一步背后的为什么讲清楚因为你踩过的坑十有八九都是因为只知道按教程敲命令却没弄懂原理。1. OpenClaw与Skill先搞清楚它们在数据抓取里的角色1.1 OpenClaw到底是什么OpenClaw 本质上是一个本地优先的智能体运行环境。你可以把它理解成一个“调度中枢”它负责接收任务、拆分任务、调用工具、串联流程并把最终结果整理给你。它不内置某个大模型而是通过配置接入不同的推理后端既可以是 OpenAI、Anthropic 等 API也可以是本地跑着的 ollama、qwen2.5 这类开源模型。在跨境电商数据抓取场景里OpenClaw 最大的价值不是“思考”而是“行动”。它能拿到你输入的任务描述理解成一连串需要执行的动作然后把每个动作映射到具体的 Skill。比如你说“抓取某个分类前 20 个商品的标题、价格、评论数”OpenClaw 会找到对应 Skill传入参数等待执行结果再决定下一步动作。这里有个容易误解的地方很多人把 OpenClaw 当成一个“爬虫工具”认为它就是替代 Scrapy 的新框架。实际上 OpenClaw 更接近一个“指挥官”爬虫部分仍然要由 Skill 里的 Python 脚本、Playwright 浏览器操作或 API 请求来完成。它的优势在于把这些代码用标准方式组织起来让你不需要每次从零写流程管理系统。1.2 Skill机制将抓取逻辑封装成可复用单元Skill 的形态很直接一个目录里面包含描述文件、入口脚本、依赖清单和可选资源文件。OpenClaw 启动时会把 Skill 注册到自己的工具列表里之后你可以通过自然语言调用它也可以通过 API、定时任务或事件触发它。一个典型 Skill 目录长这样skill_amazon_product/ ├── skill.json # Skill 元信息名称、描述、参数定义 ├── main.py # 入口脚本接收参数并执行抓取 ├── requirements.txt # Python 依赖 └── utils/ ├── parser.py # 页面解析辅助函数 └── retry.py # 重试与限流逻辑skill.json里的核心字段是description和parameters。OpenClaw 会根据描述决定“什么时候该调用这个 Skill”根据参数定义把用户指令里的变量提取出来比如url、max_items、output_format。你写描述时一定要具体包含足够多的关键词否则 OpenClaw 可能将该调用的技能识别成别的脚本这也是很多 Skill“没反应”的根源。把抓取逻辑封装成 Skill 的好处在于第一逻辑隔离一个 Skill 挂了不会影响主进程第二参数标准化同一个 Skill 可以复用于不同站点或分类页第三可以配合 OpenClaw 的记忆和代码执行能力在失败时自动换策略重试比如从静态解析降级到浏览器渲染。1.3 和“直接用Python脚本爬”相比OpenClaw Skill赢在哪如果你已经会用 Python 写爬虫大概率会有疑问我直接写个crawler.py扔到 crontab 里定时跑不行吗当然行。但实际维护过就会发现脚本一旦变多、需求一变你就会开始重复造轮子需要一个统一的地方管理所有采集脚本的参数、状态和日志需要把脚本运行和任务调度、异常通知、结果存储串起来希望用自然语言临时调整抓取范围而不是去改代码想基于抓取结果触发后续动作比如发送通知、写入数据库或者再抓下一层页面。OpenClaw 的 Skill 体系正好补上了这些。你只要写好一个 SkillOpenClaw 就自动给了你任务的输入输出契约、可观察的日志、持久化的运行状态。它还支持并行启动多个 Skill 实例配合异步执行比单脚本轮询更清晰。当然它也不是银弹。如果你的抓取目标非常固定、结构简单直接写脚本反而更快。OpenClaw Skill 更适合“多站点、多任务、流程会变化、需要跟其他系统联动”的场景。跨境电商数据抓取恰好是这种典型站点多、反爬策略多变、字段需求经常调整。2. 环境搭建从零跑通OpenClawWindows版2.1 准备WSL2和Node.js环境绕开最常见的“无法验证”坑Windows 上装 OpenClaw第一道坎往往是 WSL2。很多教程直接让你在 PowerShell 里跑wsl --install然后就没下文了。实际执行时你可能会遇到“OpenClaw 无法安全验证 WSL2 环境”这类提示或者运行wsl -- status报错。这里不用慌本质上就是 WSL2 内核没更新或者默认发行版没安装好。我的固定套路如下第一步以管理员身份打开 PowerShell执行升级命令wsl --update这个命令会把 WSL2 内核更新到最新版。如果系统提示“更新未完成”多半是 Windows 版本太旧先去检查系统更新把补丁装完再回来。第二步安装并设置默认发行版wsl --install --distribution Ubuntu-22.04 wsl --set-default Ubuntu-22.04OpenClaw 很多底层工具依赖 Linux 环境比如 Playwright 的浏览器依赖、fps 相关的系统库在 WSL 里处理更顺滑。你不需要把整个项目都放 Linux但至少要让 OpenClaw 的主服务跑在 Linux 侧。第三步验证环境wsl -- status正常情况下会看到类似“默认分发版: Ubuntu-22.04”和“默认版本: 2”的信息。如果显示的还是 WSL1就手动转换wsl --set-version Ubuntu-22.04 2之后在 WSL 终端里安装 Node.js。这里我建议直接用 NodeSource 的源Ubuntu 自带的 nodejs 版本有时太旧会导致 OpenClaw 初始化失败。实测在 Ubuntu 22.04 下Node.js 建议选 18 或 20 的长期维护版curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs node -vOpenClaw 安装时还会依赖 Python 3.10 和一些编译工具提前装好可以省很多事sudo apt-get update sudo apt-get install -y python3-pip python3-venv git build-essential2.2 拉取OpenClaw安装依赖并启动服务OpenClaw 目前没有一键安装的 Windows 图形程序绝大多数情况下是通过 Git 拉取仓库后本地启动。我的习惯是建一个干净的目录比如~/workspace/openclawgit clone https://github.com/your-registry/openclaw.git cd openclaw npm install npm run setupnpm run setup会做几件事创建默认配置目录、检查模型后端是否可用、初始化 Skill 目录。跑完之后能看到对应平台生成的openclaw.config.js或环境文件。我建议不要把任何 API Key 写死在配置文件里用环境变量方式传入例如export OPENCLAW_MODEL_PROVIDERollama export OPENCLAW_MODEL_NAMEqwen2.5:3b export OPENCLAW_API_BASEhttp://localhost:11434然后启动服务npm start启动成功后终端会显示监听端口和日志路径。你可以另开一个 PowerShell 访问http://localhost:端口来确认服务已经在跑。如果只是命令行模式也可以直接进入交互会话先跑一个hello之类的内置 Skill 验证链路。2.3 本地模型与API两种算力配置方案OpenClaw 本身不产生“智能”它需要一个模型来理解你的指令并决定调用哪个 Skill。模型可以走两类方向本地模型例如 ollama 部署的 qwen2.5-3b优势是免费、隐私好、响应速度可控。缺点是模型小了理解能力确实有限特别是一些带歧义的抓取指令它可能选错 Skill 或把参数填歪。我的经验是3B 以下模型适合流程比较固定的内部脚本比如“定时抓这个固定 URL”不太适合临时让它“分析一下页面结构然后自己改策略”。如果你技术底子不错可以用 7B 甚至 14B 的量化模型跑起来也还凑合。云端 API例如 OpenAI、Anthropic 或国内大模型厂商的接口理解能力强复杂指令也能 hold 住但每次调用都有成本和环境依赖。对于频繁抓取任务我不建议每步都让大模型干预。更经济的做法是用云端模型应对“动态指令”用固定的 Skill 脚本完成抓取本身。也就是让 OpenClaw 只在拆解任务阶段用大脑实际操作阶段完全交给代码。我自己的典型配置是外部 API 负责调度和自然语言理解核心抓取逻辑全部交给 Skill 里的 Python 脚本跑完后的结构化数据直接写入 MySQL 或 CSV全程不把实际数据塞给大模型。这样做既省钱又减少了数据在中间层被截断的概率。3. 亲手写第一个抓取Skill商品信息采集3.1 Skill的标准目录结构与配置文件这里我不拿“Hello World”糊弄你直接写一个真实能用的商品信息采集 Skill目标站点以亚马逊若干站点的公开商品页为例实际运营时请务必先看目标站点条款和 robots 协议只采集允许公开访问的数据并设置合理频率。首先在 OpenClaw 的skills目录下建一个文件夹命名为amazon_productcd ~/workspace/openclaw/skills mkdir amazon_product cd amazon_product touch skill.json main.py requirements.txtskill.json是 OpenClaw 识别这个 Skill 的入口。一份可用的配置如下{ name: amazon_product, description: 抓取跨境电商商品页面的标题、价格、评分、评论数和可用性。适用于亚马逊、eBay等公开商品页。, version: 1.0.0, parameters: { type: object, properties: { url: { type: string, description: 商品页完整URL }, max_retries: { type: integer, default: 3, description: 最大重试次数 }, timeout: { type: integer, default: 15, description: 请求超时秒数 }, output: { type: string, enum: [json, csv], default: json, description: 输出格式 } }, required: [url] } }这里description写得越具体越好。因为 OpenClaw 是靠语义匹配决定调用哪个 Skill 的如果你写“抓取商品信息”它可能在多个技能之间犹豫写成“抓取商品页面的标题、价格、评分、评论数和可用性”命中率会明显提升。requirements.txt先放最基础的库requests beautifulsoup4 lxml retry3.2 用requestsBeautifulSoup实现静态页面采集很多商品页的 HTML 里直接包含标题和价格文本不需要模拟浏览器就能拿下来。对应main.py的核心逻辑我拆成三块请求、解析、输出。请求层要注意三点设置 User-Agent、控制超时、做好重试。不要裸奔用默认 Python UA这也是被站点安全策略拦掉的第一个原因。可以合并维护一组浏览器 UA每次请求轮换。import requests from bs4 import BeautifulSoup import json import sys import time from retry import retry UA_LIST [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15 ] def get_random_ua(): return UA_LIST[int(time.time()) % len(UA_LIST)] retry(tries3, delay2, backoff2) def fetch_page(url, timeout15): session requests.Session() session.headers.update({User-Agent: get_random_ua(), Accept-Language: en-US,en;q0.9}) response session.get(url, timeouttimeout) response.raise_for_status() return response.text解析层不能只靠一套选择器。同一个站点在不同地区、不同时间HTML 结构可能微调。我的做法是先尝试几个候选选择器取第一个有结果的内容。def parse_product(html): soup BeautifulSoup(html, lxml) title None for sel in [#productTitle, h1#title, .product-title]: el soup.select_one(sel) if el: title el.get_text(stripTrue) break price None for sel in [.a-price .a-offscreen, .priceToPay .a-offscreen, span.apexPriceToPay]: el soup.select_one(sel) if el: price el.get_text(stripTrue) break rating None for sel in [span[data-hookrating-out-of-text], .a-icon-alt]: el soup.select_one(sel) if el: rating el.get_text(stripTrue) break rating_count None for sel in [span#acrCustomerReviewText, a#acrCustomerReviewLink]: el soup.select_one(sel) if el: rating_count el.get_text(stripTrue) break availability None for sel in [#availability span, .availability]: el soup.select_one(sel) if el: availability el.get_text(stripTrue) break result { url: url, title: title, price: price, rating: rating, rating_count: rating_count, availability: availability } return result入参解析与输出部分从命令行参数里读取 URL 和配置。不建议让 Skill 自己解析大段的自然语言参数在skill.json里声明后OpenClaw 会把用户指令映射成结构化参数传给脚本。def main(): # 实际运行时 OpenClaw 会以 JSON 字符串传入参数这里兼容命令行 import argparse parser argparse.ArgumentParser() parser.add_argument(--url, requiredTrue) parser.add_argument(--timeout, typeint, default15) parser.add_argument(--output, defaultjson) args parser.parse_args() html fetch_page(args.url, args.timeout) data parse_product(html) if args.output json: print(json.dumps(data, ensure_asciiFalse, indent2)) else: print(,.join([str(data.get(k, )) for k in [url, title, price, rating, rating_count, availability]]))运行测试python main.py --url https://www.example.com/product-page --output json如果一切正常你会看到类似这样的 JSON{ url: https://www.example.com/product-page, title: Stand Mixer 5.0L, price: $299.99, rating: 4.6 out of 5 stars, rating_count: 1,234, availability: In Stock }3.3 动态页面兜底接入Playwright重试实际抓取时你会发现有些页面数据不是直接写在 HTML 里的而是通过 JavaScript 动态加载。如果 requests 解析返回的字段全是null就该切到浏览器渲染方案。我习惯在静态解析失败后再启动 Playwright而不是一开始就用浏览器因为后者吃资源、速度慢十几条数据也会拖到十分钟。改造一下抓取入口增加一个“必要字段是否完整”的判断def is_data_complete(data): return bool(data.get(title) and data.get(price))如果is_data_complete为假就调用 Playwright 版本重新抓取。这里注意playwright需要提前安装浏览器内核pip install playwright playwright install chromium对应的渲染抓取函数def fetch_page_with_playwright(url, timeout30): from playwright.sync_api import sync_playwright with sync_playwright() as pw: browser pw.chromium.launch(headlessTrue, args[--no-sandbox]) context browser.new_context(user_agentget_random_ua(), localeen-US) page context.new_page() page.goto(url, wait_untilnetworkidle, timeouttimeout * 1000) page.wait_for_selector(#productTitle, timeouttimeout * 1000) html page.content() browser.close() return html在main()里改成一个简单的降级逻辑html fetch_page(args.url, args.timeout) data parse_product(html) if not is_data_complete(data): print(静态解析数据不完整切换到Playwright..., filesys.stderr) html fetch_page_with_playwright(args.url, args.timeout) data parse_product(html)这套“先快后稳”的策略很大程度兼顾了速度与成功率。在大多数商品列表页上静态解析能命中 70% 以上的页面剩下 30% 走浏览器渲染总体耗时仍可接受。4. 抓取策略进阶让Skill变得聪明且守规矩4.1 限流、重试与代理先算好并发账数据抓取不是不能跑快而是要在目标站点可接受范围内跑得快。拿商品页来说常见站点的响应时间大约在 300 到 800 毫秒单线程抓 1000 条可能需要 5 到 15 分钟。很多人一上来就开 20 个线程结果要么触发站点限流要么本地网络先崩。这里有个参数估算公式每秒请求数 单次请求平均耗时 / 并发数。如果你的平均请求耗时是 0.5 秒单线程就是每秒 2 个请求。开 5 个并发每秒就变成 10 个请求。对中小型跨境电商站点来说每秒 5 到 10 个请求算比较保守如果要达到更高频率就必须提前确认目标站点是否允许并使用合规的商业代理池来分摊 IP 压力。retry库的重试策略我建议用“指数退避 抖动”from retry import retry retry(tries5, delay1, backoff2, jitter(0, 0.2)) def fetch_page_with_retry(url, timeout15): # 请求代码 pass这里tries5表示重试 5 次delay1表示第一次重试前等待 1 秒backoff2表示每次等待时间翻倍。这样能有效避开瞬时抖动又不会对目标造成持续压力。重试超过次数后我会记录失败 URL 并继续下一条而不是让整个任务中断。关于代理只提一种合规场景如果你运营的店铺本身需要用到数据中心代理或住宅代理这些通常从正规服务商采购用于访问公开访问的数据。配置代理时可以把代理地址写到环境变量里Skill 启动时自动读取export HTTP_PROXYhttp://your-proxy-host:port export HTTPS_PROXYhttp://your-proxy-host:port注意检查目标站点是否禁止代理访问以及代理服务商的使用条款。没有许可证的抓取行为不仅违反平台条款还可能涉及法律风险务必谨慎。4.2 数据解析与字段清洗不只取回数据要取对数据抓取完成了只成功了一半字段是否干净直接决定后续能不能用。我在本地跑了上千条数据之后发现最影响下游使用的问题集中在价格、评论数和评分。价格字段常见陷阱是“符号 数字”混在一起比如$1,299.99。你需要统一换算成数字方便比较和计算同时保留原始文本用于展示。我在 Skill 里加一个clean_price函数import re from decimal import Decimal def clean_price(price_str): if not price_str: return None cleaned re.sub(r[^\d.], , price_str) try: amount Decimal(cleaned) return str(amount.quantize(Decimal(0.01))) except Exception: return None评论数的问题则是“1,234”和“1.2K”这类缩写表示。统一转成整数def parse_count(text): if not text: return None text text.strip().upper() if K in text: return int(float(text.replace(K, )) * 1000) if M in text: return int(float(text.replace(M, )) * 1000000) return int(text.replace(,, ))评分字段有的写“4.6 out of 5 stars”有的写“4.6星”我建议只保留数字部分并转成浮点数def parse_rating(text): if not text: return None match re.search(r(\d\.?\d*), text) return float(match.group(1)) if match else None清洗后的数据会统一进入结果对象原始文本也会保留在raw字段里方便排查异常。4.3 反爬边界与合规红线能抓什么、不能碰什么这里必须花点篇幅讲清楚边界。OpenClaw Skill 是自动化工具不是攻击工具。在跨境电商场景里数据合规是重中之重。我自己的原则很简单只抓公开页面、尊重 robots 文件、不尝试绕过登录墙和验证码、遵守目标站点服务条款、控制抓取频率防止影响正常用户访问。如果某个站点明确禁止自动抓取或者对登录态内容做了强校验那就不要强行突破。市面上确实有人教“破解验证码”“模拟人类轨迹”这些文章热度高但出了问题第一个被追责的就是实施者。做独立站运营或电商选品更稳的替代方案是通过平台官方 API、授权数据服务商、或者浏览器插件提供的导出功能把合规数据拿到本地。在你自己的 Skill 代码里我推荐加一段访问策略检查。比如读取目标域名根目录的robots.txt如果对应路径禁止访问就自动跳过from urllib.robotparser import RobotFileParser def can_fetch(url): rp RobotFileParser() rp.set_url(https://example.com/robots.txt) rp.read() return rp.can_fetch(*, url)虽然实际执行不一定每次都能读到但这个动作能提醒自己和团队我们是在做边界内的事情。别嫌麻烦这是从业者的基本素养。5. 综合实战把目标站标题/价格/评论数抓到CSV5.1 目标页面分析与接口定位现在把它们揉成一个综合任务。假设我们要采集某个头部跨境电商公开商品列表页的前 20 个商品信息并把结果输出成 CSV。第一步先人工打开页面按 F12 看 DOM 结构和网络请求。如果是静态列表页商品卡片会在 HTML 里可以直接用 BeautifulSoup 解析。如果商品卡片是 JS 渲染就要找底层 API。很多站点会向/api/products/search这类路径发送 JSON 请求返回结构化商品信息。直接调用这个接口往往比解析 HTML 更稳、更省资源。查看网络面板时重点看XHR和Fetch类型的请求尤其是返回 JSON 的那些。找到接口后用 requests 模拟同样的请求把需要的字段提取出来。这比 Playwright 更高效也是进阶工程师和初学者的分水岭不要只盯着 HTML学会找数据背后的接口。5.2 Skill代码逐段拆解这里我以“页面是静态 HTML”为主路径、以“接口请求”为备选路径来写一个综合 Skill。命名cross_border_list目录结构如下cross_border_list/ ├── skill.json ├── main.py └── requirements.txtskill.json描述里写清楚用途{ name: cross_border_list, description: 抓取跨境电商列表页商品数据包括标题、价格、评分、评论数和详情链接。支持静态页面和JSON接口两种模式。, parameters: { type: object, properties: { start_url: {type: string, description: 列表页URL}, max_items: {type: integer, default: 20}, output_path: {type: string, default: ./output.csv} }, required: [start_url] } }main.py核心代码我按功能分块。第一块配置参数与辅助函数import csv import json import sys import time import re import requests from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse第二块静态页面解析def parse_list_html(html, base_url): soup BeautifulSoup(html, lxml) items [] for card in soup.select([data-component-types-search-result], .product-item): title_el card.select_one(h2 span, .title) price_el card.select_one(.a-price .a-offscreen, .price) rating_el card.select_one(.a-icon-alt, .rating) count_el card.select_one(span.a-size-base, .reviews) link_el card.select_one(h2 a, a.title-link) if not title_el: continue item { title: title_el.get_text(stripTrue), price: clean_price(price_el.get_text(stripTrue) if price_el else ), rating: parse_rating(rating_el.get_text(stripTrue) if rating_el else ), rating_count: parse_count(count_el.get_text(stripTrue) if count_el else ), url: urljoin(base_url, link_el[href]) if link_el else } items.append(item) if len(items) max_items: break return items这里用到了前面写的clean_price、parse_rating、parse_count在完整脚本里直接复制。第三块JSON接口解析有时候 HTML 里没有完整数据但接口返回了 JSON。这时候直接解析 JSON 字段效率最高def parse_list_api(json_data): items [] results [] # 这里按常见的 JSON 结构做兼容实际根据目标接口调整 if isinstance(json_data, dict): results json_data.get(data, {}).get(items, json_data.get(results, [])) elif isinstance(json_data, list): results json_data for item in results: title item.get(title) or item.get(name) price item.get(price) or item.get(sale_price) rating item.get(rating) or {} count item.get(review_count) or item.get(reviews) or {} items.append({ title: title, price: clean_price(price), rating: parse_rating(rating) if isinstance(rating, str) else (rating or 0), rating_count: int(count) if isinstance(count, int) else parse_count(str(count)), url: item.get(url) or item.get(link, ) }) return items第四块主流程def main(): start_url sys.argv[1] if len(sys.argv) 1 else None max_items int(sys.argv[2]) if len(sys.argv) 2 else 20 output_path sys.argv[3] if len(sys.argv) 3 else ./output.csv ... response requests.get(start_url, headersheaders, timeout15) html response.text data parse_list_html(html, start_url) if len(data) 0: data parse_list_api(response.json()) # 如果接口可用 # 写入CSV with open(output_path, w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[title, price, rating, rating_count, url]) writer.writeheader() writer.writerows(data[:max_items]) print(f写入{len(data[:max_items])}条数据到{output_path})这个 Skill 的实际代码还要加上异常处理和日志但整体骨架已经很接近生产可用。把它放进 OpenClaw 的skills目录后重新启动 OpenClaw 服务你就能通过指令直接调用调用 cross_border_list 抓取 start_url取前20条保存到 products_2025.csvOpenClaw 会解析出参数并执行返回结果摘要。5.3 定时调度与结果入库数据抓回来之后下一个需求多半是“每天自动跑”。OpenClaw 提供了定时任务能力可以在配置里声明 cron 表达式。比如每天凌晨 2 点执行一次{ skill: cross_border_list, schedule: 0 2 * * *, arguments: { start_url: https://www.example.com/category?page1, max_items: 50, output_path: /data/products_daily.csv } }定时任务跑完之后我建议把结果追加到一个带日期的文件里或者直接写入数据库。CSV 文件如果要长期累积文件名加时间戳from datetime import datetime ts datetime.now().strftime(%Y%m%d_%H%M) output_path fproducts_{ts}.csv对于更正式的入库场景我会把 CSV 改为直接执行 SQL INSERT。Skill 里加载pymysql或psycopg2把items列表逐行写入库表。这里注意把清洗后的数字字段由字符串转成 Python 原生类型否则数据库会报错。import pymysql conn pymysql.connect(hostlocalhost, userroot, password..., databaseshop_data) cursor conn.cursor() for item in items: cursor.execute( INSERT INTO products (title, price, rating, rating_count, url, crawled_at) VALUES (%s, %s, %s, %s, %s, NOW()), (item[title], float(item[price] or 0), float(item[rating] or 0), int(item[rating_count] or 0), item[url]) ) conn.commit() cursor.close() conn.close()入库前最好做个去重如果存在同 URL 记录且抓取时间在当天就跳过。这样定时任务再怎么跑数据也不会膨胀。6. 常见问题排查实录我踩过的那些坑6.1 WSL和PowerShell环境问题汇总OpenClaw 在 Windows 下的集成问题有一半出在 WSL 和 PowerShell 的配合上。最典型的报错就是“OpenClaw 无法安全验证 WSL2 环境”多半是因为运行wsl -- status时系统返回了“未安装”或“默认版本不是2”。排查步骤在 PowerShell 执行wsl -- status看前几行是否有“默认分发版”和“默认版本”。如果显示 WSL 已安装但没有默认分发执行wsl --set-default Ubuntu-22.04。如果默认版本还是 1执行wsl --set-version Ubuntu-22.04 2。如果wsl --update报“不能更新”先升级 Windows 补丁再重试。另一个高频坑是 Node.js 找不到。在 PowerShell 里装了 Node.js但 WSL 里的node命令照样不存在。因为 WSL 是独立的文件系统你要在 WSL 内部安装 Node.js或者在 PowerShell 里把 Windows 的 Node 路径C:\Program Files\nodejs加到 WSL 的 PATH 中但我不推荐这样跨系统调用容易发生权限问题。直接在 WSL 里重新装 Node.js 更稳妥。6.2 Skill运行报错与依赖冲突Skill 运行失败常见原因有三个。第一依赖没装。新写的 Skill 依赖pandas或者lxml但 OpenClaw 的 Python 环境和你的开发环境不是同一个。部署后记得在 OpenClaw 所在的虚拟环境里单独执行pip install -r requirements.txt第二浏览器内核缺失。使用 Playwright 的 Skill 在 Linux 环境启动时会提示chromium executable doesnt exist。执行playwright install chromium还不够有些服务器还缺系统依赖库playwright install-deps chromium这个命令需要 sudo 权限权限不足时就容易报Missing libraries。这也是为什么我建议在 WSL 里跑 OpenClaw而不是直接用 Windows 的命令行install-deps在 Linux 下成功率高很多。第三环境变量没生效。我在shell里设了export OPENCLAW_API_BASE...但 OpenClaw 启动进程时读不到。后来发现要写到.env文件里或者放在openclaw.config.js中module.exports { model: { provider: process.env.OPENCLAW_MODEL_PROVIDER || ollama, name: process.env.OPENCLAW_MODEL_NAME || qwen2.5:3b, apiBase: process.env.OPENCLAW_API_BASE || http://localhost:11434 } };第四参数解析错误。OpenClaw 把用户指令映射成 Skill 参数时如果参数类型和skill.json里声明不一致Skill 会直接报错。我就遇到过想把max_items传成 “20 条” 但成了字符串 “20”Python 里int()转换失败。稳妥做法是在 Skill 入口做一层参数整理把所有输入都强制转换到预期类型而不是信任 OpenClaw 传过来的原始值。6.3 抓取结果的质量问题排查数据到了手上问题更隐蔽。比如价格变成None或标题出现一堆空白字符。我的排查顺序是检查抓取时的 HTML 是否完整把html保存到本地用浏览器打开看目标元素是否真的存在。检查选择器是否因为站点改版失效多数站点会定期调整 CSS 类名用flex或grid布局的站更明显。遇到这种情况优先改用更稳定的属性选择器比如基于>