Python · 4 分钟阅读
`dnspython`:DNS 处理库
目录
- 1. 安装
- 2.
dns.resolver.resolve():核心查询 API - 3. 常见记录类型
- 4. 查询示例
- 5. 实战:DNS 轮询业务监控
- 6. 自定义 nameserver
- 7. 常见问题
- 8. 小结
dnspython 是 Python 的 DNS 工具包,几乎支持所有记录类型,可用于查询、
zone 传输、动态更新、TSIG 签名、EDNS、结果校验等。它能在脚本里替代
nslookup / dig,并和现有平台集成。
详细实战:检查 URL 是否可访问。
1. 安装
pip install -U dnspython
import dns.resolver
print(dns.__version__) # 检查安装
2. dns.resolver.resolve():核心查询 API
重要:
dns.resolver.query()在 dnspython 2.0 已弃用,3.0 删除了。
请使用dns.resolver.resolve()。
import dns.resolver
# 语法
dns.resolver.resolve(qname, rdtype, rdclass, raise_on_no_answer=True, ...)
| 参数 | 含义 |
|---|---|
qname |
待查的域名 |
rdtype |
资源记录类型(A / MX / NS / CNAME / PTR …) |
rdclass |
网络类,常用 dns.rdataclass.IN |
tcp |
是否强制 TCP |
source / source_port |
自定义查询源 |
raise_on_no_answer |
无应答时是否抛异常 |
lifetime |
超时时间(秒) |
3. 常见记录类型
| 类型 | 含义 |
|---|---|
A |
域名 → IPv4 |
AAAA |
域名 → IPv6 |
MX |
邮件交换记录 |
NS |
权威域名服务器 |
CNAME |
别名 |
PTR |
IP → 域名(反向解析) |
TXT |
SPF / DKIM / 域名验证 |
SOA |
起始授权机构 |
4. 查询示例
4.1 A 记录
import dns.resolver
domain = "www.google.com"
try:
answers = dns.resolver.resolve(domain, "A")
for rdata in answers:
print(rdata.to_text())
except dns.resolver.NXDOMAIN:
print("no such domain")
except dns.resolver.NoAnswer:
print("no A record")
except dns.resolver.Timeout:
print("dns query timeout")
A 记录只在最终目标是 IP 时才返回;如果域是
CNAME形式,需要先查
CNAME,或在配置里启用search。
4.2 MX 记录
answers = dns.resolver.resolve("163.com", "MX")
for rdata in answers:
print(f"preference={rdata.preference}, exchange={rdata.exchange}")
4.3 NS 记录
answers = dns.resolver.resolve("baidu.com", "NS")
for rdata in answers:
print(rdata.to_text())
4.4 CNAME
answers = dns.resolver.resolve("www.baidu.com", "CNAME")
for rdata in answers:
print(rdata.to_text())
4.5 反向解析(PTR)
import dns.reversename
addr = "8.8.8.8"
rev = dns.reversename.from_address(addr)
answers = dns.resolver.resolve(rev, "PTR")
for rdata in answers:
print(rdata.to_text())
5. 实战:DNS 轮询业务监控
背景:很多网站一个域名对应多个 IP(DNS 轮询),用来做简单负载均衡。
但若某个 IP 不可用,DNS 不会自动剔除。本节写一个“解析 + 主动探测”脚本。
"""DNS 轮询监控:解析 + HTTP 探活。"""
import socket
from dataclasses import dataclass
from typing import Iterable
import dns.resolver
import requests
@dataclass
class HealthResult:
ip: str
ok: bool
status: int | None
error: str | None = None
def resolve_a(domain: str) -> list[str]:
"""返回 A 记录里的所有 IP。"""
try:
answers = dns.resolver.resolve(domain, "A", lifetime=5)
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout) as e:
raise RuntimeError(f"DNS 解析失败:{e}") from e
return [r.to_text() for r in answers]
def check_http(ip: str, host: str, port: int = 80, timeout: float = 5.0) -> HealthResult:
"""对单个 IP 做 HTTP 探活。"""
try:
resp = requests.get(
f"http://{ip}:{port}/",
headers={"Host": host},
timeout=timeout,
allow_redirects=False,
)
return HealthResult(ip=ip, ok=resp.ok, status=resp.status_code)
except requests.RequestException as e:
return HealthResult(ip=ip, ok=False, status=None, error=str(e))
def monitor(domain: str) -> Iterable[HealthResult]:
ips = resolve_a(domain)
for ip in ips:
yield check_http(ip, host=domain)
if __name__ == "__main__":
for r in monitor("www.example.com"):
print(r)
工业级监控还要加:告警去重、告警通知(邮件/钉钉)、指标打点、
持续时间窗口判定、IPv6 fallback 等——参见
业务服务监控。
6. 自定义 nameserver
默认走系统 DNS。要走自建的 8.8.8.8 或内网 DNS:
import dns.resolver
resolver = dns.resolver.Resolver()
resolver.nameservers = ["8.8.8.8", "1.1.1.1"]
answers = resolver.resolve("example.com", "A")
或者使用环境变量:
export DNS_RESOLVER=8.8.8.8
7. 常见问题
NoAnswer怎么办? 域名存在但没有这种记录。检查rdtype写对没。NXDOMAIN:域名根本不存在。Timeout:网络不通或被防火墙拦截。检查lifetime/ 防火墙 /nameservers。CNAME链上跑 A 失败? 用dns.resolver.resolve配合search=True(默认),
库会顺着 CNAME 链走到底再查 A。- 需要 EDNS / TCP?
dns.resolver.resolve(qname, rdtype, use_edns=0, tcp=True)。 - API 名称变化:见到老代码里的
query()请替换为resolve(),否则
dnspython 2.0+ 会抛AttributeError。
8. 小结
dns.resolver.resolve()是核心 API;query已删除。- 支持几乎所有 DNS 记录类型,能直接替代
dig/nslookup。 - DNS 轮询监控思路:解析所有 IP → 对每个 IP 探活 → 报告异常。
- 在大型项目里给
Resolver配好自定义nameservers与lifetime超时。