ARTICLE DETAIL

资讯详情

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

Keenable网页搜索API与Time Machine接入实战:从概念到工程实践

Keenable网页搜索API与Time Machine接入实战:从概念到工程实践 做 AI 应用的同学应该都有这种体会模型本身再强不联网的时候就像一台断网的电脑很多问题只能靠训练数据里的旧知识回答。尤其是做 Agent、RAG、舆情监控、竞品分析这类项目时实时拿到网页内容几乎是刚需。最近 Keenable 推出了独立的网页搜索 API还带了一个叫 Time Machine 的能力接口设计和过去的搜索 API 不太一样。这篇就把从账号准备、接口调用、参数解释到常见报错排查的完整过程整理出来给正在做搜索接入的同学做个参考。1. Keenable 网页搜索 API 与 Time Machine 到底是什么1.1 网页搜索 API 解决了什么问题网页搜索 API 本质上是把“搜索网页”这件事从浏览器里拆出来变成一个可供程序调用的接口。过去我们在代码里拿到网页内容通常有两种方式自己去爬搜索引擎结果页再解析 HTML。直接对接某些大厂搜索产品的非正式接口。这两种方式的问题都很明显。爬页面要处理反爬、验证码、页面结构变化而且搜索引擎的页面结构经常改今天能解析的字段明天可能就失效了。非正式接口更不稳定随时可能因为调用频率过高被封。独立网页搜索 API 的价值就在于把“关键字 - 网页搜索结果列表”这个过程标准化的封装好程序只需要传参数、收结果不需要关心搜索服务端是怎么实现的。这种能力最常见的落地场景包括AI Agent 在回答问题时实时检索最新信息减少模型幻觉。RAG 应用的知识库定期抓取指定主题的新内容。舆情监控系统定时搜索品牌词、竞品词。学术或投资研究中的信息采集。自动化测试中的页面关键词校验。1.2 Time Machine 是什么概念Time Machine 是这次推出的比较有意思的功能。从命名和定位来看它解决的是“网页历史状态检索”的问题。普通搜索 API 拿到的是当前时间点的搜索结果而 Time Machine 允许调用方指定一个历史时间点去查询“在那个时间点这个关键词能搜到什么”。这对需要追溯信息发布时间的场景很有用比如还原某个事件在特定日期的舆论热度。查看某个产品页面在改版前的内容。对比竞品在不同月份的宣传口径。审计某条信息最早出现的时间和渠道。需要提醒的是历史网页数据的覆盖范围、最早可回溯时间、快照更新频率不同产品的差异很大。在正式使用前建议先阅读官方文档确认时间范围和数据覆盖说明不要默认所有域名和所有时间点都有快照。1.3 为什么需要独立的搜索 API 而不是大模型内置搜索很多人会问现在很多大模型平台已经提供了带联网搜索的接口为什么还要单独对接一个网页搜索 API原因主要有几点。第一解耦。把搜索能力从模型调用中拆出来意味着可以自由组合不同的模型。今天用 A 模型明天换成 B 模型搜索逻辑不用改。这在大模型 API 更新频繁的当下非常实用。第二可控。独立 API 的请求参数、返回结构、缓存策略都由自己掌控更容易嵌入现有系统。第三可观测。搜索是搜索模型是模型分开后每一次搜索请求的耗时、成功率、结果质量都能单独统计方便排查问题。第四成本核算更清晰。搜索调用量和模型 Token 消耗量分开计量预算管理更直观。2. 环境准备与版本说明2.1 开发环境本文的示例使用 Python 编写运行环境如下。版本可以根据你的实际情况调整重点看调用思路。操作系统Windows 10 / macOS / Linux 均可。Python3.8 及以上示例代码使用了requests库和标准库中的json、time。网络环境能正常访问 Keenable API 服务域名。IDEVisual Studio Code 或任何你习惯的编辑器。如果你的项目是 Java、Go、Node.js也不必担心HTTP 接口的调用方式是一致的只是发送 HTTP 请求的库不同。2.2 注册账号与获取 API Key调用任何付费 API 之前第一步都是注册账号并获取密钥。常规流程如下打开 Keenable 官方网站注册账号。进入控制台或 API 管理页面。创建一个应用或项目系统会生成对应的 API Key。根据需要开通网页搜索 API 的套餐确认是否包含 Time Machine 能力。这里强调一个安全原则API Key 等同于账号的访问凭证绝对不要写死在代码里更不要提交到 Git 仓库。推荐通过环境变量或配置文件管理。# 在终端中设置环境变量示例为 macOS / Linux 写法 export KEENABLE_API_KEYyour_api_key_here2.3 接口地址与通用约定由于产品接口细节可能会调整本文不写死真实的生产地址而是用示例域名代替。真实地址请以官方文档为准。一个典型的 REST 风格搜索 API 会遵循以下约定请求方法POST或GET。如果查询参数复杂、包含较多筛选条件通常用POST。请求头包含Authorization: Bearer API_KEY和Content-Type: application/json。响应格式JSON。字符编码UTF-8。下面是一个通用的调用约定示例约定项说明请求方式POST认证方式Bearer Token请求体格式application/json响应体格式application/json超时设置建议 10 到 30 秒3. 核心概念与参数拆解在写完整代码之前先拆解一下最核心的几个概念。3.1 认证方式绝大多数 API 使用 API Key 作为身份凭证。常见的有两种传递方式请求头方式Authorization: Bearer API_KEY。查询参数方式?api_keyAPI_KEY。推荐使用请求头方式因为查询参数可能被服务端日志、代理日志记录存在泄露风险。3.2 搜索请求参数搜索 API 最核心的参数通常包括参数名是否必填说明query是搜索关键词language否搜索结果语言例如zh-CN、en-USregion否搜索地区影响结果的地域偏向market否市场或站点范围部分产品会使用page_size否每页返回结果数量常见范围是 1 到 20page否页码用于翻页sort否排序方式例如relevance相关度、date时间time_range否结果的时间过滤例如过去一天、过去一周freshness否部分产品用该参数控制结果的新鲜度需要注意不同产品的参数命名差异很大。比如有些产品用q而不是query有些产品用count而不是page_size。接入前一定要先看官方接口文档的 Request Parameters 表格确认参数名和取值枚举。3.3 Time Machine 时间参数Time Machine 通常需要额外指定一个时间参数。常见的设计方式有两种使用timestamp传入 Unix 时间戳。使用date传入 ISO 8601 格式的日期时间例如2024-06-01T00:00:00Z。从工程角度ISO 8601 的可读性更好也方便在日志中直接查看。但如果你需要在代码中频繁比较时间Unix 时间戳更高效。一个假设的请求体设计如下{ query: Keenable API, language: zh-CN, page_size: 10, timestamp: 1717200000 }这里的timestamp: 1717200000对应的是 2024 年 6 月 1 日 00:00:00 UTC。具体含义是查询这个时间点的搜索结果快照。3.4 响应结构搜索 API 的响应通常会包含以下几个部分results搜索结果列表每一项包含标题、链接、摘要、发布时间等字段。total结果总数用于分页。query_id本次请求的唯一标识方便排查问题。cached本次响应是否命中了服务端缓存。一个假设的响应示例{ query_id: a1b2c3d4-1234-5678-9abc-def012345678, total: 128, results: [ { title: Keenable 推出独立网页搜索 API, url: https://example.com/news/keenable-api, snippet: Keenable 发布了独立网页搜索 API并附带 Time Machine 能力……, published_at: 2025-01-10T08:30:00Z, source: example.com } ], cached: false }响应里的字段可能比这更多也可能字段名不同核心思路是根据官方文档把字段名映射到你自己的数据结构中避免在后端硬编码字段名。4. 完整实战用 Python 调用 Keenable 搜索 API接下来用一个完整示例从零开始写一个可运行的 Python 搜索客户端。4.1 创建项目结构先创建目录结构keenable-search-demo/ ├── main.py ├── search_client.py ├── requirements.txt └── .env.example4.2 安装依赖requirements.txt内容如下requests2.31.0 python-dotenv1.0.0然后执行pip install -r requirements.txtpython-dotenv用于读取.env文件中的环境变量方便本地调试。4.3 编写核心搜索客户端文件路径search_client.pyimport os import time import requests class KeenableSearchClient: def __init__(self, api_key: str, base_url: str https://api.example.com/v1): self.api_key api_key self.base_url base_url self.session requests.Session() self.session.headers.update({ Authorization: fBearer {api_key}, Content-Type: application/json, }) def search(self, query: str, page_size: int 10, **kwargs): 基础搜索接口。 :param query: 搜索关键词 :param page_size: 每页结果数 :param kwargs: 其他参数如 language、region、timestamp 等 :return: 解析后的 JSON 响应 payload { query: query, page_size: page_size, } payload.update(kwargs) response self.session.post( f{self.base_url}/search, jsonpayload, timeout20, ) response.raise_for_status() return response.json() def search_history(self, query: str, timestamp: int, **kwargs): Time Machine 历史搜索接口。 :param query: 搜索关键词 :param timestamp: Unix 时间戳表示要查询的历史时间点 payload { query: query, timestamp: timestamp, } payload.update(kwargs) response self.session.post( f{self.base_url}/search/history, jsonpayload, timeout30, ) response.raise_for_status() return response.json()这个类做了三件事初始化时把API Key写入 Session 的请求头后续所有请求自动携带认证信息。search方法封装基础搜索支持通过**kwargs扩展参数。search_history方法封装 Time Machine 调用把timestamp作为必填参数。4.4 编写入口程序文件路径main.pyimport os import time from dotenv import load_dotenv from search_client import KeenableSearchClient def format_results(data: dict) - None: 格式化打印搜索结果 print(fquery_id: {data.get(query_id)}) print(ftotal: {data.get(total)}) for idx, item in enumerate(data.get(results, []), start1): print(f\n[{idx}] {item.get(title)}) print(f URL: {item.get(url)}) print(f 摘要: {item.get(snippet)}) print(f 发布时间: {item.get(published_at)}) def main(): load_dotenv() api_key os.getenv(KEENABLE_API_KEY) if not api_key: raise ValueError(请先设置 KEENABLE_API_KEY 环境变量) client KeenableSearchClient(api_keyapi_key) # 1. 基础搜索 print( * 60) print(基础搜索Keenable API) print( * 60) result client.search(Keenable API, page_size5, languagezh-CN) format_results(result) # 2. Time Machine 历史搜索 print(\n * 60) print(Time Machine 搜索查询 2024 年 6 月 1 日的结果) print( * 60) # 2024-06-01 00:00:00 UTC 对应的 Unix 时间戳 past_timestamp int(time.mktime(time.strptime(2024-06-01, %Y-%m-%d))) history_result client.search_history(Keenable API, timestamppast_timestamp) format_results(history_result) if __name__ __main__: main()4.5 运行与验证先准备环境变量文件.envKEENABLE_API_KEYyour_api_key_here然后运行python main.py预期会看到两个部分第一部分输出当前时间的搜索结果第二部分输出指定历史时间点的搜索结果。如果你的 API Key 没有开通 Time Machine 权限第二个请求可能会返回权限错误这时候需要到控制台确认套餐是否包含该能力。如果返回结果为空不要急着判断 API 有问题先确认关键词、时间范围和地区参数是否合理。比如zh-CN语言环境下搜索一个英文新品名结果可能本身就很少。5. 进阶在 AI Agent 中接入搜索能力搜索 API 单独用价值有限真正能放大效果的是把它接入 AI Agent 或 RAG 工作流。5.1 把搜索结果格式化为模型上下文大模型需要的是结构化、简洁的文本上下文。直接丢原始 JSON 给模型既浪费 Token又可能让模型被无关字段干扰。建议把搜索结果格式化为 Markdown 或纯文本列表。核心片段如下def build_context(results: list) - str: 把搜索结果列表转换为模型友好的上下文文本 lines [] for idx, item in enumerate(results, start1): title item.get(title, ) url item.get(url, ) snippet item.get(snippet, ) published item.get(published_at, ) lines.append( f{idx}. [{title}]({url})\n f 发布时间: {published}\n f 摘要: {snippet} ) return \n\n.join(lines)这样转换之后可以拼进 Promptprompt f请根据以下搜索结果回答用户问题。 搜索结果 {build_context(result.get(results, []))} 用户问题Keenable 的 Time Machine 是什么 请用中文回答并注明信息来源。 5.2 结果缓存设计网页搜索 API 通常按调用次数计费。同样一个关键词在短时间内反复查询非常浪费。建议在应用层加缓存。import time class SearchCache: def __init__(self, ttl: int 300): self.cache {} self.ttl ttl def get(self, key: str): item self.cache.get(key) if item and time.time() - item[time] self.ttl: return item[data] return None def set(self, key: str, data): self.cache[key] {data: data, time: time.time()}使用时在调用client.search之前先查缓存命中则直接返回。注意缓存时间不要设置太长否则搜索结果的时效性会下降。5.3 批量查询的注意事项批量查询多个关键词时要注意控制并发。大多数搜索 API 都有 QPS每秒请求数限制。你可以用一个简单的信号量控制并发from concurrent.futures import ThreadPoolExecutor, as_completed import threading semaphore threading.Semaphore(2) # 同一时间最多 2 个请求 def safe_search(client, keyword): with semaphore: return client.search(keyword, page_size5) keywords [AI Agent, 大模型, RAG, 搜索 API] with ThreadPoolExecutor(max_workers4) as executor: futures [executor.submit(safe_search, client, kw) for kw in keywords] for future in as_completed(futures): data future.result() print(data.get(total))这里的核心思路是用Semaphore控制实际进入 API 的并发数而不是盲目相信线程池的max_workers。6. 常见报错与排查思路接入任何 API都绕不开报错排查。下面把最常遇到的错误整理成表格方便对照处理。问题现象常见原因解决思路HTTP 401 UnauthorizedAPI Key 缺失、错误或已失效检查环境变量中的 Key确认没有多余空格到控制台重新生成 KeyHTTP 402 Payment Required账户余额不足或套餐配额耗尽检查账户余额确认计费套餐及时充值或升级HTTP 403 Forbidden接口权限不足IP 白名单拦截确认是否开通了 Time Machine 权限核实白名单配置HTTP 404 Not Found接口路径写错对照官方文档确认 URL尤其是版本号/v1部分HTTP 429 Too Many Requests请求频率超过限制降低并发增加退避重试或申请提高 QPS 配额HTTP 5xx服务端内部错误先等待几秒重试持续出现则联系技术支持并提供query_idHTTP 529 Overloaded服务端负载过高属于临时性故障不要立刻高频重试使用指数退避策略间隔 1s、2s、4s 逐步重试连接超时 / read timeout网络波动或响应时间过长增大超时时间检查网络代理设置确认域名可以访问响应中total为 0关键词过于冷门时间范围过窄地区参数不当扩大时间范围调整 language/region换更通用的关键词6.1 排查清单遇到报错时建议按下面的顺序排查不要一上来就改代码。确认 API Key 是否正确是否包含多余空格。确认接口地址和环境测试环境 / 生产环境是否正确。确认请求参数名和取值是否符合文档要求。确认账户余额和套餐配额是否充足。查看服务端返回的错误信息而不是只看状态码。如果错误信息包含request_id或query_id保留并提交给技术支持。6.2 重试策略对于 429、529、5xx 这类临时性错误合理的重试可以显著提高成功率。一个简单的指数退避实现如下import time import requests def request_with_retry(func, max_retries: int 3): for attempt in range(max_retries): try: return func() except requests.exceptions.HTTPError as e: status_code e.response.status_code if status_code in (429, 500, 502, 503, 529) and attempt max_retries - 1: wait_time 2 ** attempt # 1s, 2s, 4s print(f请求失败状态码 {status_code}{wait_time} 秒后重试...) time.sleep(wait_time) continue raise重试不是万能的。如果是 401、403 这类认证或权限错误重试多少次都没有意义必须先去解决 Key 和权限问题。7. 最佳实践与工程建议7.1 异常处理分层不要把requests的异常直接抛到业务层。建议在客户端的search方法内部先做一层封装把网络异常、HTTP 错误、JSON 解析错误分别处理class APIError(Exception): def __init__(self, status_code, message, query_idNone): self.status_code status_code self.message message self.query_id query_id super().__init__(f[{status_code}] {message})然后在调用处统一捕获APIError转换成业务层的可读提示。7.2 安全与凭证管理API Key 的安全是底线。几条具体的建议生产环境使用密钥管理服务保存 API Key而不是写进配置文件。日志中禁止打印完整的 API Key 和请求头。如果 API 支持子 Key 或多 Key尽量按用途拆分一个项目用一个 Key方便单独回收。在服务端配置 IP 白名单限制 API Key 的使用来源。7.3 日志与可观测性每次搜索请求都建议记录以下信息请求关键词。是否命中缓存。请求耗时。返回状态码。结果数量。query_id。有了这些日志才能回答“为什么这个关键词结果变少了”“为什么今天调用量突然涨了”这类问题。7.4 成本控制搜索 API 是按调用量计费的控制成本可以从几个方向入手用缓存拦截重复关键词。设置单用户或单任务的调用上限。对批量任务做优先级队列避免高峰时段集中请求。定时统计各关键词的调用分布停掉无效的采集任务。7.5 Time Machine 的使用建议Time Machine 属于比较重的能力调用前先想清楚是否真的需要历史数据。需要注意历史数据覆盖范围可能有限冷门关键词不一定有历史快照。不建议把 Time Machine 作为实时搜索的默认方案实时搜索走普通搜索接口即可。历史查询结果建议做好持久化因为同一时间点的历史快照未必每次返回都一致。涉及法律合规、审计、取证等场景时务必确认数据来源的合法性和授权边界。8. 总结与后续学习建议这篇围绕 Keenable 独立网页搜索 API 和 Time Machine从概念、环境准备、参数拆解、Python 实战接入、AI Agent 场景集成到报错排查和工程建议整理了一条相对完整的路径。核心收获可以归纳为几点搜索 API 的本质是标准化封装调用之前先确认参数命名和权限Time Machine 是面向历史数据检索的能力适合有回溯需求的场景但要注意数据覆盖范围接入过程要把错误处理、重试、缓存、日志当成一等公民来设计而不是等上线后再补。如果接下来要继续深入可以往这几个方向走一是把搜索能力接入 LangChain、LlamaIndex 这类 Agent 框架理解 Tool 调用的封装方式二是研究搜索结果的去重与排序策略提高喂给大模型的上下文质量三是做一套完整的调用统计和告警系统让 API 调用变得可观测、可治理。动手实践是最好的学习方法先申请一个 Key跑通基础搜索再试着加上 Time Machine 参数感受一下历史检索和实时检索的区别。遇到报错也别慌按第六节的排查清单逐项确认大部分问题都能定位到原因。
返回列表