尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

最小可运行示例:一个 GET 请求查询 ICP 备案信息

最小可运行示例:一个 GET 请求查询 ICP 备案信息 适用场景ICP 备案查询是一个非常高频的开发诉求。常见的落地场景包括域名准入检查在内容发布、广告投放或用户提交外链之前先判断目标域名是否完成备案从源头规避因未备案域名导致的业务风险。运营数据清洗批量筛选已备案域名用于活动报名或开发者认证避免人工逐一核对。企业内部系统集成在 CMS 或工单系统中增加备案信息自动回填减少运营人员在工信部站点手动检索的时间。安全巡检与资产管理定期扫描公司域名列表中备案主体的变更及时发现备案被注销或主体不一致的问题。以上场景都有一个共同点调用方只关心「这个域名有没有备案」「备案主体是谁」并不需要理解工信部备案系统内部复杂的查询逻辑。这正好是 ICP 备案查询 API 的设计边界所在。接口能力边界在写第一行代码之前先明确接口能做什么、不能做什么能避免很多认知偏差。接口本质上是「域名 → 备案信息」的映射查询它具备三个值得注意的能力自动域名清洗接口接收的不一定是纯域名。传入https://www.baidu.com/abc、m.baidu.com:8080/foo或baidu.com接口都会自动剥离协议、路径、端口和www.前缀统一识别为baidu.com。这省去了调用方自行做 URL 解析的代码。已备案与未备案的语义区分已备案域名返回is_filedtrue以及完整的 6 个字段未备案、境外域名或备案已注销的域名返回is_filedfalse且字段为空。注意未备案不是错误而是正常的业务响应因此不需要用 try/catch 包裹业务判断。缓存策略已备案数据缓存 24 小时未备案数据缓存 1 小时。原因是备案状态本身变更频率低而新备案通过审核后有尽快被查到的需求。这个缓存设计意味着你查询到的结果不是绝对的实时状态但用于业务判断已经足够。请求参数与鉴权本次调用的信息如下项目值请求方法GET请求地址https://v1.apizero.cn/api/icpQuery 参数domain必填Header 参数Authorization可选分类开发工具推荐 QPS5 / sQuery 参数domain是唯一必填参数类型为字符串。它的宽容度很高支持完整 URL 输入接口会自动清洗。换句话说下面三种写法在语义上是等价的baidu.com https://www.baidu.com/abc m.baidu.com:8080/fooHeader 鉴权参数Authorization是可选的鉴权头格式为Bearer sk_live_xxx。匿名调用时可以省略该参数但会受每日调用额度的限制当业务量较大或对稳定性有要求时建议配置 API Key 后再调用。最小可运行示例curl 一行接入最小可运行示例的核心目标只有一个用最少的代码拿到有效响应。curl 是这个目标最直接的体现。把下面的命令复制到终端将$APIZERO_API_KEY替换成你的真实 Key或者直接去掉-H行做匿名调用curl -sS \ -X GET \ -H X-API-Key: $APIZERO_API_KEY \ https://v1.apizero.cn/api/icp?domainbaidu.com注意上述命令中的X-API-Key是素材中 curl 示例使用的鉴权头。如果你使用文档最新推荐的Authorization: Bearer方式则改成curl -sS \ -X GET \ -H Authorization: Bearer $APIZERO_API_KEY \ https://v1.apizero.cn/api/icp?domainbaidu.com两者具体以官方文档的鉴权说明为准。建议先跑通第一个 curl再进入代码封装阶段。从命令行走向代码Python 与 JavaScript 示例curl 用来验证连通性很高效但业务系统最终还是要落到代码里。这里给出 Python 和 Node.js 两个最小可运行版本。Python 示例使用标准库urllib.request不依赖任何第三方库import json import urllib.parse import urllib.request API_URL https://v1.apizero.cn/api/icp DOMAIN baidu.com params urllib.parse.urlencode({domain: DOMAIN}) url f{API_URL}?{params} req urllib.request.Request( url, headers{ # 匿名调用时移除这一行 Authorization: Bearer sk_live_xxxxxxxxxxxxxx, Accept: application/json, }, ) with urllib.request.urlopen(req, timeout5) as resp: payload json.load(resp) if payload.get(code) 0: data payload.get(data, {}) if data.get(is_filed): print(f{data[domain]} 已备案) print(f备案号: {data[icp_code]}) print(f主办单位: {data[company_name]}) print(f单位性质: {data[company_type]}) print(f网站名称: {data[site_name]}) print(f审核时间: {data[audit_time]}) else: print(f{DOMAIN} 未备案、已注销或为境外域名) else: print(f业务异常: code{payload.get(code)}, msg{payload.get(msg)})判断逻辑非常直观先检查业务码code再检查is_filed。这种两级判断避免了把「未备案」当成「接口异常」。JavaScript 示例在 Node.js 18 环境中可以直接使用全局fetchconst apiUrl https://v1.apizero.cn/api/icp; const domain baidu.com; const url new URL(apiUrl); url.searchParams.set(domain, domain); const res await fetch(url, { headers: { // 匿名调用时移除这一行 Authorization: Bearer sk_live_xxxxxxxxxxxxxx, Accept: application/json, }, }); const payload await res.json(); if (payload.code 0) { const data payload.data; if (data.is_filed) { console.log(${data.domain} 已备案); console.log(备案号: ${data.icp_code}); console.log(主办单位: ${data.company_name}); console.log(单位性质: ${data.company_type}); console.log(网站名称: ${data.site_name}); console.log(审核时间: ${data.audit_time}); } else { console.log(${domain} 未备案、已注销或为境外域名); } } else { console.error(业务异常: code${payload.code}, msg${payload.msg}); }两个示例都遵循同一个处理框架拆解 URL → 发请求 → 先看业务码 → 再看业务数据。这比直接访问data.icp_code要稳健因为未备案时data是空字段结构直接取属性会拿到undefined。返回字段逐个拆解以domainbaidu.com为例成功响应如下{ code: 0, data: { audit_time: 2019-05-16 16:06:21, company_name: 北京百度网讯科技有限公司, company_type: 企业, domain: baidu.com, icp_code: 京ICP证030173号-1, is_filed: true, site_name: 百度一下你就知道 }, msg: 成功, request_id: abc123def456 }各字段含义如下字段类型说明codenumber业务状态码0表示成功msgstring响应描述例如「成功」request_idstring请求唯一标识排查问题时可以提供给服务方data.domainstring清洗后的域名data.is_filedboolean是否已备案。true为已备案false为未备案data.icp_codestring备案号如京ICP证030173号-1data.site_namestring网站名称来自备案信息data.company_namestring主办单位名称可能是企业、个人或事业单位data.company_typestring单位性质如「企业」data.audit_timestring备案审核通过时间格式为YYYY-MM-DD HH:mm:ss一个容易忽略的细节返回的domain是接口清洗后的值不一定是请求时传的原始字符串。如果业务系统里需要回写数据库建议以响应中的data.domain为准避免不同格式造成的数据冗余。未备案与异常情况的语义区分这是本文重点强调的边界。很多开发者在第一次接入时会有疑惑未备案是不是抛错误不是。未备案、境外域名、备案已注销时接口返回的code仍然是0但data.is_filed为false并且data中除domain外的业务字段为空。这种设计有一个明显的好处业务代码可以写出非常干净的 if 分支if (payload.code ! 0) { // 只有这里才是真的异常比如参数错误、鉴权失败、请求频率超限 } if (data.is_filed) { // 已备案逻辑 } else { // 未备案逻辑 }不要用「icp_code是否存在」来判断备案状态因为字段是否为空并不是该接口承诺的契约is_filed才是判断备案状态的唯一依据。常见错误与排查思路初次接入时最可能遇到以下几类问题按排查优先级排序鉴权方式不对。先确认你使用的是Authorization: Bearer还是X-API-Key两者混用可能被识别为无效鉴权。其次是确认 Key 前缀是否完整例如sk_live_开头。域名格式异常。虽然接口有自动清洗能力但如果你传入的字符串包含空格或换行清洗逻辑可能无法正确识别。建议在请求前做一次trim()。把未备案当成失败。is_filedfalse不是错误先检查你的代码是否在code ! 0时把未备案的数据也拦截掉了。忽略缓存导致的数据延迟。一个刚刚通过审核的新备案域名在 1 小时缓存窗口内可能仍然返回未备案。设计业务逻辑时要预留这个时间窗口不要基于一次查询结果做永久性标记。频率超限。接口 QPS 为 5 / s。如果业务需要在短时间内批量查询必须在客户端做限速否则会收到限流响应。超时时间设置过短。网络抖动时一个跨地域请求可能超过 3 秒。建议把超时时间设为 5 秒并在超时后做一次重试但重试次数不建议超过 2 次避免对服务端造成额外压力。工程化注意事项把接口从「能跑」提升到「可靠运行」还需要关注以下工程细节缓存与时效性接口侧已经有 24 小时 / 1 小时的缓存业务侧不需要再做长时间缓存。但如果你的场景是每日批量巡检建议把查询结果落库并记录查询时间便于追踪备案状态变化的时间点。批量场景的限速设计假设需要批量查询 10000 个域名按 5 QPS 计算理论耗时约 33 分钟。建议用量使用令牌桶或简单的间隔循环把请求速率控制在 4 QPS 左右留出余量。增加本地增量缓存已备案域名 24 小时内不重复请求未备案域名 1 小时内不重复请求。日志与可观测性建议把以下信息写入日志传入的原始域名和接口返回的清洗后域名request_id响应耗时code与is_filed的组合结果request_id是排查问题时的关键凭证。一旦出现批量异常可以依据request_id快速证实或排除接口侧故障。使用场景的资料留存如果是合规审查或内容安全场景建议把接口返回完整 JSON 存档而不仅仅是提取某一个字段。一旦后续出现争议原始响应就是最直接的证据。参考文档接口文档页https://apizero.cn/aidocs/icp原始文档Markdownhttps://apizero.cn/aidocs/icp/raw.md
返回列表