apnic-skills
APNIC(亚太互联网络信息中心)Go 语言 SDK,完整覆盖 APNIC 提供的所有公开数据服务与查询能力。
graph TB
subgraph SDK["apnic-skills SDK"]
direction TB
Client["Client<br/>HTTP + 反爬<br/>+ 分块下载"]
subgraph Data["数据获取器"]
Stats["Stats 统计<br/>delegated/extended<br/>assigned/ipv6-assigned<br/>legacy"]
BGP["Thyme BGP<br/>summary/raw-table<br/>asn-map/bad-prefixes<br/>used-autnums/spar"]
RDAP["RDAP<br/>ip/cidr/asn<br/>domain/entity<br/>search/help"]
Whois["Whois + 反向 DNS"]
IRR["IRR 数据库<br/>inetnum/autnum/route<br/>19 种对象类型"]
RPKI["RPKI/RRDP<br/>notification/snapshot<br/>delta"]
REx["REx 跨 RIR<br/>resources/holder<br/>network/count"]
Transfers["转移 + 变更<br/>+ 遥测"]
end
subgraph Features["内置特性"]
Stealth["浏览器伪装<br/>请求头 + 抖动"]
Chunked["分块下载<br/>Range 请求<br/>2MiB 块 ×4"]
Cache["令牌桶缓存<br/>默认 30min TTL"]
Filter["链式过滤<br/>country/type/status<br/>日期范围"]
Verify["数据完整性<br/>MD5 + PGP 签名"]
end
Client --> Data
Client --> Features
end
subgraph CLI["CLI (cobra)"]
Commands["24 个子命令<br/>完整覆盖 SDK"]
Flags["全局标志<br/>--stealth --json<br/>--bgp-source --date"]
end
CLI --> SDK
subgraph APNIC["APNIC 服务"]
FTP["ftp.apnic.net<br/>Stats/IRR/Transfers"]
RDAP_Svc["rdap.apnic.net"]
Thyme["thyme.apnic.net<br/>BGP 分析"]
RExAPI["api.rex.apnic.net<br/>跨 RIR 注册库"]
WhoisSvc["whois.apnic.net:43"]
end
SDK --> APNIC
架构总览
安装
go get github.com/hitechcloud-vietnam/apnic-skills
快速开始
package main
import (
"context"
"fmt"
"log"
apnic "github.com/hitechcloud-vietnam/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
// RDAP 查询 IP
network, err := client.RDAPLookupIP(ctx, "1.1.1.1")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Network: %s, Country: %s, Type: %s\n",
network.Handle, network.Country, network.Type)
// 获取 Delegated Stats
entries, err := client.GetDelegatedEntries(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total entries: %d\n", len(entries))
// 链式过滤
result := apnic.NewFilter(entries).
ByCountry("CN").
ByType("ipv4").
ByStatus("allocated").
Result()
fmt.Printf("CN allocated IPv4 entries: %d\n", len(result))
}
API 概览
1. Delegated Stats(IP/ASN 分配记录)
| 方法 |
说明 |
FetchDelegatedEntries(ctx) |
获取最新标准版分配记录 |
GetDelegatedEntries(ctx) |
带缓存的标准版分配记录 |
FetchDelegatedEntriesByDate(ctx, date) |
获取指定日期的分配记录 |
FetchDelegatedResult(ctx, date) |
获取完整结果(含 header/summary) |
2. Extended Delegated Stats(扩展版,含组织标识)
| 方法 |
说明 |
FetchExtendedEntries(ctx) |
获取最新扩展版分配记录 |
GetExtendedEntries(ctx) |
带缓存的扩展版分配记录 |
FetchExtendedEntriesByDate(ctx, date) |
获取指定日期的扩展版 |
FetchExtendedResult(ctx, date) |
获取完整结果(含 header/summary) |
3. Assigned Stats(按前缀大小聚合的分配统计)
| 方法 |
说明 |
FetchAssignedEntries(ctx) |
获取最新分配统计 |
GetAssignedEntries(ctx) |
带缓存的分配统计 |
FetchAssignedEntriesByDate(ctx, date) |
获取指定日期的分配统计 |
3b. IPv6 Assigned Stats(逐条 IPv6 分配记录)
| 方法 |
说明 |
FetchIPv6AssignedEntries(ctx) |
获取最新逐条 IPv6 分配记录 |
FetchIPv6AssignedEntriesByDate(ctx, date) |
获取指定日期的逐条 IPv6 分配记录 |
FetchIPv6AssignedResult(ctx, date) |
获取完整结果(含 header/summary) |
与聚合版 assigned 不同,ipv6-assigned 列出每一条 IPv6 分配(registry|cc|ipv6|start|prefix|date),无 status/opaque-id 列。
4. Legacy Stats(历史遗留资源)
| 方法 |
说明 |
FetchLegacyEntries(ctx) |
获取最新历史遗留记录 |
GetLegacyEntries(ctx) |
带缓存的历史遗留记录 |
FetchLegacyEntriesByDate(ctx, date) |
获取指定日期的历史遗留记录 |
5. RDAP 查询(结构化数据)
| 方法 |
说明 |
RDAPLookupIP(ctx, ip) |
RDAP IP 地址查询 |
RDAPLookupCIDR(ctx, cidr) |
RDAP CIDR 查询 |
RDAPLookupASN(ctx, asn) |
RDAP ASN 查询 |
RDAPLookupDomain(ctx, domain) |
RDAP 域名查询(反向 DNS) |
RDAPLookupEntity(ctx, handle) |
RDAP 实体/联系人查询 |
RDAPSearch(ctx, query) |
RDAP 实体名称搜索(等价于 RDAPSearchEntities(ctx,"fn",query)) |
RDAPSearchEntities(ctx, field, query) |
RDAP 实体搜索:field="fn" 名称搜索(支持 * 通配)/ field="handle" 精确查找 |
RDAPHelp(ctx) |
RDAP /help:服务能力声明(rdapConformance 扩展)与通知 |
RDAPSearchDomains(ctx, name) |
RDAP /domains:按名称搜索 in-addr.arpa 反向 DNS 域 |
所有 RDAP lookup 均提供 *At 变体(如 RDAPLookupIPAt(ctx, ip, date))用于点对点历史查询(RFC3339),返回该 UTC 时刻的资源状态,基于 APNIC history_version_0 扩展。
6. Transfers(IP/ASN 转移记录)
| 方法 |
说明 |
FetchTransfers(ctx) |
获取最新转移记录(每日 JSON 快照) |
GetTransfers(ctx) |
带缓存的转移记录 |
FetchTransfersByYear(ctx, year) |
获取指定年份的转移记录 |
FetchTransfersAll(ctx, date) |
获取累积 transfers-all 全集(管道分隔,自 2010 年起全部转移);date="" 取最新,YYYYMMDD 取当日归档 |
FetchTransfersAllMD5(ctx, date) |
transfers-all 的 MD5 校验值 |
FetchTransfersAllASC(ctx, date) |
transfers-all 的 PGP 签名(.asc) |
7. Changes(资源变更记录)
| 方法 |
说明 |
FetchChanges(ctx) |
获取最新变更记录 |
GetChanges(ctx) |
带缓存的变更记录 |
FetchChangesByDate(ctx, date) |
获取指定日期的变更记录 |
7b. Whois/RDAP 服务遥测
| 方法 |
说明 |
FetchTelemetry(ctx, date) |
获取 whois-rdap-stats 遥测(每小时发布):查询总量、按类型分布、Top ASN;date="" 取最新 |
FetchTelemetryMD5(ctx, date) |
遥测 JSON 的 MD5 校验值 |
7c. IRR 全量转储(RPSL)
| 方法 |
说明 |
FetchIRRDatabase(ctx, objType) |
获取并解析指定类型的 IRR 数据库转储(apnic.db.<type>.gz),objType 见 IRRObjectTypes(19 类) |
GetIRRDatabase(ctx, objType) |
带缓存的 IRR 数据库转储 |
FetchIRRCurrentSerial(ctx) |
获取 APNIC.CURRENTSERIAL(IRR 数据库当前序号) |
domain 类型的 IRR 转储承载反向 DNS 委派信息(x.in-addr.arpa + nserver/zone-c)。
7d. thyme BGP 路由表分析
| 方法 |
说明 |
FetchBGPSummary(ctx) |
获取 thyme data-summary:BGP 路由表分析指标(条目数、AS 数、ROA 覆盖、地址空间占比等,冒号键值) |
FetchBGPRawTable(ctx) |
获取 thyme data-raw-table:每条已宣告路由 prefix\tASN |
FetchBGPASNMap(ctx) |
按 origin ASN 聚合 raw table(本地派生,无额外请求) |
FetchBGPBadPrefixes(ctx, source) |
获取 thyme data-badpfx-nos:长度超过 /24 的前缀及其 origin AS(疑似路由泄漏) |
FetchBGPPerPrefixLength(ctx, source) |
获取 thyme data-pfx-nos:按前缀长度统计已宣告前缀数(/N:count 网格) |
FetchBGPUsedAutnums(ctx, source) |
获取 thyme data-used-autnums:所有在用 ASN 及注册名、国家码 |
FetchBGPSparPrefixes(ctx, source) |
获取 thyme data-spar:特殊用途地址注册表(RFC 6890)前缀及 origin AS |
FetchBGPSinglePfx(ctx, source) |
获取 thyme data-singlepfx:宣告少于 20 个前缀的 ASN 计数(按 RIR 分组) |
source 为 per-call 数据源参数:"current"(默认,全球视图)、"au"(Brisbane)、"hk"(HKIX),空串走客户端默认(WithThymeSource 设置,默认 current)。
7e. RPKI / RRDP
| 方法 |
说明 |
FetchRRDPNotification(ctx) |
获取 RRDP notification.xml:session_id、serial、当前 snapshot 引用与 deltas 列表 |
FetchRRDPSnapshot(ctx, uri) |
流式解析 RRDP snapshot.xml:仅保留 <publish>/<withdraw> 的 rsync URI,丢弃 base64 CMS body(边界内存) |
FetchRRDPDelta(ctx, uri) |
流式解析 RRDP delta.xml(增量更新,格式同 snapshot) |
7f. REx 跨 RIR 资源注册库
REx(Resource EXplorer,api.rex.apnic.net/v1/*)是 APNIC 提供的公开 REST API,将五大 RIR(APNIC/ARIN/RIPE/LACNIC/AFRINIC)的委派资源聚合为统一视图,并按持有组织(opaqueId)归集。这是各 RIR 独立 stats/RDAP 无法替代的能力:
| 方法 |
说明 |
FetchRExUserNetwork(ctx) |
自定位网络:按调用方源 IP 返回覆盖前缀、起源 ASN、经济体代码(无需参数) |
FetchRExResources(ctx, type) |
跨 RIR 最近委派资源视图(带持有者归因),type 可为 ipv4/ipv6/asn 或空 |
FetchRExHolder(ctx, opaqueID, rir) |
按 opaqueId 聚合某组织持有的全部 ASN 与前缀及规模指标 |
FetchRExHoldersUniqueCount(ctx) |
全 RIR 去重持有者总数 |
rir 取值为 afrinic/apnic/arin/lacnic/ripencc(RIPE NCC 的代码是 ripencc,不是 ripe)。opaqueId 可从 RExResource.OpaqueID 或扩展 delegated stats 获取。REx 走 HTTPS 公开 API,复用统一 HTTP 出口,自动获益于反爬伪装。
8. Whois 查询
| 方法 |
说明 |
QueryWhois(ctx, query) |
原始 Whois 查询 |
QueryWhoisIP(ctx, ip) |
IP 地址 Whois 查询(返回解析结果) |
QueryWhoisASN(ctx, asn) |
ASN Whois 查询(返回解析结果) |
QueryWhoisWithFlags(ctx, query, flags) |
带标志的 Whois 查询 |
ParseWhoisResponse(response) |
解析 Whois 响应文本 |
9. 反向 DNS
| 方法 |
说明 |
ReverseDNS(ctx, ip) |
IP 反向 DNS 解析 |
10. 历史数据
| 方法 |
说明 |
FetchHistoricalDelegated(ctx, date) |
获取指定日期的历史分配数据 |
FetchHistoricalExtended(ctx, date) |
获取指定日期的历史扩展数据 |
FetchHistoricalAssigned(ctx, date) |
获取指定日期的历史分配统计 |
FetchHistoricalLegacy(ctx, date) |
获取指定日期的历史遗留数据 |
FetchDelegatedByYear(ctx, year) |
获取指定年份的分配数据 |
FetchExtendedByYear(ctx, year) |
获取指定年份的扩展数据 |
ListAvailableYears() |
列出可用的历史数据年份 |
11. 数据校验
| 方法 |
说明 |
VerifyMD5(ctx, dataType, date) |
端到端校验:下载数据文件 + MD5 旁挂文件,本地计算并比对 |
FetchMD5Checksum(ctx, dataType, date) |
获取 MD5 校验值(兼容 BSD MD5 (file) = 与 GNU 风格) |
FetchASCSignature(ctx, dataType, date) |
获取 PGP 签名(.asc) |
FetchPublicKey(ctx) |
获取 APNIC 签名公钥(CURRENT_PUBLIC_KEY) |
12. 过滤与分组
| 方法 |
说明 |
FilterEntries(entries, country, resType) |
按国家和类型过滤 |
FilterByStatus(entries, status) |
按状态过滤 |
FilterByDateRange(entries, start, end) |
按日期范围过滤 |
FilterExtendedByOpaqueID(entries, opaqueID) |
按组织标识过滤 |
FilterExtendedByCountry(entries, country) |
按国家过滤扩展版 |
FilterExtendedByType(entries, resType) |
按类型过滤扩展版 |
FilterExtendedByStatus(entries, status) |
按状态过滤扩展版 |
GroupByCountry(entries) |
按国家分组 |
GroupExtendedByOpaqueID(entries) |
按组织分组 |
GroupExtendedByCountry(entries) |
按国家分组扩展版 |
13. 链式过滤 API
// 标准版链式过滤
result := apnic.NewFilter(entries).
ByCountry("CN").
ByType("ipv4").
ByStatus("allocated").
ByDateRange(start, end).
Result()
// 扩展版链式过滤
extResult := apnic.NewExtendedFilter(extEntries).
ByCountry("JP").
ByType("ipv6").
ByOpaqueID("A92E1062").
Result()
14. CIDR 计算
// 标准版
cidr, err := entry.CIDR()
// 扩展版
cidr, err := extEntry.CIDR()
// 历史遗留版
cidr, err := legacyEntry.CIDR()
客户端配置
client := apnic.NewClient(
apnic.WithCacheTTL(10 * time.Minute),
apnic.WithUserAgent("my-app/1.0"),
apnic.WithRDAPBaseURL("https://rdap.apnic.net"),
apnic.WithWhoisServer("whois.apnic.net:43"),
apnic.WithWhoisTimeout(15 * time.Second),
apnic.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
)
反爬与浏览器伪装(默认开启)
为避免被目标站点识别为爬虫,SDK 默认启用浏览器伪装中间件:所有 HTTP 出口(含 whois 抖动)注入主流 Chrome 的完整请求头(UA、Accept-Language、Sec-Fetch-、Sec-Ch-Ua- 等),并在请求间加入令牌桶限速与随机抖动。可通过 Option 调整或关闭:
client := apnic.NewClient(
apnic.WithStealth(true), // 默认 true;false 时仅发 UA+Accept(向后兼容)
apnic.WithBrowserUserAgent("Mozilla/5.0 ..."), // 自定义浏览器 UA
apnic.WithJitter(200*time.Millisecond, 800*time.Millisecond), // 每请求随机延迟区间
apnic.WithRateLimit(2.0), // 全局每秒最大请求数(令牌桶,0=不限速)
apnic.WithFTPBaseURL(apnic.DefaultFTPBaseURL), // IRR/transfers-all/telemetry 的 FTP 根
apnic.WithRRDPBaseURL(apnic.DefaultRRDPBaseURL),
apnic.WithThymeBaseURL(apnic.DefaultThymeBaseURL),
apnic.WithRExBaseURL(apnic.DefaultRExBaseURL), // REx 跨 RIR 资源注册库
)
兼容性说明:stealth 开启时设置 Accept-Encoding: gzip,Go Transport 不会自动解压,故 fetchText、RRDP 流式解析器与 REx fetchJSON 均显式处理 Content-Encoding: gzip,避免双重解压。测试中可用 APNIC_NO_JITTER=1 跳过抖动以加速。
大文件分块下载(默认开启)
APNIC FTP 对大文件(delegated 4.3MB、extended、IRR apnic.db.inetnum.gz 50MB+ 等)实施单连接带宽限速 ~8-22 KB/s——这不是反爬检测(任意 UA 速度相同、无 403、响应头正常 accept-ranges: bytes),而是服务器级限流。单连接下载 50MB 的 IRR dump 需 ~40 分钟,远超常规超时。
SDK 默认启用多连接分块下载:探测支持 Range 后,将文件切成 ~2MB 的小块,以 4 个并发 Range 请求轮转下载(每连接独立限速,总吞吐提升 3-4 倍),再用 io.Pipe 流式合并、按需 gzip 解压。每个分块请求仍走 doHTTPRequest,完整继承浏览器伪装头 + 令牌桶限速 + 抖动。不支持 Range 或传输层 gzip 的端点透明回退单连接。
client := apnic.NewClient(
apnic.WithMaxConcurrentDownloads(4), // 并发 Range 请求数(0/1 禁用分块)
apnic.WithChunkSize(2*1024*1024), // 显式每块大小;默认 2MiB
apnic.WithDownloadTimeout(5*time.Minute), // 单块超时(建议 ≥ 单块大小/8KBps)
)
选块策略:默认每块 2MiB(在 22KB/s 限速下约 90 秒下完,远低于建议的 5 分钟单块超时)。maxConcurrent 仅控制并发数,不限制块数——50MB 文件会切成 ~25 块经 4 个 worker 轮转。块数硬上限 64,并发硬上限 16,避免对服务器造成压力。若仍遇单块超时,可调小 WithChunkSize(如 1MiB)或调大 WithDownloadTimeout。
慢块容错:当某个分块因连接卡住触发 context deadline exceeded 时,SDK 会自动将该块拆成两个子块以新连接并发重试,绕开卡死的 TCP 连接,避免单点抖动拖垮整个下载。
命令行工具(CLI)
仓库附带基于 cobra 的 apnic CLI,封装全部 SDK 能力:
# 构建
go build -o bin/apnic ./cmd/apnic
# 示例
apnic delegated --json | jq '.Entries | length'
apnic filter --source delegated --country CN --type ipv4
apnic rdap ip 1.1.1.1 --date 2020-06-01T00:00:00Z
apnic rdap help # RDAP 服务能力声明与通知
apnic rdap domains 1 # 搜索 in-addr.arpa 反向 DNS 域
apnic transfers-all # 累积转移全集(自 2010)
apnic transfers-all --date 20220904
apnic stats-telemetry # whois/RDAP 服务查询遥测
apnic irr inetnum # IRR 数据库转储(RPSL)
apnic irr serial # APNIC.CURRENTSERIAL
apnic bgp summary # thyme BGP 路由表分析指标
apnic bgp raw-table # thyme 原始路由表
apnic bgp asn-map # 按 origin ASN 聚合
apnic bgp bad-prefixes # 长度超过 /24 的前缀及 origin AS(路由泄漏候选)
apnic bgp per-prefix-length # 按前缀长度统计已宣告前缀数
apnic bgp used-autnums # 所有在用 ASN 及注册名/国家码
apnic bgp spar-prefixes # 特殊用途地址注册表(RFC 6890)前缀
apnic bgp single-pfx # 宣告少于 20 前缀的 ASN 计数(按 RIR)
apnic rpki notification # RRDP notification(session/serial/snapshot/deltas)
apnic rpki snapshot # 流式解析当前 snapshot
apnic rex network # REx 自定位网络(源 IP 的覆盖前缀/ASN/经济体)
apnic rex resources ipv4 # 跨 RIR 最近委派资源(持有者归因)
apnic rex holder <opaqueId> <rir> # 聚合某组织持有的全部 ASN/前缀
apnic rex count # 全 RIR 去重持有者总数
apnic verify integrity --type delegated
反爬相关全局标志:--stealth(默认 true)、--browser-ua、--jitter 200ms-800ms、--rate-limit(每秒请求数)、--ftp-base-url/--rrdp-base-url/--thyme-base-url/--rex-base-url、--bgp-source(thyme BGP 数据源:current/au/hk,默认 current)。
License
MIT