
简介这是一套面向网站开发者与运维人员的域名WHOIS信息查询源码基于PHP实现可部署于自有服务器用于实时查询域名注册商、到期时间、域名服务器等核心注册信息弥补第三方平台在定制化查询体验上的局限。压缩包共包含19个文件整体大小约290KB以PHP业务逻辑文件为核心配合HTML页面结构、CSS样式、JS交互以及woff/ttf/eot/svg字体与png/gif/ico图标素材覆盖从查询入口到结果展示的完整链路目录划分清晰便于直接修改部署。代码同时包含logo、404页面等细节适合作为学习PHP表单处理、API请求与结果渲染的参考样例。已有278人浏览学习对于需要快速搭建WHOIS查询工具或了解域名信息查询实现原理的开发者而言这套轻量源码具有直接的参考价值。1. 域名信息查询同款WHOIS源码这套打包好的代码到底能拿来做什么拿到一个名为“域名信息查询同款WHOIS源码.zip”的压缩包第一反应别急着解压。先想清楚WHOIS 本身不是什么新鲜技术它是一个运行了三十多年的查询协议所有域名注册商、备案查询站、安全分析平台背后都在用同一套底层的查询-响应机制。所谓“同款源码”拆开看通常就是三类东西的组合——连接 WHOIS 服务器的客户端、解析各国注册局返回文本的规则表、以及一个让人能输入域名就看到注册人、到期时间、状态码的查询页面。它能不能用、值不值得投入取决于你拿到的是哪类源码以及你打算把它放在什么场景里跑。这篇笔记我会把原理讲透再按最常见的工程结构从零把一套可用的 WHOIS 查询服务搭出来并把解析、缓存、避坑这些真正决定项目生死的细节一次说清。2. 搞清楚 WHOIS 查询到底查了什么协议、端口与服务器分配2.1 WHOIS 不是 Web 接口是 43 端口上的纯文本对话很多第一次接触的人会误以为 WHOIS 查询是调某个 HTTP API实际上传统 WHOIS 走的是 TCP 43 端口客户端连上服务器后发送一行域名文本服务器直接把结果以纯文本形式吐回来然后断开连接。这个流程在 RFC 3912 里定义得很简单请求就是一行 ASCII 字符串加回车换行响应是一段没有统一格式的文本。所有“域名信息查询”工具的本质都是把这段不可控的文本解析成结构化的字段。用 Python 写一个最小查询客户端不超过十行核心就是 socket 连接加收发数据import socket def whois_lookup(domain: str, server: str whois.verisign-grs.com, port: int 43) - str: # 创建 TCP 连接WHOIS 协议默认端口是 43 with socket.create_connection((server, port), timeout10) as sock: # 按协议要求发送域名加换行编码必须是 ASCII sock.sendall((domain \r\n).encode(ascii)) buf [] while True: # 循环收数据直到服务器关闭连接为止 chunk sock.recv(4096) if not chunk: break buf.append(chunk.decode(iso-8859-1, errorsreplace)) return .join(buf) if __name__ __main__: print(whois_lookup(example.com))这段代码的逻辑很直白建立连接、按协议发送查询行、循环收取直到对方断开。这里有个容易被忽略的参数——decode(iso-8859-1)WHOIS 响应的历史包袱很重很多海外注册局返回的内容在纯 ASCII 之外还带 Latin-1 字符用 UTF-8 解码很可能直接抛异常。实际项目中我会把解码方式也做成可配置项因为你没法预测上游服务器今天用哪种编码。2.2 域名和 IP 的 WHOIS 服务器不是同一套查谁得先知道该问谁“同款源码”里最核心的隐藏逻辑就是服务器选择。域名注册数据分散在不同注册局手里每个注册局有自己的 WHOIS 服务器而且注册局之间的数据还会往下游批发商分发。比如.com和.net的权威数据在 Verisign.org在 PIR.cn在 CNNIC欧洲的.eu在 EURid。IP 地址段则归五个区域互联网注册机构管——ARIN 管北美、RIPE 管欧洲、APNIC 管亚太你要查一个 IP 归属得先从 IANA 的 whois.iana.org 问出该 IP 属于哪个 RIR再转向对应的服务器。常见的做法是在源码里维护一张“后缀到服务器”的映射表查不到时再走 IANA 自动跳转。表格接在下面查询对象默认 WHOIS 服务器说明.com / .netwhois.verisign-grs.comVerisign 运营数据不含隐私字段.orgwhois.pir.orgPublic Interest Registry.cnwhois.cnnic.cnCNNIC 中文域名与 .cn 域名IP 地址段whois.iana.org 起跳先查 RIR 归属再转查.iowhois.nic.ioIdentity Digital 运营需要特别提醒的是即使查询的是 .com也不一定能从 Verisign 直接拿到完整注册人信息。很多注册商对 WHOIS 输出做了隐私保护替换返回结果里可能只有“REDACTED FOR PRIVACY”这类占位符。源码里如果只有单一服务器地址不做上游递归或备选切换那查询质量就会打折。2.3 命令行先跑通再动代码用系统自带工具验证网络链路的连通性在接进自己代码之前先用系统自带的 whois 命令验证目标服务器通不通。很多部署环境根本装不上 whois 客户端或者 43 端口被安全组挡着这些问题在命令行阶段暴露出来比在源码里排查快得多。# Linux/macOS 下直接查 example.org看系统带的工具能否拿到原始返回 whois example.org | head -50 # 如果没装用 apt 安装Debian/Ubuntu装的是 jwhois 或 whois 包 sudo apt-get install whois # 用 curl 也能模拟 WHOIS 请求这是最轻量的拨测方法 curl -s --max-time 10 telnet://whois.verisign-grs.com:43 example.com上面三条命令是三层排查思路第一条验证本机有没有客户端第二条解决安装问题第三条绕开系统客户端直接用 curl 模拟协议。如果第三条能返回结果而代码跑不通说明源码里 socket 收发逻辑有问题如果第三条也超时那就是网络层的问题——检查安全组是否放行 43 端口以及本机防火墙出方向策略。这个排错顺序能替你省下一大半无意义的代码调试时间。3. 把源码跑起来工程结构与三个必调参数3.1 一个典型的 WHOIS 源码包结构拆解解压“同款源码.zip”后你会看到几个常见目录不管它用的是 PHP 还是 Python骨架基本一致入口脚本index.php / main.py / app.js负责接收域名输入、调用查询模块、把结果渲染成页面。连接模块whois_client.py / whois.php封装 socket 或 fsockopen 逻辑负责连接服务器、收发数据、处理超时。解析规则目录parsers/里面按注册局放了一堆正则或规则文件这是整个包里最值钱的部分。缓存层cache/ 或 Redis 配置用于减少重复查询、降低被封号风险。前端模板templates/查询表单和结果展示页面。拿到源码我建议按这个顺序做三件事先把入口脚本连到数据库的地方注释掉看纯查询能不能跑再检查连接模块里的服务器映射表是否完整最后看解析规则文件是否带默认兜底规则因为 WHOIS 返回格式不统一缺兜底规则就意味着随时解析失败。任何“同款源码”如果这三样不全跑起来的质量都不会高。3.2 最小可用部署本地起一个纯查询脚本定位到源码包里负责查询的核心函数先绕开 Web 框架直接跑命令行版本这样能把网络、协议、解析三者隔离出来单独验证。以 Python 为例修改过的最小验证脚本是这样的import json import sys from whois_client import WHOISClient # 假设这是包里的连接模块 from parsers import get_parser # 这是解析规则加载器 def query_and_parse(domain: str) - dict: # 初始化客户端参数依次是超时、重试次数、缓存开关 client WHOISClient(timeout10, retries2, use_cacheFalse) raw_text client.lookup(domain) if not raw_text: return {error: empty response} # 根据域名后缀选择合适的解析器找不到就用默认兜底解析器 tld domain.rsplit(., 1)[-1] parser get_parser(tld) or get_parser(_default) return parser.parse(raw_text) if __name__ __main__: domain sys.argv[1] if len(sys.argv) 1 else example.com print(json.dumps(query_and_parse(domain), ensure_asciiFalse, indent2))要注意这里的get_parser(tld)是典型的规则分发表它对 .com、.cn、.org 各返回一个专用解析器谁都不匹配时走_default。专用解析器里的是带锚点、考虑过多行的正则在提取而默认解析器通常只是粗暴地按冒号切分。这两个参数——timeout和retries——是部署环境里最先要调的。本地网络通畅时 10 秒超时足够但上游部分注册局响应极慢尤其是在跨境网络环境下建议超时放宽到 20 秒重试次数不要大于 2 次否则大量无效请求会让你的出口 IP 很快被临时限速。3.3 接进 Web 页面前先把返回结果做一次“人工核验”把源码接到网页之前建议先对同几个域名做一次人肉比对。所谓人肉比对就是打开命令行 whois 工具查询一次原始输出再和源码解析出来的字段逐项对照。这一步能发现多个问题解析规则匹配不到导致的字段缺失、把注册商名称错配到注册人字段、状态码被正则吃掉一半等等。我在实际项目里吃过一次大亏某个 .cn 域名的解析规则对“Domain Status: ok”这种行只配了半条正则导致状态字段只匹配出o而不是ok前端页面直接显示“域名状态异常”。这种问题光看单元测试根本暴露不出来因为模拟样本里没覆盖这一行格式。所以凡是涉及 WHOIS 解析的源码第一课就是不要相信任何一条拿真实域名跑不出正确结果的解析规则。4. 解析 WHOIS 返回文本正则之外还需要一张规则表4.1 为什么不能只靠正则注册局之间的格式差异远超想象WHOIS 协议没规定响应内容的格式这是所有“域名信息查询源码”最痛的点。Verisign 返回的 .com 记录里有“Registrar: GoDaddy.com, LLC”和“Registry Expiry Date: 2026-08-15T04:00:00Z”CNNIC 返回的 .cn 记录格式完全不同字段名是“注册商”和“过期时间”中文和英文混着来。某些欧洲注册局返回的字段名是首字母大写的“Registrant Name”另一些是大小写敏感的“registrant_name”。拿正则去覆盖每一种格式是不现实的所以工程上的做法是分三层按 TLD 找解析器、按解析器内定义的关键字段正则去匹配、最后对完全匹配不上的字段做启发式猜测或留空。源码里真正值钱的部分不是连接逻辑那是几十行的事而是那张覆盖了数百个后缀的规则表。规则表不是一次性写出来的是靠一条条真实返回样本喂出来的。我封装过一个简化的解析器核心是用正则加显式兜底逻辑import re class GenericWhoisParser: def __init__(self): # 规则表字段名 - 正则表达式 self.field_patterns { registrar: re.compile(rRegistrar:\s*(.)), expiry_date: re.compile(rRegistry Expiry Date:\s*(.)), creation_date: re.compile(rCreation Date:\s*(.)), status: re.compile(rDomain Status:\s*(.)), } self._default_pattern re.compile(r^\s*([^:]):\s*(.)$) def parse(self, raw_text: str) - dict: result {} for line in raw_text.splitlines(): matched False # 逐条规则尝试匹配命中即写入 for field, pattern in self.field_patterns.items(): m pattern.search(line) if m: result[field] m.group(1).strip() matched True break if not matched: # 兜底规则按冒号切分存入 raw 字段 m self._default_pattern.match(line) if m and m.group(2).strip(): result.setdefault(raw_fields, {})[m.group(1).strip()] m.group(2).strip() return result这段代码里的参数关键点有两个field_patterns里正则的贪婪和锚点选择。(.)能捕获行尾所有内容但如果注册局在同一字段上返回多行比如多个 Domain Status这种单行匹配就会漏掉后面几行。遇到过多个状态的域名时需要把search改成findall并把 result 存成列表。兜底逻辑保证了未知格式的字段至少不会丢会出现在raw_fields里供后续人工分析而不是直接被静默丢弃——这个设计是从生产环境里沉淀出来的血泪经验。4.2 解析后的数据结构分字段存还是存原始文本决定你后面能不能查很多“同款源码”只做了解析展示没做数据落库。这导致同一个域名查第二次又得全量走网络效率极低。我建议在源码基础上加一个缓存表至少存三个关键字段域名、到期时间、原始返回文本。结构参考下面列名类型用途domainvarchar(255) 主键查询主键确保同一域名只存一条registrarvarchar(255)注册商用于展示expiry_datedatetime到期时间用于过期提醒raw_textmediumtext原始返回解析器升级后可重放updated_attimestamp最后查询时间控制刷新频率将原始文本也存下来非常关键当解析规则出 Bug 时不需要重新发起网络查询直接从库里捞raw_text回放就能验证新正则。没有这个字段每次调试都要打一次上游既慢又容易被限速。4.3 隐私保护字段拿不到真实注册人信息时怎么展示不误导用户近年各大注册局和注册商普遍启用 WHOIS 隐私保护返回结果里真实注册人信息被替换成代理邮箱或占位符。源码解析完如果发现 Registrant Name 是 “REDACTED FOR PRIVACY” 或 “DATA NOT DISCLOSED”前端要明确展示“该域名开启隐私保护”而不是让用户以为注册人就叫“REDACTED”。做这条消息过滤时不要用简单的等值判断因为各注册商的占位符文案五花八门有全大写的、有带下划线的、还有PrivacyGuard.org之类的代理名称。稳妥做法是维护一个隐私标识词表命中就标记privacy_protectedtrue。这也是解析模块里少数容易写但又极其影响结果可信度的逻辑之一。5. 域名信息查询的五个常见坑从连不通到解析错逐一排查5.1 端口 43 被安全组拦截客户端一直超时现象本地命令行能查出结果一上服务器就超时或机房部署后永远报错“连接失败”。原因当前机器的安全组出站规则禁了非 80/443 端口。43 端口不在常规放行列表里云厂商默认安全组大概率不放行。解决在云控制台安全组出站方向放行 TCP 43 端口目标地址写 0.0.0.0/0WHOIS 服务器分散在全球各地无法按 IP 白名单限制。改完等 30 秒生效再用 curl 拨测一次复验。5.2 上游 WHOIS 服务器限速查询一多就报 429 或返回空现象连查几十个域名后所有请求开始超时或返回一条错误信息。多数注册局有频率限制策略检测到一段时间内来自同一 IP 的密集查询会静默丢弃请求或直接断开连接。原因客户端没做查询频率控制或者缓存失效太快导致重复查询打到上游。解决一是在连接模块里加最小请求间隔同一个 IP 每秒最多发一两个查询二是把 Redis 或数据库缓存时长设为 24 小时以上域名注册信息不是实时变化的几小时级的延迟完全不影响用户体验。5.3 .cn 返回的中文注册商信息被错误解码成乱码现象解析 .cn 域名时注册商和联系人显示成注册商一类乱码。原因CNNIC 的 WHOIS 返回是 GBK/GB2312 编码源码里统一按 Latin-1 或 UTF-8 解码必然错乱。解决在连接模块里按服务器域名区分解码方式——连接 whois.cnnic.cn 时用gb18030解码其他服务器用iso-8859-1。判断方式别硬编码建议把编码格式直接做成服务器映射表里的一列后续遇到其他非 ASCII 注册局只用加表不用改代码。5.4 一批域名全解析成 None但原始返回里有字段现象解析结果全是空值把raw_text拉出来看字段其实都在。原因解析规则的正则写得太严比如要求Creation Date:冒号后必须有一个空格而实际上某些注册局返回的是制表符或多个空格。解决把解析正则改成更宽容的写法例如Creation Date:\s*而不是Creation Date:。同时在解析完跑一遍自检如果expiry_date为空但raw_fields里存在疑似日期的值触发告警日志。生产环境里我会把这个告警接进错误监控它比单元测试更能反映真实数据质量。5.5 查询的是子域名拿到的却是注册局默认错误页现象用户输入www.example.com查 WHOIS返回的结果是“Domain not found”。原因WHOIS 协议查询只支持主域名服务器不识别www前缀。源码里没有对输入做归一化处理直接把整串域名发给了上游。解决在入口处做域名提取逻辑——从输入里剥掉www.以及可能的子域名前缀。注意不能简单用split(.)[-2:]因为.com.cn、.org.uk这类多段后缀会导致误切。正确做法是拿一份公共后缀列表类似 publicsuffix.org 的规则做最长匹配提取出真正的主域名。这个坑看着小实际却是用户投诉最多的问题之一。6. 验证与进阶用真实数据做回归测试再给查询服务加一层监控源码跑通只是第一步真正要投入生产环境前你得有自己的验证手段。我的习惯是建立一个固定域名样本集每个样本都标注了期望解析结果跑任何修改后都必须通过这批样本域名期望解析结果特殊关注点example.comregistrar 非空、状态含 ok常规场隐私保护域名如任意一个开启保护的 .comprivacy_protectedtrue占位符识别中文域名xn--开头的 punycode 形式不报错、能找到注册局编码与 IDN 处理不存在的域名如 nonexistent-xyz-12345.com错误信息可读错误分支处理把这批用例放进命令行跑一遍比对解析 JSON 的差异比启动 Web 页面点按钮高效得多。只有这批样本全部通过后我才会接回 Web 页面做手动确认。监控方面我会在源码里加两个指标查询成功率和解析成功率。前者看网络链路后者看规则质量。任何低于 99% 的情况都值得立刻查日志——解析成功率掉到 95% 以下通常意味着某家注册局改版了返回格式你的正则规则需要更新了。WHOIS 解析是一个长尾问题永远有新后缀、新格式、新隐私策略冒出来代码稳定运行三个月后还会给你“惊喜”。所以从第一天起就把raw_text落库、把解析失败样本单独留一份、把异常告警接出来是我这些年做域名信息查询方向最值得推荐的习惯。你踩过的每一个非标准格式都会成为下一版规则表的注脚。希望这些经验能帮你在第一批域名解析失败时少走两趟弯路。本文还有配套的精品资源点击获取