海风域名查询工具v1.0:RDAP批量查询与过期域名筛选实战

发布时间:2026/10/6 13:09:43
海风域名查询工具v1.0:RDAP批量查询与过期域名筛选实战
简介海风域名查询工具v1.0是一套基于PHP开发的轻量级域名查询系统面向需要在Linux主机上快速搭建域名信息查询服务的站长与开发者尤其适合具备基础PHP环境配置能力的中级用户。资源包为rar压缩格式整体约372KB体积小巧便于部署包内以PHP源码文件为主涵盖安装引导、后台管理及数据配置等模块另含sql数据文件用于初始化数据库结构。使用前需将sitemap.xml、inc/config.php、install/data.sql等文件权限设为777运行install/index.php完成安装随后通过/acp/进入后台默认账号密码均为admin。目前已有118人学习关注读者可获得一套可直接运行的域名查询工具源码掌握从权限配置、数据库导入到后台管理的完整部署流程并基于现有代码结构进行二次开发或功能扩展适合作为小型查询类站点的搭建参考。1. 海风域名查询工具 v1.0从域名到可用情报一条命令能跑通什么手里攥着几十个域名想知道哪些还能注册、哪些快到期、哪些已经被人抢注一个个去注册商页面敲半小时就没了。海风域名查询工具 v1.0 要解决的就是这件事把域名批量查询、状态筛选、结果落盘串成一条流水线让「查一批域名」从手工活变成脚本活。它适合做站群监控、品牌域名防御、过期域名捡漏的从业者也适合刚接触域名 API 的新手拿来练手。核心思路不复杂——用公开的 RDAP/WHOIS 接口做数据源本地做并发控制和结果缓存最后输出成 CSV 或 JSON。v1.0 这个版本号意味着功能边界清晰单机跑、命令行调、不依赖数据库。下面从选型到落地把这条路走一遍。2. 域名查询工具的技术选型RDAP 还是 WHOIS并发怎么控2.1 为什么优先选 RDAP 而不是传统 WHOIS传统 WHOIS 走 43 端口返回的是纯文本每个注册局的格式都不一样解析起来全是正则地狱。RDAPRegistration Data Access Protocol是 ICANN 推的标准化协议走 HTTPS返回 JSON字段名统一解析成本低一个量级。常见做法是能用 RDAP 的 TLD 就走 RDAP不支持的再降级到 WHOIS。判断某个 TLD 是否支持 RDAP可以查 IANA 的 bootstrap 文件。这个文件是公开的格式是 JSON列出了每个 TLD 对应的 RDAP 基础 URL。工具启动时拉一次缓存到本地后续查询直接查表。import json import urllib.request def load_rdap_bootstrap(cache_pathrdap_bootstrap.json): 拉取 IANA RDAP bootstrap 文件本地缓存 url https://data.iana.org/rdap/dns.json try: with open(cache_path, r) as f: return json.load(f) except FileNotFoundError: with urllib.request.urlopen(url, timeout10) as resp: data json.loads(resp.read().decode()) with open(cache_path, w) as f: json.dump(data, f) return data def find_rdap_endpoint(bootstrap, tld): 根据 TLD 找到对应的 RDAP 端点 for service in bootstrap.get(services, []): tlds, urls service[0], service[1] if tld.lower() in [t.lower() for t in tlds]: return urls[0] return None这段代码的逻辑是先尝试读本地缓存没有就去 IANA 拉。find_rdap_endpoint遍历 services 数组匹配 TLD 后返回第一个 URL。参数上timeout10是防止网络卡死缓存文件建议放在脚本同目录方便清理。注意 bootstrap 文件会更新建议每周强制刷新一次可以在缓存文件里记一个时间戳来判断。2.2 并发模型线程池还是异步 IO域名查询是典型的 IO 密集型任务瓶颈在网络往返不在 CPU。Python 里两条路concurrent.futures.ThreadPoolExecutor或者asyncio aiohttp。新手建议先用线程池代码直观调试方便熟手可以上 asyncio同样并发数下资源占用更低。线程池的关键参数是max_workers。设太小查询慢设太大容易被目标服务器限流甚至封 IP。血泪经验是对 RDAP 端点单域名并发控制在 510 比较稳如果查的是同一个注册局下的几百个域名建议降到 35并在请求间加 0.20.5 秒延迟。from concurrent.futures import ThreadPoolExecutor, as_completed import time def batch_query(domains, query_func, max_workers5, delay0.3): 批量查询域名带并发控制和延迟 results {} with ThreadPoolExecutor(max_workersmax_workers) as executor: future_map {} for d in domains: future executor.submit(query_func, d) future_map[future] d time.sleep(delay) # 提交间隔降低被限流概率 for future in as_completed(future_map): domain future_map[future] try: results[domain] future.result(timeout15) except Exception as e: results[domain] {error: str(e)} return resultsmax_workers5和delay0.3是保守起点。如果查询目标分散在多个注册局可以适当提高并发如果集中在一个注册局建议再降。future.result(timeout15)是单次查询的超时防止某个请求卡住整个批次。返回的 results 字典里成功的是结构化数据失败的是 error 字段后续可以单独重试。2.3 结果字段设计哪些信息值得落盘查询结果不要只存一个「可用/不可用」。RDAP 返回的 JSON 里有几个字段对实际决策很有价值ldhName域名本身、status状态数组比如 clientTransferProhibited 表示锁定了转移、events注册、到期、最后变更时间、nameserversDNS 服务器、entities注册商信息。把这些抽出来存成扁平结构后面做筛选和排序才方便。字段名来源路径用途domainldhName域名标识statusstatus 数组判断是否可注册、是否被锁reg_dateevents 中 eventActionregistration注册时间exp_dateevents 中 eventActionexpiration到期时间捡漏核心registrarentities 中 roleregistrar注册商判断转移难度nameserversnameservers 数组DNS 配置判断是否活跃落盘格式建议 CSV 和 JSON 各存一份。CSV 给 Excel 看JSON 给后续脚本消费。注意 CSV 里的逗号和换行要转义用 Python 的csv模块自动处理别手拼字符串。3. 海风域名查询工具 v1.0 的最小可跑通实现3.1 环境准备与依赖安装工具本身不依赖重型框架Python 3.8 以上即可。需要装的第三方库只有requests发 HTTP 请求和tqdm进度条可选但强烈建议。如果要用 WHOIS 降级方案再加一个python-whois。python3 -m venv venv source venv/bin/activate pip install requests tqdm python-whois虚拟环境是后悔药别省这一步。不同项目的依赖冲突起来排查成本远高于建环境的十秒钟。python-whois在部分系统上依赖whois命令行工具Ubuntu 下apt install whoismacOS 自带。3.2 单域名查询函数RDAP 请求与解析先写一个查单个域名的函数输入域名输出结构化字典。核心步骤拆出 TLD查 bootstrap 找端点拼 URL 发请求解析 JSON 抽字段。import requests from urllib.parse import urlparse def query_single_domain(domain, bootstrap): 查询单个域名返回结构化结果 tld domain.rsplit(., 1)[-1] endpoint find_rdap_endpoint(bootstrap, tld) if not endpoint: return {domain: domain, error: no_rdap_endpoint, source: rdap} url f{endpoint.rstrip(/)}/domain/{domain} headers {Accept: application/rdapjson} try: resp requests.get(url, headersheaders, timeout10) except requests.RequestException as e: return {domain: domain, error: str(e), source: rdap} if resp.status_code 404: return {domain: domain, status: [available], source: rdap} if resp.status_code ! 200: return {domain: domain, error: fhttp_{resp.status_code}, source: rdap} data resp.json() result { domain: data.get(ldhName, domain).lower(), status: data.get(status, []), source: rdap, } for event in data.get(events, []): action event.get(eventAction) if action registration: result[reg_date] event.get(eventDate) elif action expiration: result[exp_date] event.get(eventDate) for entity in data.get(entities, []): if registrar in entity.get(roles, []): vcard entity.get(vcardArray, [None, []])[1] for item in vcard: if item[0] fn: result[registrar] item[3] result[nameservers] [ns.get(ldhName) for ns in data.get(nameservers, [])] return result逻辑说明404 直接判定为可注册这是 RDAP 的约定。200 时解析 events 和 entitiesevents 里可能有多个动作只取 registration 和 expiration。entities 里找 role 含 registrar 的从 vcardArray 里抽 fn正式名称。参数上timeout10和批量查询保持一致Accept头声明要 RDAP JSON 格式。注意有些注册局返回的 vcard 结构有差异取item[3]前要判断长度生产环境建议加 try。3.3 批量查询与结果落盘把单域名函数套进批量框架加上进度条和落盘。import csv import json from tqdm import tqdm def run_batch(domain_file, output_prefixresult): 读取域名列表批量查询输出 CSV 和 JSON with open(domain_file) as f: domains [line.strip() for line in f if line.strip()] bootstrap load_rdap_bootstrap() results [] with ThreadPoolExecutor(max_workers5) as executor: future_map {executor.submit(query_single_domain, d, bootstrap): d for d in domains} for future in tqdm(as_completed(future_map), totallen(domains)): results.append(future.result()) # 落盘 JSON with open(f{output_prefix}.json, w) as f: json.dump(results, f, ensure_asciiFalse, indent2) # 落盘 CSV fields [domain, status, reg_date, exp_date, registrar, nameservers, error, source] with open(f{output_prefix}.csv, w, newline) as f: writer csv.DictWriter(f, fieldnamesfields, extrasactionignore) writer.writeheader() for r in results: r[status] |.join(r.get(status, [])) r[nameservers] |.join(r.get(nameservers, [])) writer.writerow(r) return resultsdomain_file是每行一个域名的文本文件。extrasactionignore让 DictWriter 忽略 fields 里没列的键避免报错。status 和 nameservers 是列表用竖线拼成字符串再写 CSV。JSON 保留原始结构方便后续程序读。输出前缀默认 result会生成 result.json 和 result.csv。3.4 用真实域名跑一遍验证准备一个 domains.txt放几个不同类型的域名一个已注册的、一个大概率可注册的、一个不存在的 TLD。echo example.com domains.txt echo thisdomainprobablynotregistered12345.com domains.txt echo test.zzz domains.txt python run_batch.py预期结果example.com 返回 200status 里有 clientTransferProhibited 之类的状态有 reg_date 和 exp_date。第二个域名大概率返回 404status 为 available。第三个域名因为 .zzz 不在 bootstrap 里返回 no_rdap_endpoint。跑完后打开 result.csv检查字段是否完整日期格式是否是 ISO 8601。如果 example.com 返回 429说明被限流了把 max_workers 降到 2delay 加到 1 秒再试。4. 海风域名查询工具 v1.0 避坑与常见问题排查4.1 返回 429 或连接被重置现象批量查询跑到一半大量请求返回 429 Too Many Requests或者连接直接被重置。原因目标 RDAP 端点对单 IP 有速率限制并发数或请求频率超了。解决把 max_workers 降到 23delay 提到 0.51 秒如果还是不行换出口 IP 或者分批次跑每批之间停几分钟。别硬刚封了 IP 更麻烦。4.2 日期字段解析出来是 None现象reg_date 和 exp_date 在结果里是 None但域名明明已注册。原因不同注册局返回的 events 结构有差异有的用 eventAction 字段有的用 eventType还有的日期格式不是标准 ISO。解决在解析 events 时同时兼容 eventAction 和 eventType日期用dateutil.parser做宽松解析。如果某个注册局完全不返回 events只能降级到 WHOIS 补数据。4.3 某些 TLD 查不到 RDAP 端点现象.cn、.de 等 TLD 在 bootstrap 里找不到对应端点返回 no_rdap_endpoint。原因部分国家顶级域名不提供公开 RDAP 服务或者端点不在 IANA 的 bootstrap 列表里。解决对这些 TLD 降级到 WHOIS 查询用python-whois库解析文本输出。注意 WHOIS 返回格式因注册局而异正则要写宽松些匹配不到就存原始文本别丢数据。4.4 CSV 打开乱码或列错位现象用 Excel 打开 result.csv中文注册商名称乱码或者列对不齐。原因Excel 默认用本地编码打开 CSV不是 UTF-8另外如果字段里有逗号没转义列会错位。解决写 CSV 时用encodingutf-8-sig让 Excel 识别 BOM。逗号转义交给 csv 模块别手拼。如果还是乱码用 VS Code 或 Numbers 打开确认数据本身没问题。4.5 查询速度远低于预期现象100 个域名跑了十几分钟比预期慢很多。原因可能是 delay 设太大或者某个请求卡住导致线程池等待也可能是 DNS 解析慢。解决先看 tqdm 进度条是不是均匀前进如果卡在某个域名单独查那个域名看是不是端点响应慢。把 timeout 从 10 降到 5减少单次等待。如果目标端点本身慢考虑换数据源或者接受这个速度别为了快牺牲稳定性。5. 把查询结果用起来过期域名筛选与自动化监控查到数据只是第一步真正有价值的是从结果里筛出「快到期且状态允许转移」的域名。我的习惯是写一个筛选脚本读 result.json按 exp_date 排序过滤掉 status 里含 clientTransferProhibited 的这种转移不了再按注册商分组方便批量操作。from datetime import datetime, timezone import json def filter_expiring(input_json, days30): 筛选 N 天内到期且可转移的域名 with open(input_json) as f: data json.load(f) now datetime.now(timezone.utc) candidates [] for item in data: exp item.get(exp_date) if not exp: continue try: exp_dt datetime.fromisoformat(exp.replace(Z, 00:00)) except ValueError: continue days_left (exp_dt - now).days if 0 days_left days: status item.get(status, []) if clientTransferProhibited not in status: candidates.append({**item, days_left: days_left}) candidates.sort(keylambda x: x[days_left]) return candidatesdays30是筛选窗口按需改。fromisoformat处理 ISO 8601 日期replace(Z, 00:00)兼容 UTC 标记。过滤 clientTransferProhibited 是关键否则筛出来的域名根本转不走。返回结果按剩余天数升序最急的排前面。进阶用法是把这个脚本挂到 cron 里每天跑一次结果追加到历史文件用 diff 看哪些域名状态变了。比如某个域名从 clientTransferProhibited 变成 ok说明锁解了可以动手了。这个监控思路同样适用于品牌域名防御——盯着竞品或仿冒域名的到期时间提前准备。我踩过最大的坑是没做历史记录某次一个心仪的过期域名在凌晨被释放等我早上看到结果时已经被抢了。后来加了每日快照和状态变更告警才没再错过。工具本身不复杂值钱的是持续跑和及时看。希望帮到你。本文还有配套的精品资源点击获取