iFlow CLI 实战:一条命令将网页文章抓取、翻译并保存为 Markdown

发布时间:2026/10/10 5:51:33
iFlow CLI 实战:一条命令将网页文章抓取、翻译并保存为 Markdown 1. 先说结论这个命令到底解决了什么问题我平时有大量阅读英文技术文章的习惯但真正读起来总是卡在几个点上页面广告和推荐流干扰太多、想存下来却复制到本地全是乱版、遇到长文读着费劲还得来回切翻译窗口。一开始我也图省事用浏览器插件或者在线翻译页解决但每次都要手动复制、粘贴、重新排版遇到十几页的长文更是折磨。后来我给自己定了一个小目标能不能用一个命令行命令一次搞定所有事情——给它一个网址它自己抓取正文、清理格式、输出中文翻译并且直接保存成 Markdown 文件。正好手里一直在用 iFlow CLI它支持注册自定义 Command于是我就动手做了一个网页文章下载与翻译工具这里把完整的实现思路和踩坑记录分享出来。这个方案非常适合以下几类人参考需要批量抓取网页正文做资料归档的人、经常阅读外文技术博客但又不想被在线翻译限制字符数的朋友以及正在研究 iFlow CLI 自定义命令开发、想知道配置细节和调试方法的开发者。哪怕你对 CLI 工具不熟悉只要跟着下面每一节操作也能把这套逻辑搬到自己的环境里。2. 命令的完整工作流设计2.1 从URL到Markdown的四个处理阶段在设计这个工具之前我先把整个需求拆成了几个独立的处理阶段。这样做的好处是每个阶段都能单独测试、单独替换哪怕以后换翻译引擎或者改输出格式也不用把整条链路推倒重来。整个命令的工作流分为四段抓取阶段根据传入的 URL 请求网页内容拿到 HTML 源码。这里要考虑超时、重定向、User-Agent 伪装、Gzip 压缩等基础问题稍不小心就会在编码上翻车。解析阶段从 HTML 中提取文章标题和正文。核心难点在于去除导航栏、侧边栏、页脚、广告、评论等噪声只保留作者真正写出来的内容。翻译阶段把正文文本送入翻译引擎分批处理等待结果返回并尽可能保持原文的段落结构。落盘阶段将翻译后的文本序列化成 Markdown写入本地文件同时在终端输出摘要信息告诉用户文件保存在哪里。这里插一句我自己的设计心得最开始我的想法是一步到位直接在抓取函数里翻译、翻译函数里写文件结果调试的时候根本分不清是抓取失败还是翻译失败还是编码问题。后来老老实实拆成四个函数每个函数只做一件事调试效率明显上来了。2.2 技术选型Python生态的轻量组合技术选型上我倾向用最常见的 Python 生态没有引入重型框架。整个工具就依赖四个库requests发请求、BeautifulSoup解析 DOM、html2text或者自己写的转换器做 HTML 到 Markdown 的转换、xxhash做文本指纹缓存。如果你不想装这么多也可以只用 requests BeautifulSoup最后用正则清理 HTML 标签。iFlow CLI 的自定义 Command 并不限定脚本语言只要注册时指定执行入口即可。我选择 Python 是因为它的字符串处理和文本清洗生态最成熟写起来效率最高。如果你更熟悉 Node.js用 fetch cheerio 的组合也能实现同样的效果整体逻辑完全一致。工具的运行模式我定为单条命令 可选参数必传参数文章 URL可选参数目标语言默认中文、是否保留英文原文、输出目录、是否生成带元数据的 Markdown这个设计考虑很直接大多数 AI 阅读场景只想要一份干净的译文但资料归档的用户往往希望保留原文和链接信息。参数化设计能让同一套代码适应两种用法。3. 正文提取模块的实现3.1 获取页面与编码处理的坑正文提取是整个工具的基础如果这一步拿到的是乱码或者残缺内容后面翻译得再好也没有意义。我先说抓取阶段最容易踩的几个坑。第一个坑是编码识别。绝大多数网页是 UTF-8 编码但不少老站点、个人博客用 GBK、GB2312 甚至 Latin-1如果不做处理直接按 UTF-8 解码中文会变成一片乱码。我的做法是优先读取 HTML 源码中meta charset或meta http-equivContent-Type里的声明其次用requests返回的apparent_encoding去兜底。在实测中apparent_encoding的识别准确率并不算高所以我会再写一层白名单兜底——遇到常见的 GBK 类页面时直接尝试用gb18030解码。第二个坑是反爬虫与请求头。有个别文档站会拦截默认的 Python UA。我的处理方式是在请求头中伪造一个常见的浏览器 UA并加上 Accept、Accept-Language 等字段同时设置 15 秒超时和最多两次重试。对于需要登录才能查看文章的站点我暂时没有做 Cookie 注入只是在报错信息中明确提示用户。第三个坑是重定向与短链接。很多分享出来的链接是经过短链跳转的最终页面的 URL 才是真实文章地址。我会记录重定向后的最终 URL后面生成 Markdown 元数据时以最终 URL 为准。3.2 基于DOM结构和规则评分提取正文拿到 HTML 之后接下来就是提取正文。网上有不少现成的正文提取库但它们的通病是模型较重且对中文站点适配一般。我这里采用了一种轻量但极其有效的规则评分思路原理其实很简单遍历 DOM 中的所有p标签统计每个p的文本长度和其所属父容器。给每个容器打分评分维度包括段落数量、段落平均长度、文本中标点密度、链接密度正文中链接密度低、是否包含标题标签。选取得分最高的容器作为正文容器如果得分不足阈值则回退到取整篇文章的最大文本节点。这个逻辑和人类判断哪里是正文的方式非常接近一段长文本且配着标题、很少有外链的区块大概率就是正文。对于绝大多数技术博客这种方法的准确率在 90% 以上比我预想的要好。如果你遇到结构特别复杂的页面比如多栏目站点还可以再叠加一层正文评分阈值当最高分容器得分不足设定阈值时直接提示用户页面结构异常请手动指定正文容器选择器。我给工具预留了--selector参数用户可以手动传入 CSS 选择器来强制指定正文区域。这个功能我在处理某个老博客时真用上了实测能兜底很多奇怪结构。3.3 清理与结构化输出提取到正文容器之后还不能直接翻译因为里面还残留着很多div嵌套、span样式节点、空行和无意义的链接。我的清理流程大致是移除所有script、style、noscript节点移除图片节点但保留alt文本如果存在作为占位提示移除空段落和只有空格的节点将br标签替换为换行符将h2、h3、p、li等关键标签映射为 Markdown 结构这里有一个细节直接用html2text库转换虽然省事但它在处理代码块时经常丢缩进在处理pre标签时表现不稳定。所以我的做法是先把 HTML 树里的代码块节点单独摘出来用专门的转换函数处理再插回正文流。这样能保证 GitHub 上常见的那种带行号代码块被正确保留为 Markdown 代码块。最终正文提取模块的输出是一个包含title、url、author、publish_time、content_markdown、word_count六个字段的字典。这个字典结构贯穿后续所有流程翻译模块和落盘模块都只依赖它。4. 翻译模块的接入4.1 翻译引擎选型与成本估算翻译引擎的选择直接决定了工具好不好用。考虑到可访问性与成本问题我接的是兼容主流大模型接口协议的通用翻译服务这类服务通常有免费额度且文档全面、稳定性高。选择这类服务有几个理由接口协议通用换一家服务商时只需改 base_url 和模型名称不用重写逻辑。上下文理解强相比传统逐句翻译整段翻译的语序和术语一致性更好。输入价格便宜当前模型输入 token 的成本已经降至可忽略不计单篇一万字的文章翻译费用一般在几分钱级别。我在代码里预留了--engine参数默认使用通用兼容接口也保留切换到本地模型的可能。如果你对数据隐私有更高要求可以在此基础上接入本地推理服务只改一个 URL 配置项。4.2 长文本分批翻译的处理逻辑翻译阶段最核心的问题是模型有上下文窗口限制单次请求不能塞入整篇长文。所以需要把正文切成若干批次再分批翻译。我一开始的方案是按字符数硬切每 1800 个字符切一段。用了两天就发现问题英文长句在硬切时经常被拦腰截断翻译出来的句子语义支离破碎。后来改成按段落边界聚合把正文拆成段落列表。从第一段开始累计 token 数当累计值接近 1500 时把当前已累计的段落作为一批提交。批次之间留 50 个 token 的余量防止模型输出超长。这样处理的优势是每个批次的文本在语义上是完整的段落集合翻译质量比硬切好得多。如果你处理的是中文原文token 数可以适当放宽因为中文在 token 化后的比例和英文差异不小。还要考虑翻译进程的并发与速率限制。免费档的服务通常有 QPS 限制比如每秒只允许 3 到 5 个请求。我的处理是在批次之间加入可变延迟避免一次性把批次全部发出去导致限流。实测下来延迟从 0.3 秒到 1 秒随机取值既能保证速度也能稳定过限流。4.3 专业术语的稳定性处理翻译技术文章时最怕的是同一个术语在不同段落被翻成不同的词。比如 command 一会儿是命令一会儿是指令读者看得一头雾水。我的做法是引入一个术语表映射 翻译后替换的双保险机制在请求翻译之前先对原文做预处理把术语统一替换为占位符。例如把iFlow CLI替换成{{TERM_IFLOW_CLI}}把Command替换成{{TERM_COMMAND}}。翻译完成后再把占位符替换回术语原文确保这些词不被翻译引擎改动。术语表维护在一份独立的 JSON 文件中用户可以自行扩展。实际体验下来这个技巧非常实用。像 CLI、API、markdown 这类专业词汇混在中文译文里是正常现象刻意翻译反而会显得不自然。术语表机制还让我可以把品牌词固定为英文保证阅读一致性。为了防止重复处理同一篇文章浪费 token我还加了内容指纹缓存对原文取 xxhash 值如果之前翻译过相同内容直接读取缓存结果跳过翻译环节。这对重复执行命令、批量处理文章时节省成本帮助很大。5. 注册为iFlow Command配置与调试全记录5.1 Command配置结构解析iFlow CLI 的自定义 Command 注册方式非常直观。它通过一个.iflow/commands.json文件来声明命令的元信息每个命令指向一个可执行脚本。我的配置结构大致如下{ command: article2md, description: 下载网页文章并翻译为中文 Markdown 文件, parameters: [ { name: url, type: string, required: true, description: 文章页面地址 }, { name: lang, type: string, default: zh-CN, description: 目标语言默认简中 }, { name: output, type: string, default: ./output, description: 输出目录默认当前目录下 output 文件夹 }, { name: keep-original, type: boolean, default: false, description: 是否在 Markdown 中保留英文原文 } ], script: ./scripts/article2md.py, runner: python3, output_mode: file }这段配置里有三个设计点值得展开说一下。第一个是output_mode: file。iFlow CLI 支持两种输出模式一种是命令执行结果直接输出文本到终端另一种是生成文件后返回文件路径。由于这个工具的核心产物是 Markdown 文件我选了文件模式这样终端不会刷出几千行译文只返回一个保存路径体验更干净。第二个是runner字段。我指定为python3意味着 iFlow 会调用python3 scripts/article2md.py并把参数透传进去。如果你在 Windows 环境需要改成python或者其他对应的解释器路径。第三个是parameters的参数名设计。我把布尔参数命名为keep-original而不是original虽然命令行里多敲了几个字符但可读性更好用--keep-original时任何使用者都能立刻明白这个参数的作用。5.2 参数定义与交互体验设计参数定义之后还需要处理命令在终端中的交互体验。我在脚本里做了一层参数校验与提示逻辑URL 必须是以http://或https://开头的合法链接否则提示并退出。输出目录不存在时自动创建而不是报错。翻译引擎未配置时提示先去环境变量里设置 API Key并给出示例。如果用户没有传 URL直接进入交互模式脚本会通过input()提示用户输入网址。交互模式的加入是我后来才想到的。最开始这个工具只支持全参数调用有次我在终端里忘了带网址命令直接报错退出又要重新敲一遍完整命令。后来加了检测缺少 URL 时进入input()交互只问一个必填项其他的用默认值。虽然代码只多几行但日常使用顺手太多。另外我还定制了终端的进度输出每个阶段结束后向 stderr 输出一行带状态标记的日志例如[1/4] 页面下载完成、[2/4] 正文提取完成、[3/4] 翻译完成、[4/4] 文件已写入。这样用户能清楚看到命令卡在哪一步。实际调试时这四行日志帮我快速定位了绝大多数问题。5.3 调试过程的几个关键技巧在把脚本接入 iFlow CLI 的过程中我总结了几个调试技巧这些经验在跑任何自定义 Command 时都适用。技巧一先隔离脚本再接入 CLI。也就是说先把 Python 脚本单独放在终端里用参数跑通确认输出符合预期之后再改commands.json接入 iFlow。不要直接改完配置就去测试否则报错时根本不知道是配置格式问题还是脚本逻辑问题。技巧二用--help验证参数解析。参数多了以后我经常忘记某个参数到底是--output还是--output-dir。所以我为脚本实现了--help分支终端里输入article2md --help就能看到完整的参数说明不用反复翻源码。技巧三日志与数据分离。所有过程日志一律写入 stderr只有最终文件路径写入 stdout。这是 Unix 工具设计的经典原则接 iFlow 这类 CLI 框架时必须遵守。曾有次我把一段 DEBUG 日志打到了 stdout结果 iFlow 把整段日志当成了返回值终端显示差点崩掉。技巧四善用环境变量。API Key 这类敏感信息不要写进命令配置或脚本源码统一从环境变量读取。iFlow 本身也支持读取用户级环境变量所以我在脚本中通过os.getenv(TRANSLATE_API_KEY)读取密钥缺失时给出明确的提示文案。6. 实测效果与踩坑记录6.1 三个典型场景的实测对比我在本地对三个不同结构的网站做了完整测试结果如下表测试目标页面结构正文提取结果翻译质量耗时某技术博客单篇教程英文标准文章页内容居中侧边栏较少标题、段落、代码块全部正确提取术语一致性好句子通顺约 12 秒某新闻门户深度报道英文多栏布局含大量相关阅读推荐正文完整提取无侧边栏噪声段落结构保留完整引用句译得准确约 18 秒某个人博客随笔日文极简风格仅有正文和评论区正文提取正常评论区被完全过滤翻译基本流畅个别语气词有偏差约 9 秒从测试结果来看规则评分提取法在标准文章页和个人博客上的表现最稳定在多栏布局的门户网站上也能有效过滤侧边栏。翻译耗时和文章长度正相关主要瓶颈在分批请求的网络往返而不是模型处理本身。我还测试了一个极端情况某页面正文超过 15000 字。此时翻译阶段会拆成 10 批以上总耗时达到两三分钟。因为加了缓存机制第二次运行相同 URL 时直接秒出结果这一点非常爽。6.2 高频错误清单与解决方案运行一段时间后我把遇到的高频错误整理成了一份自查清单。这里挑几个典型的大家如果遇到类似报错可以对照排查。错误一[1/4] 页面下载失败: HTTP 403出现 403 基本就是被服务端拦截了。绝大多数时候是 UA 被识别我把请求头里的 UA 换成最新版 Chrome UA 字符串后解决。极个别站点还有更强力的防护这种只能通过--cookies参数手动注入登录态解决。错误二[2/4] 正文提取失败: No suitable content block这个报错表示页面没有找到符合评分阈值的正文容器。我第一次遇到是在一个 PDF 转 HTML 的页面上正文其实全是图片自然没有足够的文本段落。后来我补充了逻辑当正文文本长度少于 500 个字符时不强行翻译直接提示该页面可能以图片为主。错误三翻译返回内容包含大量英文原文这不是报错但经常误导人。原因是模型在处理超长段落时偶发漏译。我在后处理里加了一层漏译检测如果译文里连续出现超过 80 个英文字符的句子就把该段落标记为未翻译再单独送一次小规模补译请求。错误四[4/4] 文件写入失败: File name too long这个是我在 Windows 上测试时踩的坑。文章标题很容易超过 Windows 文件名的 255 字符限制。解决办法是把文件名做哈希截断标题前 50 个字符 8 位短哈希。这个改动不仅解决了上限问题还避免了不同文章同名导致的覆盖冲突。6.3 真实使用中的两个意外发现除了表格里测试的网站我在实际使用中还发现了一些意料之外的结果。第一个发现是这个工具对于没有正文但内容全部在图片里的网站几乎无能为力但这类网站并不多所以我没有引入 OCR 的打算。如果你有强需求可以在解析阶段接入一个基础的图片转文字模型但这会把工具的依赖库和耗时都拉高一个量级。第二个发现是翻译后的 Markdown 文件可以很好地兼容本地知识库软件。我用某笔记软件打开生成的.md文件正文排版、代码块、标题层级全部正常显示。这让工具的价值从阅读辅助直接扩展到资料沉淀日常积累外文资料方便了很多。7. 后续可以扩展的方向基础功能跑通之后我一直在考虑这个工具还能往哪些方向扩展。有几个方向已经验证过可行性可以供大家参考。第一是批量抓取与队列化。现在一次只能处理一个 URL但很多时候需求是一篇长文连载或者一个专题下的多篇文章。如果能把多个 URL 放进一个文件命令通过--batch-file参数循环处理再配合已有的缓存机制就能实现全站某个栏目的批量归档。第二是生成双语对照版本。目前生成的 Markdown 只有译文如果用户需要中英对照排版可以在落盘阶段把原文段落和译文段落交错输出。这个功能已经在规划中实现上只要把--keep-original参数从布尔值改成三种模式仅译文、原文在上、段落级交错。第三是定时拉取与更新监测。对于经常更新的文档类页面可以配合系统的计划任务定期检查页面指纹发现页面变化就重新抓取翻译。这本质上是一个轻量级的网页监控系统也是我最想做的下一步。第四个方向更有意思把工具作为其他语言模型的工具接口。因为 iFlow CLI 的命令本身就是可被框架调用的我可以让另一个智能体调用article2md命令来获取网页内容再基于内容做问答或摘要。这样这个工具就从单机脚本变成了信息管线的一环价值会更大。结语一点个人经验这个工具的完整实现并不复杂真正花时间的不是代码而是那些看似不需要处理、实际非处理不可的边界情况。比如编码识别、长文本切割、术语稳定、文件重名、限流退避这些细节单独拎出来都不起眼但合在一起才决定了工具能否在日常中长期使用。我在实际使用中最深的体会是工具类的项目最好优先解决自己真实遇到的痛点而不是一上来追求功能大而全。第一版我只做了下载正文并翻译用了一周之后才逐步加了缓存、批处理和术语表。每加一个功能都是因为某个真实操作刺痛了我而不是为了炫技。如果你也想基于 iFlow CLI 做自己的命令建议从一个尽量小、尽量单功能的需求开始跑通之后再慢慢加东西。自定义 Command 的养成本质上是一个不断收敛自己需求的过程。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询