简介海风域名查询工具是一款基于PHP开发的域名查询工具专为Linux主机环境设计面向需要自建域名查询服务的站长、运维人员以及PHP二次开发者。资源包为RAR压缩格式约372KB下载页未提供有效的文件总数与类型明细核心内容以PHP源码形式提供涵盖前台查询与后台管理模块。目前已有118人学习下载。该工具内置完整安装引导逻辑通过简单配置即可部署独立管理后台并预设默认管理员账号便于直接使用。对于开发者源码结构简洁可学习PHP项目搭建、数据库初始化以及Linux文件权限设置对于使用者可快速搭建轻量级域名查询平台满足日常域名状态、归属信息查询等需求。整体兼顾实用性与学习价值。1. 海风域名查询工具 v1.0 是什么一条命令查清域名底细而不是手动敲一下午周五晚上某开发者的工作群有人抛了个活200 多个域名的注册信息和解析状态周一早上要交表格。手动 whois 一次只能查一个输出格式各家注册局还不一样部分域名查询直接卡住不动。海风域名查询工具 v1.0 就是冲着这种场景来的一个本地命令行工具一条命令批量查完 WHOIS、DNS 解析和备案信息输出统一的 JSON 或表格。它适合两类人一类是做域名资产盘点、需要定期核对注册信息的运维和安全从业者另一类是买域名之前想做尽调、怕买到有历史污点域名的个人开发者。用之前觉得“这有什么难的”真正批量跑完才发现坑全在解析、限速和数据源选型上。2. 域名信息的三个数据源WHOIS、DNS 与备案的选型和协议细节2.1 WHOIS 只是“文本问答”端口 43、响应格式与解析难度WHOIS 本质上是一个基于 TCP 43 端口的文本协议客户端连上服务器发送一行域名\r\n服务器把结果以纯文本返回然后断开连接。整个过程没有结构化的字段定义没有统一的编码声明。这就是为什么很多人自己写 whois 脚本时总是翻车你以为返回的是key: value的整齐列表实际上有的注册局返回一大段散文式的说明有的把注册人信息放在一个多行缩进块里还有的会返回 301 跳转提示——告诉你“这个域名的 whois 服务在另一台服务器上请去那里查”。海风 v1.0 把协议层的事情做了封装。它内部先向whois.iana.org查询域名所属的注册局拿到whois服务器地址后再向真正的注册局发起第二次查询最后按照运行者整理的别名映射表来解析字段。理解这一点很重要因为如果你要基于海风做二次开发或者排查突发故障你得知道它底层其实是两个连续的 43 端口对话。import socket def raw_whois(domain: str, server: str, port: int 43, timeout: int 10) - str: 最底层的 WHOIS 查询动作连上 43 端口发送查询读取全部响应。 sock socket.create_connection((server, port), timeouttimeout) sock.send((domain \r\n).encode(utf-8, ignore)) chunks [] while True: data sock.recv(4096) if not data: break chunks.append(data.decode(utf-8, ignore)) sock.close() return .join(chunks) print(raw_whois(example.com, serverwhois.iana.org))这段代码展示了 WHOIS 最朴素的原貌。参数上需要注意timeout它决定了连接和接收数据的最大等待时间海风 v1.0 默认是 8 秒在批量场景里这个值不宜再大否则单个域名的卡顿会拖垮整批任务。recv(4096)是每次读取的缓冲区大小足够覆盖绝大多数 whois 返回如果返回内容超过缓冲区循环会继续读取直到连接关闭所以结果不会丢。解析是真正的重头戏。同一个“注册商”字段在不同注册局的响应里可能叫Registrar、Registrar:或Sponsoring Registrar创建时间的写法更多有Creation Date、created、Registered on。海风 v1.0 的应对方案是一套别名映射表把每个标准字段对应的各种写法都列进去解析时逐个匹配。这比单纯用正则抓第一个日期可靠得多后面避坑章节会专门讲日期抓错的问题。另外要提一下 RDAP。WHOIS 的 43 端口在很多企业网络和机房环境里会被安全策略拦掉海风 v1.0 做了备选方案遇到 43 端口不可达时自动切换 RDAPRegistration Data Access Protocol走 443 端口查询。RDAP 返回的是标准 JSON天然结构化不需要做文本解析。用requests向公共 bootstrap 服务发起请求即可例如https://rdap.org/domain/example.com但生产环境建议自建缓存避免每次查询都走公共服务。这里的取舍很明确能用 WHOIS 就用 WHOIS它的覆盖面最广被网络策略卡住时RDAP 是后悔药。2.2 DNS 查询别用 getaddrinfo用 dnspython 拿全 A、CNAME、MX、NS、TXT注册信息回答的是“这个域名是谁的”DNS 解析回答的是“这个域名指向哪”。很多人图省事直接用 Python 的socket.getaddrinfo()查 IP这个做法有三个毛病第一它只返回 A 和 AAAA 记录查不了 CNAME、MX、NS、TXT第二它走系统解析器结果受本地 hosts 文件和系统缓存影响拿到的不一定是权威答案第三它对 NXDOMAIN 和超时的区分很含糊出错时给不出可用的诊断信息。海风 v1.0 的做法是直接用 dnspython 构造独立的 Resolver显式指定公共递归解析器绕过系统配置。这样做的好处是批量场景下的行为可预期不受运行机器环境影响。import dns.resolver def query_dns(domain: str, record_type: str A, nameserver: str 8.8.8.8) - list[str]: 查询指定类型的 DNS 记录返回文本形式的记录值列表。 res dns.resolver.Resolver(configureFalse) # 不读系统 /etc/resolv.conf res.nameservers [nameserver] res.timeout 5.0 # 单次查询超时 5 秒 res.lifetime 8.0 # 整个解析过程最长 8 秒 try: answers res.resolve(domain, record_type) return [r.to_text() for r in answers] except dns.resolver.NoAnswer: return [] # 域名存在但该记录类型不存在 except dns.resolver.NXDOMAIN: return [NXDOMAIN] # 域名本身不存在 except dns.resolver.LifetimeTimeout: return [TIMEOUT] # 解析超时常见于网络抖动 print(query_dns(example.com, A))这段代码里有几个参数值得细看。configureFalse是海风批量查询的关键设计如果不显式关闭系统配置在 DNS 配置异常的机器上跑批量任务结果会五花八门。nameserver指定为公共解析器后所有查询行为统一。timeout和lifetime的区别也要搞清楚前者是单次向单个服务器发送查询的等待时间后者是整个解析过程的预算在批量场景里这两个值控制着单域名的最坏耗时避免某个域名把整批任务卡住。为什么要把查询结果全部转成文本列表而不是保留对象因为海风 v1.0 的输出目标是 JSONr.to_text()返回的是规范化文本比如 CNAME 记录会带末尾的点A 记录就是纯 IP序列化时不需要额外处理。我做资产盘点时最关心的是NXDOMAIN和NoAnswer的区别前者说明域名已经掉了后者说明域名还在但某个服务不在了这决定了资产清理的方向——顺手记一下这个区分在资产盘点里价值很高。2.3 备案信息走数据服务不硬爬公示页面备案信息是中文互联网特有的查询项它回答的是“这个域名的运营主体是谁”。海风 v1.0 把备案查询设计成一条数据链路首选第三方数据服务商的接口失败时降级到本地离线库最后才走页面查询兜底。为什么把爬虫放在最后因为公示页面有验证码、有频率限制、页面结构还会不定期改版任何一个变化都会让爬虫当场失效。这不是写代码能解决的问题是数据源的稳定性问题。海风 v1.0 在这条链路上的处理方式很务实每次查询前先检查本地离线库的更新日期超过 24 小时就先尝试在线接口刷新接口返回异常时不中断整个批量任务而是把当前域名的icp字段标记为UNKNOWN继续处理下一个。批量任务最忌讳单点失败拖垮全盘这个设计保证了 200 个域名里即使有一两个备案接口抽风剩下的也能出结果。3. 本地跑通海风 v1.0安装、最小查询与必调参数3.1 环境准备Python 版本与依赖库海风 v1.0 是个典型的 Python 命令行工具运行环境要求 Python 3.9 以上。它的核心依赖有三个dnspython 负责 DNS 查询requests 负责 RDAP 和备案接口的 HTTP 请求rich 负责终端表格渲染。拿到源码包后第一步永远是把依赖装进虚拟环境不要直接往系统 Python 里装——批量查询涉及网络超时和并发依赖版本冲突会让排查变得很痛苦。# 假定源码目录是 haifeng/入口模块是 haifeng.cli cd haifeng python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt python -m haifeng.cli --versionpython -m haifeng.cli这种入口方式比直接python main.py好在哪它让包内相对导入稳定后续加子命令时不会出现“找不到模块”的问题。--version在 v1.0 里会输出当前版本号和依赖的检查结果如果某个依赖缺失这一步就能看出来不用等跑查询时报错。安装完成后建议先跑一次--help看子命令结构。海风 v1.0 把功能拆成了三个子命令query负责单域名查询batch负责批量文件处理serve是 HTTP 服务模式。这个拆分在第五章之前不需要深入但记住 batch 是主力、query 是调试工具、serve 是进阶玩法后面读起来会顺很多。3.2 最小查询haifeng query 一条命令看全貌跑通安装之后第一个查询建议用最完整的参数组合目的是看看数据长什么样。python -m haifeng.cli query example.com --whois --dns --icp --format json--whois、--dns、--icp三个开关分别控制三类数据源是否查询能单独关掉某个模块比如查境外域名时--icp没必要开--format json指定输出结构默认是终端表格。输出的大致结构是{ domain: example.com, query_time: 2025-06-01T22:15:3308:00, whois: { registrar: Example Registrar, Inc., creation_date: 1997-09-15, expiration_date: 2026-09-14, status: [ok] }, dns: { a: [10.0.0.1], ns: [a.iana-servers.net, b.iana-servers.net], mx: [], txt: [vspf1 -all] }, icp: { status: UNKNOWN }, overall_status: found }query_time是查询发起的本地时间这个字段很关键因为 whois 里的创建时间是注册局记录的静态信息而query_time标记的是这条数据的获取时间做历史对比时两个时间缺一不可。overall_status是海风 v1.0 对结果的汇总判断found表示域名可解析、whois 有记录not_found表示 NXDOMAINerror表示查询过程中出现了超时或连接被重置。刚上手时不要急着调参数先用这个全量命令查一个自己熟悉的域名对照你在注册商后台看到的到期时间是否一致。这一步能验证工具的解析逻辑对不对——如果连自己域名都解析串了那后面批量查询的结果也得重新审视。3.3 参数怎么调超时、重试、并发与输出格式海风 v1.0 的默认参数在多数场景下是保守的但批量任务通常需要按网络环境调整。最经常动的是--timeout、--retry、--concurrency和--output四个参数参数默认值适用场景与建议--timeout8 秒弱网环境调到 15 秒机房网络调到 5 秒可以加速失败--retry2 次不要超过 3 次重试过多会触发对端限速--concurrency5 个服务器环境可提到 8超过 10 基本必被限速--output终端输出批量场景必须指定文件避免输出刷屏--timeout是个双刃剑。设太大一个连不上的域名要等半分钟才确认失败200 个域名就是几十分钟的浪费设太小正常查询也会频繁超时。我的习惯是先拿 10 个域名试跑观察失败率失败率高于 30% 就加超时时间低于 5% 就维持默认。--retry的默认 2 次是否该调取决于你查的是什么域名——老域名注册局的服务器往往比较稳定可以保持默认查一些冷门后缀时第一次查询大概率超时重试反而能成功。另外--concurrency不是单纯地开线程它受到第二章讲的 socket 数和 HTTP 连接数双重约束。海风 v1.0 内部每个域名查询会发起至少 2 个网络请求whois 和 dns并发数设成 5 时网络层实际同时可能有 10 个左右连接这个数量对注册局服务器还算友善。我自己第一次跑 500 个域名时把并发调到了 20结果大概第 40 个域名开始返回的全是空响应——后面避坑章节会专门讲这个。4. 批量查询几百个域名输入规范、结果结构与并发限速4.1 输入文件规范一行一个域名支持注释批量查询是海风 v1.0 的主力场景。输入文件要求 UTF-8 编码一行一个域名支持#开头的注释行和空行。注意这里有个容易忽略的约束不能带http://或https://前缀不能带路径。很多人从 Excel 里拷域名时顺手把 URL 整段粘进来海风会直接报格式错误。# 2025 年域名资产盘点清单 # 核心业务域名 example.com example.org # 待清理的旧项目域名 legacy-project.net # 中文域名 例子.中国这里有一个细节值得展开为什么输入文件里允许直接写中文域名因为海风 v1.0 在解析阶段会自动做 IDN 到 punycode 的转换用户不需要自己先去转码。这个设计的价值在于批量清单往往是从业务方手里直接拿来的业务方不会帮你转码工具能自动处理就省掉了一层手工活。但第五章会提到这个自动转换在遇到不规范编码时会留下一个坑需要知道在哪儿看日志。批量执行时用batch子命令指定输入文件和输出文件python -m haifeng.cli batch --input domains.txt --output result.json --concurrency 5 --format json--output在这里不只是重定向海风 v1.0 会把结果按域名分组写成一个 JSON 数组并且每处理完一个域名就写一次文件。这个“边跑边写”的设计很重要批量任务跑了几十分钟中途断电或者被杀进程已经完成的部分不会丢剩下的重新跑一遍输入文件即可不需要重头再来。4.2 结果怎么设计JSON 字段与查询时间戳批量结果的结构和单域名查询一致但数组里每个元素多了一个入参回显字段方便按域名索引。字段设计上最值得说的是两个地方。第一个是query_time。海风 v1.0 在每个域名的结果里都记录查询时间而不是在数组外层统一记一个开始时间。原因很简单200 个域名按 5 并发跑完可能耗时 40 分钟第一个域名和最后一个域名的查询时间相差很大如果只记整体开始时间后续排查“某个域名当时的状态为什么是这样”时时间线完全对不上。第二个是overall_status的取值策略。它不是一个简单的成功/失败布尔值而是综合 whois 和 dns 两部分结果给出判断overall_status判定条件资产盘点含义foundDNS 可解析或 whois 有记录域名还在使用not_foundDNS 返回 NXDOMAIN 且 whois 无记录域名已删除可以释放error查询超时或连接被重置数据源异常需要人工复查partialwhois 成功但 DNS 失败注册存在但解析异常重点排查partial是 v1.0 里最容易被人忽视但最有价值的分类。它捕捉的情况在资产盘点里往往是事故前兆域名续费了但解析被误删或者 DNS 服务器挂了但 whois 没变。批量跑完后先看overall_status的分布再针对error和partial的域名单独复查比从头到尾看一遍所有的记录效率高得多。4.3 并发与限速ThreadPoolExecutor 信号量 退避并发控制是批量查询工具翻车概率最高的地方。表面逻辑很简单用ThreadPoolExecutor开 N 个线程每个线程处理一个域名。但海风 v1.0 在实现时加了一层信号量原因是前面 3.3 节提到的“线程数不等于连接数”——每个域名的查询内部还会拆出 whois、dns、icp 等多个子请求如果只限制线程数网络层的并发连接数会失控。import threading import time from concurrent.futures import ThreadPoolExecutor, as_completed QUERY_SEMAPHORE threading.Semaphore(5) # 全局同时最多 5 个域名查询 def bounded_query(domain: str) - dict: 受信号量保护的查询函数确保并发不会冲破阈值。 with QUERY_SEMAPHORE: return run_single_query(domain) def run_batch(domains: list[str], workers: int 5) - list[dict]: results [] with ThreadPoolExecutor(max_workersworkers) as pool: future_map {pool.submit(bounded_query, d): d for d in domains} for fut in as_completed(future_map): domain future_map[fut] try: results.append(fut.result()) except Exception as exc: results.append({domain: domain, overall_status: error, error: str(exc)}) return resultsSemaphore(5)在这里是全局唯一的一个不是每个线程一个。它的作用是把“正在查询的域名数”和“线程池空闲线程数”剥离开即使线程池有 5 个空闲线程信号量也会卡住第 6 个域名查询直到前面的查询全部完成。run_single_query是海风 v1.0 内部的单域名查询函数它内部还有自己的超时保护两层保护叠加才能避免批量任务被几个慢域名拖死。被限速的感受很典型前面几十个域名查询正常突然连续出现空响应、连接被重置或者 whois 返回的是一段“查询过于频繁”的提示文本。海风 v1.0 的应对策略是退避重试——检测到连续的异常响应后自动暂停 30 秒再继续而不是立刻用下一个域名继续打。这个 30 秒是个经验值太短起不到降温效果太长又拖慢整体进度。我一般会在本地跑一轮测试观察限速出现的频率来微调这个值如果连续两次退避后仍然被限速就把并发往下调一半而不是加退避时间。5. 海风查询避坑指南五个最容易翻车的地方5.1 现象所有 WHOIS 查询全部超时DNS 却正常批量跑到一半发现 whois 相关的字段全是errorDNS 记录却正常能查。用--timeout 15重试也没用换域名也一样。原因出问题的机器或网络环境中43 端口被安全策略拦截了。很多企业网络默认只放行 80、443 等常用端口WHOIS 用的 43 端口在策略里经常被误伤。海风 v1.0 的默认行为是先用 WHOIS失败后自动切 RDAP 走 443 端口但如果查的是非常规后缀RDAP 端点可能没有覆盖导致仍然失败。解决确认当前网络是否放行 43 端口可以在命令行里模拟一次连接测试如果确认是被拦截就给海风设置强制 RDAP 模式让所有 whois 查询都走 443数据完整度会略降但至少能跑通。5.2 现象www 子域名查出来的信息与根域名不一致用海风查www.example.com返回的 whois 注册商和查example.com时不一样甚至注册时间都对不上。原因子域名没有独立的 WHOIS 记录注册局对带www的查询通常会返回父域的信息但有些注册局的响应里会夹带“这是在查子域名”的提示解析器如果没识别出这种提示就会把父域的信息张冠李戴。解决海风 v1.0 内置了注册域提取逻辑用一个公共后缀列表把www、m、api等前缀去掉只对注册域发查询用的时候不要手动输入带子域名的地址让工具自己做归约。如果确实想查子域名的解析记录DNS 部分不受影响whois 部分认准根域名即可。5.3 现象备案接口突然返回验证码页面之前跑得好好的批量任务今天查国内域名时icp字段开始成片地返回UNKNOWN后台日志里显示响应内容是一段带验证码的 HTML。原因备案数据服务商的反爬策略升级了或者请求频率超过了对方的限制。海风 v1.0 的降级链路在这种情况下会自动切到离线库但离线库的更新如果没跟上返回的就是过期数据。解决不要尝试去破解验证码这是死路。正确的做法是检查离线库的更新时间如果超过 24 小时就先更新离线库再继续批量任务如果在线接口持续不可用把--icp开关关掉只查 whois 和 dns备案信息单独用页面查询补漏。这个取舍在批量任务里是常态不要因为一个数据源的故障而停掉整个流水线。5.4 现象中文域名查询结果是一串乱码或直接报错批量清单里放了“例子.中国”查询结果里域名显示成xn--fsqu00a.xn--fiqs8swhois 信息对不上。原因输入文件虽然是 UTF-8 编码但海风 v1.0 在 IDN 转换时遇到了一些不规范写法比如域名里混入了全角冒号、末尾带了句号或者编码标签里已经写了一半的 punycode。解决这是一个典型的输入数据清洗问题批量前先对清单做一轮预处理把全角字符转半角去掉首尾空白已经是 punycode 的就不要重复转换。import idna raw 例子。中国 # 全角句号混入会导致编码失败 cleaned raw.replace(。, .).strip() try: ascii_domain idna.encode(cleaned).decode(ascii) print(转换成功:, ascii_domain) # 输出以 xn-- 开头的 ASCII 域名 except idna.IDNAError as exc: print(转换失败:, exc)idna.encode是 Python 的 IDNA 标准实现clean这一步在批量任务里不要省略因为数据源是业务方手工维护的清单全角符号和多余空格是常态。转换失败时不要静默跳过要把原始输入记录下来批量结束后一起处理。5.5 现象注册时间比过期时间还晚数据明显对不上个别域名的 whois 结果里creation_date显示 2020 年expiration_date却显示 2015 年明显是解析串了。原因不同注册局的响应格式里日期字段的 key 名写法差异很大海风 v1.0 的别名映射表没覆盖到某一种写法解析器退回了正则匹配——而正则匹配习惯性地抓取响应中的第一个日期很容易抓到Updated Date而不是Creation Date。解决遇到这种情况先用query --whois看原始响应文本确认这个注册局用了什么字段名然后检查海风的解析配置里有没有对应的别名没有就手动加一行把该写法映射到正确的标准字段。好消息是这种问题一般只集中在个别注册局修一次之后同类后缀的域名就都正常了。6. 把海风 v1.0 变成常驻小服务HTTP 接口、缓存与冒烟验收6.1 用 Flask 暴露一个查询接口批量工具用着顺手之后自然会想把能力开放给团队其他人用。海风 v1.0 自带的serve子命令就是干这个的它内部用 Flask 起了一个轻量 HTTP 服务。核心逻辑只有十几行from flask import Flask, request, jsonify from haifeng.cli import run_single_query app Flask(__name__) app.route(/api/query) def api_query(): domain request.args.get(domain, ).strip() if not domain: return jsonify({error: missing domain}), 400 try: data run_single_query(domain) return jsonify(data) except Exception as exc: return jsonify({domain: domain, overall_status: error, error: str(exc)}), 500 if __name__ __main__: app.run(host0.0.0.0, port8532)接口设计成 GET 是因为单次查询语义简单参数就是domain。注意run_single_query可能耗时几秒甚至十几秒在 Web 服务里这叫“长请求”如果接口被频繁调用Flask 内置的开发服务器会撑不住生产环境要用 Gunicorn 之类的多进程容器并且加一层超时中断。小团队内部用没问题面向大规模外部开放就要考虑异步任务队列了那是另一个量级的改造。6.2 给结果加一个 10 分钟缓存域名查询的黄金法则是注册信息和备案数据在小时级别内不会变DNS 记录的 TTL 一般在几分钟到几小时。所以 HTTP 服务里最值得做的一层优化就是缓存。海风 v1.0 的serve模式内置了一个简单的 TTL 缓存默认 600 秒。import time from collections import OrderedDict CACHE_TTL 600 # 缓存有效期单位秒 class TTLCache: def __init__(self, ttl: int CACHE_TTL): self.ttl ttl self.store OrderedDict() def get(self, domain: str): item self.store.get(domain) if not item: return None value, expire_at item if time.time() expire_at: del self.store[domain] return None return value def set(self, domain: str, value: dict): self.store[domain] (value, time.time() self.ttl)这个缓存是单进程内存级的代码里没有清理过期键的显式逻辑靠 get 时懒过期。小规模场景够用但要注意两点第一缓存 key 只用了域名没有区分查询参数所以--dns开关不同但域名相同会串结果实践中要么 key 带上参数标识要么保持查询参数统一第二DNS 记录变化频率高缓存只适合 whois 和 icpDNS 部分尊重记录的 TTL 更稳妥。海风 v1.0 的取舍是简单化处理——全部缓存 10 分钟在资产盘点场景里这个误差可以接受。6.3 上生产前的冒烟验收清单把服务部署到服务器之前我会跑一组固定用例做验收五个用例覆盖主要分支一个正常解析的域名验证主链路一个不存在的域名验证 NXDOMAIN 分支一个中文域名验证 IDN 转换链路一个只有 whois 没有备案的境外域名验证降级逻辑一个乱输入如not a domain验证参数校验。五个用例全部返回预期状态码和字段才算通过。我自己的教训是第一次做这种工具时跳过了验收直接跑全量结果 200 个域名里有 30 多个因为输入里混了 URL 前缀而全部报错浪费了整整一个晚上。从那以后再小的工具上线我也会先跑冒烟用例五条命令五分钟换来的是批量任务不返工。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?