ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

网页转表格Skill实战:从SKILL.md到自动导出Excel

网页转表格Skill实战:从SKILL.md到自动导出Excel 前阵子帮朋友整理一批公开网页上的报价数据十几个页面每个页面都是一堆表格。我当时的做法很原始一个个页面打开选中表格复制到 Excel再手动清理格式。弄到第三个页面的时候我就烦了顺手把这些 URL 都丢给 AI 编程助手让它“把网页里的表格整理成 Excel”。结果它倒是很勤快给我回了一段又一段 Markdown 表格看起来整齐可一旦贴进 Excel合并单元格全乱、换行全丢还得重新加工。后来我研究了一下 Claude Code / Codex 里新出的 Skill 机制发现这东西正好治这个病。所谓 Skill简单说就是一个打包好的“技能目录”里面有一份 SKILL.md 说明文档外加若干脚本和参考文件。AI 助手在看到用户需求的时候会根据说明自动加载这个技能按里面的流程执行。我把“网页转表格”这套流程做成了一个 Skill从那以后再遇到这类需求只需要给一个 URL剩下的采集、清洗、导出一条命令全搞定。这篇文章就把这个 Skill 从设计到落地的完整过程拆开来讲包括 SKILL.md 怎么写、脚本怎么调、坑在哪里希望对正在折腾 Skill 的同学有点用。1. 这个 Skill 到底做了什么和普通提示词差在哪1.1 一句话需求拆解标题已经说得很直白“网页数据直接变成一张能编辑的表格”。拆开来看其实是三个环节输入层用户提供一个公开网页的 URL可能还附带一句要求比如“只要带‘价格’的那张表”处理层程序去读取页面 HTML找出其中的结构化数据table或者列表型数据清洗掉空行和重复表头输出层生成一份能被 Excel、WPS、Numbers 直接打开的文件CSV 也行xlsx 也行需求本身不复杂但问题在于“找表格”和“清洗数据”这两步不同网页差异极大。有的是静态 HTML 表格一条请求就能拿到有的是 JS 动态渲染源码里根本看不到数据还有的是多表页面需要 AI 自己判断哪个才是用户要的。这就是我宁愿做成一个 Skill 而不是用一次性提示词硬怼的原因。1.2 Skill 是“岗位说明书 工具箱”不是一句吩咐我见过很多人混淆 Skill 和普通提示词其实差别非常大。普通提示词是一句话你说完就完了AI 只能靠它自己的常识去猜测你的意图。Skill 则是一整套“能力包”里面写清楚了这个技能在什么场景下触发description完整的工作流程比如先看页面再跑脚本最后导出允许调用的脚本和命令依赖清单requirements.txt甚至包含了参考样例打个比方普通提示词等于你临时跟一个新来的实习生说“帮我把这个网页的表格整理一下”他可能用各种奇怪的方式干Skill 等于你给这个实习生发了一份《网页数据整理标准作业流程》里面规定了他第一步干什么、第二步干什么、遇到什么情况该用哪个工具。AI 再聪明也架不住你的工作流本身就定义得清楚。1.3 Skill 和 Agent 的区别先说清楚避免混淆热词里有人搜“skill和agent的区别”这里简单带一句。Skill 是被动能力Agent 是主动执行者。Skill 如同工具箱里的锤子Agent 如同使用锤子的工人。Agent 接收任务后会拆解问题、选择策略、按需调用 SkillSkill 则尽量保持单一职责把一件具体的事做扎实。我后面会展开讲协作边界但一开始就把这个模型立住后面读起来会顺畅很多。2. Skill 的目录结构和运行原理动手前得知道2.1 一个 Skill 目录长什么样我先给出最简可用的结构web2table/ ├── SKILL.md ├── scripts/ │ ├── fetch_table.py │ └── requirements.txt └── reference/ └── examples.mdSKILL.md 是这个技能包的入口。当 AI 判断用户需求匹配该技能的 description 时就会读这个文件然后按文件里的指示行动。scripts 目录放可执行的脚本reference 目录放参考文档AI 需要的时候会自己去看。2.2 SKILL.md 的 frontmatter 千万别乱写SKILL.md 的开头有几行 YAML 格式的元信息这是整个 Skill 能不能被正确调用的关键--- name: web2table description: 从公开网页中提取表格数据并导出为 CSV/Excel 文件。当用户提供网页链接并要求生成表格时使用。适合“网页转表格、采集网页数据、抓取网页表格、网页数据整理”等需求。 ---用 Claude Code 的时候这个 Skill 通常放在项目的.claude/skills/web2table/目录或者用户级目录~/.claude/skills/web2table/。Codex 的机制类似一般放在.codex/skills下。具体放哪可以查各自文档关键是 description 一定要写足语义。这里面最容易被忽略的坑就是 description。很多 Skill 写不好触发条件结果 AI 匹配不上你明明装了 Skill它却不用最后照样用普通方式输出一堆 Markdown。我的经验是description 里一定要写清楚“什么时候用”和“用来干什么”最好带上业务关键词让它能在语义匹配阶段就撞上。2.3 脚本怎么被调用依赖声明为什么能卡死人AI 读 SKILL.md 之后会按照里面的流程自行决定要不要执行脚本。执行方式是标准的命令行调用比如python3 scripts/fetch_table.py --url https://example.com/page --format csv --output result.csv所以脚本的入口设计要“命令行友好”参数走 argparse 或 click输出保持稳定。别把一堆逻辑写进交互式代码里AI 执行起来不方便。这里还要强调一点热词里有人搜“python skill 缺少 requirements.txt 或依赖声明”这个问题我实测真的会翻车。如果你的脚本用到 pandas、requests 这些第三方库必须在 scripts 目录下放一个 requirements.txt。requests2.31.0 beautifulsoup44.12.2 pandas2.1.4 lxml4.9.3 openpyxl3.1.2AI 看到 Skill 目录里有脚本会自动检查依赖。没有 requirements.txt很多环境里它会直接说“缺少依赖声明”然后卡在那里不敢继续或者胡乱装一堆包浪费时间。2.4 为什么选 Python不选 Node说白了就三点pandas 处理表格数据太方便BeautifulSoup 的选择器生态比我见过的任何 Node 库都顺手AI 编程助手生成 Python 脚本的成功率最高。我不反对用 Node但在这个“采集 清洗 导出”的组合场景里Python 的代码量最少、可读性最强对 Skill 这种需要“让 AI 能读懂并维护”的东西来说这是实打实的优势。3. 设计这个 Skill 时我怎么拆解需求3.1 先把网页分三类再决定要不要上重武器做采集之前必须认清一个现实不是所有网页的表格都能用同一套代码拿下来。我的经验是分成三类网页类型特征方案静态表格页数据在 HTML 源码里直接可见requests BeautifulSoup动态渲染页数据由 JS 异步加载源码里只有空壳Playwright 无头浏览器多表/分页页一个页面多张表或数据分散在多个页面先列出所有表格AI 判断或按关键词过滤这个分类直接决定了脚本要不要上浏览器自动化。我的态度是主流程尽量轻量先尝试 requests发现没有表格再加 Playwright。不要一上来就开浏览器速度和稳定性都差。3.2 pandas.read_html 能用但控制力太弱一开始我图省事想过直接靠 pandas 的read_html一行代码就能把页面里所有table变成 DataFrameimport pandas as pd tables pd.read_html(url)实测下来它对规整的静态表格确实挺快但一碰到合并单元格、表头跨行、页面里混着导航条这种场景读出来的数据就乱七八糟。后来我还是老实回归 BeautifulSoup 手动定位用 CSS 选择器精确到目标表格再逐行清洗。简单场景可以用read_html兜底但 Skill 的设计目标是“能应对多种页面”所以核心逻辑还是得自己写。3.3 Skill 的输入输出怎么设计才算好用我把输入设计成三个参数url必填网页地址selector可选CSS 选择器定位某一特定表格不填就全页面找format可选csv/xlsx/md默认csv输出则统一落到一个文件里同时给 AI 返回一段摘要说明找到几张表、导出后有多少行、去掉了哪些空行。这样 AI 就能直接拿着摘要向用户汇报体验好很多。这里有个细节值得分享为了让可编辑性最好我最后导出默认用 CSV因为 Excel/WPS 都能直接打开编码用 UTF-8 with BOM否则用 Excel 打开中文 CSV 十有八九乱码。用户明确要 xlsx 时再走 openpyxl。3.4 这个 Skill 的适用边界一开始就要划定我只做公开合法、不需要登录访问的页面。如果页面本身需要登录、有明显访问限制脚本会直接退出并提示用户而不是尝试绕过任何机制。这条写在 SKILL.md 的注意事项里既是给 AI 的行为边界也是给自己省事。Skill 这种东西越界一次就不好收场边界写清楚后面少很多麻烦。4. 完整实现SKILL.md 和主脚本4.1 SKILL.md 的完整内容下面是我这个 Skill 的 SKILL.md 正文加了注释供参考--- name: web2table description: 从公开网页中提取表格数据并导出为 CSV/Excel 文件。当用户提供网页链接并要求生成表格时使用。适合“网页转表格、采集网页数据、抓取网页表格、网页数据整理”等需求。 --- # 网页转表格 将公开网页中可见的结构化表格数据提取为可编辑文件。 ## 工作流程 1. 确认目标 URL并判断是否为公开可访问页面。 2. 优先运行 scripts/fetch_table.py不手工复制粘贴页面内容。 3. 若脚本识别出多个表格根据用户描述或关键词筛选。 4. 使用脚本导出文件并检查文件是否有正常行数和表头。 ## 调用方式 python3 scripts/fetch_table.py --url URL [--selector CSS选择器] [--format csv|xlsx|md] [--output 文件名] ## 注意事项 - 只处理公开合法的数据不用于任何有访问限制的内容。 - 页面如果明显是 JS 渲染脚本会尝试二次抓取若仍失败应如实告知用户。 - 不要随意更改脚本导出的原始数据清洗逻辑已写死在脚本中。 - 若脚本需要修改先向用户说明改动原因再执行。为什么把“注意事项”单独列一节因为这些内容不是给用户看的是给 AI 看的“边界手册”。Skill 和普通代码不同的地方在于解释器是 AI它会自己决策所以必须明确告诉它什么能做什么不能做否则它会放飞自我。4.2 主脚本 fetch_table.py 的核心逻辑脚本不复杂但有几个关键函数值得贴出来。首先是抓取 HTMLimport requests from bs4 import BeautifulSoup def fetch_html(url): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 } resp requests.get(url, headersheaders, timeout15) resp.raise_for_status() resp.encoding resp.apparent_encoding return BeautifulSoup(resp.text, lxml)这里我做了两件很容易被忽视的事设置合理的 User-Agent、用apparent_encoding校正编码。前者能避免一部分基础拦截但只是为了友好地访问公开数据不是用来绕过任何访问限制后者是中文网站高发问题特别是一些 GBK 页面requests 默认的encoding会识别错文字全变乱码。然后是定位表格并清洗import pandas as pd def extract_tables(soup, selectorNone): if selector: table_list soup.select(selector) else: table_list soup.find_all(table) result [] for idx, table in enumerate(table_list): df pd.read_html(str(table))[0] df df.dropna(howall) df df.drop_duplicates() if df.iloc[:, 0].isna().all(): df df.drop(columnsdf.columns[0]) result.append({index: idx, rows: len(df), frame: df}) return resultdropna(howall)和drop_duplicates()就是清洗重复行和空行的主力。表头如果被解析成第一列 NaN也顺手处理掉。最后导出def export(df, fmt, output): if fmt csv: df.to_csv(output, indexFalse, encodingutf-8-sig) elif fmt xlsx: df.to_excel(output, indexFalse, engineopenpyxl) elif fmt md: df.to_markdown(output, indexFalse)完整脚本里再加一个 argparse 入口把参数串起来就行。4.3 动态页面的降级处理逻辑我在脚本里加了动态页面降级机制当静态请求没有抓到任何table时如果传入了--dynamic参数就尝试用 Playwright 加载页面等待网络空闲后再解析。这样做的逻辑很简单——尽量用轻量手段解决问题解决不了再上重武器而不是一上来就开着浏览器到处跑。def fetch_html_dynamic(url): from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessTrue) page browser.new_page() page.goto(url, wait_untilnetworkidle, timeout30000) html page.content() browser.close() return BeautifulSoup(html, lxml)注意 Playwright 不是默认依赖只有当用户确实需要动态页面抓取时才安装。我会在 SKILL.md 里写明这一点避免 AI 在没必要的时候也去装这个重库。5. 安装与实测把它装进 Claude Code / Codex5.1 安装路径实操安装很简单两个位置任选项目级把整个 web2table 目录放进项目的.claude/skills/下Codex 对应.codex/skills/这样只有当前项目能用用户级放进~/.claude/skills/Codex 对应~/.codex/skills/所有项目都能用我自己习惯先放用户级因为“网页转表格”属于通用需求每个项目都可能遇到。装好后不需要重启新会话自动生效。5.2 一次完整的实测我拿某公开统计页面做了测试。在 Claude Code 里输入帮我把这个网页里的表格整理成 Excelhttps://example.com/public-statsAI 会话中的处理逻辑大概是这样识别 URL 后它会在技能描述里匹配到web2table读取 SKILL.md然后直接执行python3 scripts/fetch_table.py --url https://example.com/public-stats --format xlsx --output stats.xlsx脚本跑完AI 看到导出摘要“共发现 3 张表已按关键词筛选出 1 张共 42 行”再向我确认是否还需要其它表。拿到手就是一个可以直接筛选、透视、排序的 Excel 文件已经去掉空行和重复表头。整个过程不到一分钟。5.3 实测中的意外动态页面抓了个寂寞第一次测试动态渲染页面时脚本运行后返回“未找到表格”但浏览器里明明有表格。排查后发现页面数据是 JS 调接口异步渲染的requests 拿到的源码里根本没有table。这个场景下我的解决思路很简单让脚本在检测不到表格时自动提示用户加--dynamic参数或者由 AI 判断后直接重跑。加了 Playwright 之后python3 scripts/fetch_table.py --url https://example.com/spa-page --dynamic --format csvPlaywright 那里其实没什么魔法就是启动 Chromium、等待网络空闲、再抓页面。代价是启动速度慢、依赖多所以我只把它作为降级方案而不是默认路径。6. 踩坑记录网页采集最常翻车的几个地方6.1 User-Agent 那块不是玄学有些页面默认会给没有浏览器特征的请求返回一个简化版页面甚至直接拒绝。这里不说怎么绕什么限制只提醒一个最基本的点合法采集公开数据时带一个正常的 UA 是礼貌也是常识。requests 默认的python-requests/x.x在不少站点都会被当成异常流量被拦了再去查 UA 就有点晚了。6.2 合并单元格会显著改变表结构Excel 里常见的合并单元格在 HTML 里对应的是colspan/rowspan。pandas.read_html解析时会留下大量 NaN如果不做清洗导出的表格看起来就坑坑洼洼。我的处理方式是先ffill向下填充再用drop_duplicates去掉因此产生的重复行。举个例子一个“产品-地区-销量”的表格地区列合并了单元格直接读出来会是“产品1 / 地区A / 销量...产品1 / NaN / 销量...”。不处理的话 AI 可能以为 Excel 坏了。6.3 编码问题特别隐蔽中文网页里还残留不少 GBK/GB2312 编码的页面requests的默认编码猜测在这些页面上经常失手。我踩过一次坑抓下来的页面标题全是乱码后来才想到用resp.apparent_encoding强制校正。这行代码成本极低收益极高。6.4 AI 可能拿着选择器乱猜Skill 让 AI 自动定位表格但有时候它太“聪明”了用户说“帮我抓一下这个页面里的表格”它不先运行脚本而是根据经验凭空给一个选择器猜测结果当然抓空。所以在 SKILL.md 的工作流程里我把“优先运行脚本”放在第二步就是为掐掉它这种自作主张的行为。Skill 的核心价值是流程标准化流程必须写死给 AI 的自由度要给在“筛选哪张表”这种判断上而不是给在“怎么抓”上。6.5 表格里混着导航、页脚和无关区块很多页面的table并不是数据表而是用来做布局的老式 HTML。遇到这种情况脚本会把导航条、页脚都当成表格抓出来导出的文件里全是没用的内容。我的处理思路是除了dropna(howall)之外对行数少于 2 的表格直接丢弃同时可以在 SKILL.md 中要求 AI 在导出前检查列名是否符合常见表头特征。这个判断标准不需要太复杂能挡住大部分噪音就行。7. 顺着这套思路Skill 还能做哪些事7.1 同一套机制换脚本就是新技能把 web2table 里解析表格的逻辑换成解析 JSON 接口的fetch_json.py就得到一个 web2json换成把结构化数据转成 Markdown 报告就是 web2report。Skill 的骨架基本都是同一个模式SKILL.md 定义触发条件和流程scripts 目录里放脚本依赖清单管好。模板一旦跑通剩下的就是替换脚本和 description。我这个 Skill 的后续升级方向也很明确支持用户自定义清洗规则、批量处理多个 URL、把导出结果直接存到指定目录。都是往scripts里加脚本的事。7.2 写 Skill 的正确姿势小任务起手我发现做 Skill 最大的门槛不是写脚本而是把一个模糊的“我想要一个技能”变成清晰的“输入是什么、输出是什么、异常怎么处理”。web2table 是个小项目但它把整套流程走通了之后再做别的 Skill 就快多了。做 Skill 时不要贪多一个 Skill 解决一个高频重复任务就够了。任务越聚焦SKILL.md 越好写AI 命中率也越高。我做 web2table 时没想过让它同时处理“发送邮件”“生成图表”之类的杂活杂活拆成另一个 Skill 反而更好维护。7.3 Skill 生态里的推荐思路现在各个 Skill 社区里能搜到不少现成技能比如有人做了测试用例生成有人做了周报生成。我的建议是别人的 Skill 可以先下载跑一遍理解它的 SKILL.md 结构然后改成自己的。完全照搬通常不太贴合你的工作流但把它当成“参考实现”来看价值很大。另外看到社区里有人在折腾各类语言相关的 skill这说明 Skill 的边界已经远远超出编程本身了。凡是“有固定流程 需要工具执行”的重复任务理论上都值得做一个 Skill。最后说点个人体会。写完这个 Skill 之后我处理“网页数据整理成表格”这一类需求的姿势彻底变了以前是复制粘贴加反复调试现在是一句话加一个 URL中间过程 AI 自己推着自己走。我发现做 Skill 最大的门槛不是写脚本而是把一个模糊的“我想要一个技能”变成清晰的“输入是什么、输出是什么、异常怎么处理”。web2table 是个小项目但它把整套流程走通了之后再做别的 Skill 就快多了。如果你也想试建议从小任务起手不要一上来就做“全能助手”。想想你最近每周都在重复哪件事把那个事做成 Skill比收藏一百个别人的 Skill 都有用。还有一个细节别忘SKILL.md 第一行的 description 写得越接近用户的真实说法AI 命中率越高。我因为这个字段反复改过很多次算是用时间换来的教训。
返回列表