基于Jina Reader与Exa API的免费网页抓取与搜索工具实践
1. 项目概述一个轻量级的网络信息抓取与处理工具最近在折腾一些自动化信息处理的项目发现很多时候需要从网上快速抓取内容或者进行关键词搜索然后对结果进行结构化处理。市面上的工具要么太重要么收费要么就是API调用起来特别麻烦。正好看到社区里有人分享了一个叫roryyu/websearch-free-skill的项目它基于 Jina Reader 和 Exa 这两个免费API做了一个非常轻巧的脚本工具能实现网页内容提取和关键词搜索。我花时间研究并实践了一下发现它确实解决了“快速、免费、结构化获取网络信息”这个痛点特别适合开发者、内容创作者或者需要做信息聚合分析的朋友。这个工具的核心就两个脚本一个负责从指定URL里“读”出干净的正文内容另一个负责用关键词去“搜”相关的网页结果。它最大的亮点是“免费”和“轻量”不需要你去各个平台申请复杂的API Key开箱即用。下面我就结合自己的使用和改造经验把这个工具的里里外外、怎么用、可能会遇到什么坑以及如何让它更贴合你的实际需求详细拆解一遍。2. 核心功能与设计思路解析2.1 为什么选择 Jina Reader Exa 这个组合这个项目的设计思路很清晰利用现成的、有免费额度的优质API组合成一个实用的工具。我们来看看这两个核心组件。Jina Reader API是一个内容提取服务。你给它一个网页URL它返回经过清洗的、可读的正文内容格式是Markdown。这解决了我们手动抓取网页时最头疼的问题剔除广告、导航栏、侧边栏、评论等噪音只保留核心文章。它的免费额度对于个人和小规模使用来说基本够用响应速度也很快。Exa Search API通过mcporter框架集成是一个搜索引擎API。它不同于直接调用谷歌或必应的原生搜索那需要处理反爬和复杂解析Exa 返回的结果本身就是结构化的JSON数据包含了标题、链接、摘要等信息非常便于程序处理。虽然它的免费套餐有调用次数限制但对于非高频的搜索需求来说是一个非常好的起点。这个组合的巧妙之处在于分工明确exasearch.js负责“发现”找到相关的网页链接search.js负责“消化”提取链接中的具体内容。两者结合就能完成从关键词到具体内容的一条龙处理。2.2 工具的核心能力与适用场景基于上述设计这个工具主要提供了两大核心能力精准内容提取给定一个URL直接获取其主体内容无需关心网页复杂的DOM结构。这对于做内容摘要、存档、翻译或进一步分析非常有用。结构化网络搜索给定一个关键词获取一批相关的、包含丰富元数据标题、链接、摘要的搜索结果。这对于市场调研、竞品分析、知识收集或构建简单的搜索服务原型很有帮助。它非常适合以下场景个人知识管理快速抓取博客文章、技术文档到你的笔记软件中。项目调研自动化搜索并收集某个技术话题的最新资料。内容监控定期抓取特定网站或基于关键词搜索的新内容。原型开发为你的应用快速集成一个搜索或内容预览功能验证想法。注意务必遵守目标网站的robots.txt规则并尊重版权。此工具应用于个人学习、合法合规的数据分析或获取公开信息。避免对单一网站进行高频、密集的请求以免给对方服务器造成压力。3. 环境准备与项目初始化详解3.1 系统与运行环境要求项目基于 Node.js所以第一步是确保你的开发环境就绪。Node.js要求版本 v22 或更高。这是必须的因为项目可能使用了较新的JavaScript特性或Node API。你可以通过终端命令node -v来检查当前版本。如果版本过低或未安装建议去 Node.js 官网下载安装最新的LTS长期支持版本。包管理器npm或yarn均可。npm会随 Node.js 一起安装可以通过npm -v检查。我个人的习惯是使用nvmNode Version Manager来管理多个Node.js版本这样可以轻松在不同项目间切换。如果你也从事多个Node项目开发nvm是个不错的选择。3.2 获取与安装项目依赖假设你已经将项目代码克隆到本地例如通过git clone接下来的步骤就非常简单了。进入项目目录打开终端使用cd命令切换到包含package.json文件的目录。cd path/to/websearch-free-skill安装依赖运行安装命令。这个过程会读取package.json中的dependencies字段下载axios和mcporter等必要的库。npm install如果使用yarn则运行yarn install安装完成后你会看到一个node_modules文件夹里面包含了所有依赖库。项目根目录下的package.json文件定义了项目的元数据和依赖我们可以简单看一下它的结构可能因版本略有不同{ “name”: “websearch-free-skill”, “version”: “1.0.0”, “description”: “A lightweight web search and content extraction tool”, “main”: “index.js”, “scripts”: { … }, “dependencies”: { “axios”: “^1.6.0”, “mcporter”: “^0.1.0” }, “keywords”: [“websearch”, “jina”, “exa”], “author”: “…”, “license”: “MIT” }这里的关键是dependencies部分它锁定了项目运行所必须的外部包。axios是一个强大的HTTP客户端用于向 Jina Reader API 发送请求mcporter则是一个工具集成框架这里主要用于封装对 Exa API 的调用。4. 核心脚本使用指南与实战项目提供了两个核心脚本search.js和exasearch.js。我们分别深入看看它们怎么用以及背后发生了什么。4.1 网页内容提取search.js 深度剖析search.js脚本是你的“网页阅读器”。它的任务很单纯输入一个URL输出这个网页的精华内容。基本命令格式node search.js url [previewLength]url必需参数。就是你想抓取内容的网页地址记得要包含http://或https://协议头。[previewLength]可选参数。指定你想在控制台预览的内容字符数。默认是200设置为0则只显示元数据不预览内容。实战示例与输出解读让我们抓取一个经典的示例网站看看效果node search.js https://www.example.com 300执行后你可能会看到类似下面的输出Reading: https://www.example.com Read Result Title: Example Domain URL: https://www.example.com Platform: web Content length: 1256 characters First 300 characters of content: # Example Domain This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission. [More content…]输出结果解析状态行Reading: https://www.example.com表示脚本开始处理该URL。元数据区块( Read Result )Title网页的标题通常来自HTML的title标签。URL你请求的原始URL。Platform来源平台这里固定为web。Content length提取出的纯文本内容的总字符数。这是衡量内容体量的一个关键指标。内容预览显示了你通过previewLength参数指定长度的内容开头部分。格式是Markdown非常清晰。这个过程中脚本做了什么参数校验脚本首先会检查是否提供了URL参数以及previewLength是否为有效的非负整数。构造请求使用axios库向https://r.jina.ai/your-url发送一个HTTP GET请求。Jina Reader 的服务端会负责抓取并解析目标网页。处理响应等待Jina Reader返回处理好的Markdown格式内容。解析与展示脚本定义了一个ReadResult类来结构化地存储返回的数据标题、URL、内容等然后按照预定格式漂亮地打印到控制台。错误处理如果网络超时默认15秒、链接失效4xx/5xx状态码、或者服务器无响应脚本会捕获异常并打印出用户友好的错误信息而不是让整个程序崩溃。实操心得previewLength参数非常实用。当你在调试或者只需要确认网页内容是否符合预期时设置为一个较小的值如50可以快速得到反馈。当需要获取全文进行后续处理时可以结合重定向功能将完整输出保存到文件node search.js https://some-long-article.com 0 article.txt这样article.txt里就会保存完整的元数据和内容。4.2 关键词网络搜索exasearch.js 深度剖析如果说search.js是“点”的抓取那么exasearch.js就是“面”的探索。它帮你从海量信息中找到与关键词相关的线索。基本命令格式node exasearch.js keyword [numResults]keyword必需参数。你的搜索查询词可以是一个单词或一个短语。如果短语包含空格记得用引号包起来。[numResults]可选参数。希望返回的搜索结果数量。默认是10最大支持100。Exa API 对免费调用可能有数量限制请合理设置。实战示例与输出解读搜索关于“机器学习”的最新信息node exasearch.js “machine learning tutorials” 5执行后你会得到一个结构化的JSON数组输出每个结果大概长这样[ { “title”: “A Beginner’s Guide to Machine Learning – freeCodeCamp.org”, “url”: “https://www.freecodecamp.org/news/a-beginners-guide-to-machine-learning/”, “publishedDate”: “2023-10-15”, “author”: “Jane Doe”, “score”: 0.87 }, { “title”: “Machine Learning | Google for Developers”, “url”: “https://developers.google.com/machine-learning”, “publishedDate”: “2024-01-10”, “author”: null, “score”: 0.82 } // … 更多结果 ]搜索结果字段解析title搜索结果的网页标题。url该结果对应的网页链接这是后续用search.js进行深度抓取的“入口”。publishedDate网页的发布日期如果可检测到。对于追踪最新信息非常有用。author文章作者如果可检测到。score相关性评分通常介于0到1之间分数越高代表与搜索关键词的相关性越强。脚本工作流程参数校验检查关键词是否存在以及结果数量是否为1到100之间的整数。调用搜索API通过mcporter框架封装的接口向 Exa 搜索引擎发送搜索请求并传递关键词和结果数量参数。处理与格式化接收Exa返回的原始JSON数据提取出我们关心的核心字段并以整齐的JSON格式输出到控制台方便其他程序如jq工具或Node脚本进行管道处理。两个脚本的协同工作流 这才是威力所在。你可以用exasearch.js找到一批相关链接然后用search.js批量抓取它们的内容。例如一个简单的组合思路先用exasearch.js搜索并保存结果到文件node exasearch.js “rust programming” 10 results.json。写一个简单的Shell脚本或Node脚本读取results.json遍历其中的每个url并调用node search.js ${url} 0来获取每个页面的完整内容。5. 项目结构深入与扩展改造5.1 源码文件职责分析了解每个文件的作用有助于你调试和定制这个工具。websearch-free-skill/ ├── search.js # 核心URL内容提取。包含参数解析、请求发送、结果展示和错误处理。 ├── exasearch.js # 核心关键词搜索。包含搜索逻辑、结果格式化。 ├── ReadResult.js # 数据模型定义ReadResult类用于结构化存储和表示从Jina API返回的数据。 ├── package.json # 项目配置定义名称、版本、依赖项axios, mcporter和脚本。 ├── SKILL.md # 技能文档可能用于集成到某些AI Agent平台如OpenClaw的说明。 └── README.md # 项目总览就是你刚才看到的使用说明。ReadResult.js是一个简单的类它体现了良好的设计——将数据标题、URL、内容和可能的行为如格式化输出封装在一起。虽然当前功能简单但这种结构为未来扩展比如添加内容摘要方法、导出不同格式的方法打下了基础。SKILL.md文件暗示了这个项目可能被设计为一个“技能”Skill可以嵌入到更大的自动化工作流或AI助手中。这打开了思路你可以思考如何将这两个核心功能封装成API服务或者作为其他自动化工具的一个模块。5.2 如何扩展与定制这个工具原项目提供了可靠的基础功能但在实际生产中你可能需要一些增强。这里分享几个改造方向1. 增加代理支持如果你所处的网络环境访问 Jina Reader 或 Exa 不稳定可能需要配置代理。这可以在axios的请求配置中实现。修改search.js在axios.get调用前添加代理配置。// 示例添加HTTP代理需安装axios它原生支持proxy配置 const axios require(‘axios’); const httpsProxyAgent require(‘https-proxy-agent’); const proxyUrl ‘http://your-proxy-host:port’; // 替换为你的代理地址 const agent new httpsProxyAgent(proxyUrl); const response await axios.get(apiUrl, { timeout: 15000, httpsAgent: agent, // 应用代理 // … 其他配置 });注意修改网络配置涉及复杂环境请确保你拥有合法的代理使用权并遵守相关法律法规和公司政策。此示例仅为技术可能性探讨。2. 添加结果持久化功能目前结果只打印到控制台。我们可以很容易地添加文件保存功能。修改search.js在打印结果后添加文件写入逻辑。const fs require(‘fs’).promises; // … 在获取到 readResult 之后 … console.log(readResult.toString(previewLength)); // 追加写入文件 const logData \n [${new Date().toISOString()}] \n${readResult.toString(0)}\n; await fs.appendFile(‘./crawled_logs.txt’, logData); console.log(‘结果已追加保存到 crawled_logs.txt’);3. 实现批量URL抓取结合exasearch.js的输出我们可以创建一个新的脚本batch_search.js。思路读取一个包含URL列表的文本文件或exasearch.js生成的JSON文件循环调用search.js的核心逻辑即发起Jina API请求的部分并汇总或分别保存结果。关键点在批量处理时务必在请求间添加延迟例如使用setTimeout或async/await配合延迟函数避免对Jina服务器造成瞬时压力这既是道德考量也能防止IP被临时限制。4. 增强错误处理与重试机制网络请求天生不稳定。可以增加重试逻辑当遇到网络超时或5xx服务器错误时自动重试几次。实现方式将axios.get调用包装在一个带有重试计数器的函数中使用try…catch在捕获到特定类型的错误如ECONNRESET,ETIMEDOUT且重试次数未满时等待片刻后再次尝试。6. 常见问题、错误排查与优化建议在实际使用中你可能会遇到一些问题。下面是我踩过的一些坑以及解决办法。6.1 网络请求相关错误错误现象可能原因排查与解决思路Error: connect ETIMEDOUT或Error: Network Error网络连接超时或不通。1. 检查本地网络是否正常。2. 尝试用浏览器直接访问https://r.jina.ai/https://www.example.com看Jina服务是否可用。3. 考虑是否因网络环境需要配置代理参见扩展部分。Request failed with status code 404目标网页不存在或Jina Reader无法访问该URL。1. 手动在浏览器中打开该URL确认链接有效且未被屏蔽。2. 有些网站可能有反爬机制阻止了Jina的抓取。Request failed with status code 5xxJina Reader 或 Exa 服务器端出现临时故障。1. 这是服务器问题通常等待一段时间后重试即可。2. 可以检查Jina或Exa的官方状态页面如果有的话。3. 实现简单的重试机制。脚本执行无反应长时间卡住默认15秒超时可能不够或脚本进入死循环。1. 对于内容特别多或加载慢的网页可以适当增加axios请求的timeout配置例如设为30000毫秒。2. 检查脚本逻辑确保在错误分支有正确的退出。6.2 参数与使用错误错误现象可能原因排查与解决思路Missing required parameter: url运行node search.js时没有传入URL参数。确保命令格式正确node search.js url。URL参数是必需的。Invalid previewLength. Must be a non-negative integer.previewLength参数传入了非数字、负数或小数。确保第二个参数是像0,200,500这样的整数。numResults must be between 1 and 100exasearch.js的numResults参数超出了1-100的范围。调整结果数量到有效范围内。免费API通常有单次查询上限。搜索结果显示[]或结果很少关键词太宽泛或太生僻Exa未找到相关结果或免费API额度用尽。1. 尝试更具体或更常见的关键词组合。2. 检查Exa搜索API的当前使用状态查看其官方文档或仪表板。6.3 性能与稳定性优化建议控制请求频率无论是抓取还是搜索都不要编写循环进行极高频率的调用。尊重服务提供商的免费额度在批量操作中主动添加延迟例如每次请求间隔2-5秒。处理内容编码极少数情况下某些网页的特殊字符可能在控制台显示乱码。这通常是因为终端编码问题。可以尝试将输出重定向到文件然后用支持UTF-8编码的文本编辑器查看。验证URL格式在将URL传递给脚本前可以增加一个简单的格式校验确保它以http://或https://开头避免因格式错误导致不必要的请求失败。使用环境变量管理配置如果你进行了扩展如添加代理、自定义超时时间建议将这些配置项如代理地址、超时时长放在环境变量中而不是硬编码在脚本里这样更安全、更灵活。这个websearch-free-skill项目作为一个起点已经很好地展示了如何利用现有API快速构建实用工具。它的价值在于其轻量和直接的思路。你可以根据上述的解析、示例和扩展建议把它改造成更贴合自己工作流的利器。记住工具是死的人是活的理解其原理后灵活运用和改造才是关键。