如何用Python获取设备主机名等信息,跳过或仅读取一次/etc/hosts
解决Python获取主机信息时跳过/缓存/etc/hosts的问题
针对你提到的用socket.gethostbyaddr()每次都会读取/etc/hosts的痛点,我整理了两个靠谱的方案,都用Python标准库就能实现,不需要额外装依赖:
方案一:直接跳过/etc/hosts,仅执行DNS查询
如果想完全绕开/etc/hosts直接查DNS,我们可以自己用socket实现一个简单的DNS查询逻辑,这样就不会触发系统对hosts文件的读取。以下是一个基础的A记录查询实现,还能结合反向解析获取主机名、别名等信息:
import socket import struct def dns_query(hostname, dns_server="8.8.8.8", port=53): # 构建DNS请求头部 request_id = 1234 # 随机生成请求ID也可以 flags = 0x0100 # 标准查询+递归请求 header = struct.pack(">HHHHHH", request_id, flags, 1, 0, 0, 0) # 打包查询域名 qname = b"" for part in hostname.split("."): qname += struct.pack("B", len(part)) + part.encode() qname += b"\x00" # 域名结束标记 qtype = struct.pack(">H", 1) # A记录类型 qclass = struct.pack(">H", 1) # 互联网类 query = header + qname + qtype + qclass # 发送UDP请求到DNS服务器 with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.sendto(query, (dns_server, port)) response, _ = sock.recvfrom(512) # 解析响应提取IP地址(简化处理非压缩格式) ptr = 12 # 跳过头部12字节 # 跳过查询部分 while response[ptr] != 0: ptr += 1 + response[ptr] ptr += 5 # 提取所有A记录IP ancount = struct.unpack(">H", response[6:8])[0] ips = [] for _ in range(ancount): while response[ptr] != 0: ptr += 1 + response[ptr] ptr += 1 rtype = struct.unpack(">H", response[ptr:ptr+2])[0] ptr += 10 if rtype == 1: ip = socket.inet_ntoa(response[ptr:ptr+4]) ips.append(ip) ptr += 4 # 反向解析获取主机名等信息 if ips: try: hostname, aliases, canonical_name = socket.gethostbyaddr(ips[0]) return (hostname, aliases, canonical_name, ips) except socket.herror: return (hostname, [], [], ips) return (None, [], [], []) # 使用示例 result = dns_query("example.com") print(f"主机名: {result[0]}") print(f"别名: {result[1]}") print(f"规范名: {result[2]}") print(f"IP地址: {result[3]}")
这个方法直接和DNS服务器通信,完全不依赖/etc/hosts。如果需要支持更多DNS类型(比如AAAA)或压缩格式,还可以扩展逻辑,但基础版本足够满足日常需求。
方案二:缓存/etc/hosts内容,自行处理解析逻辑
如果想自己控制/etc/hosts的读取(只读一次缓存),同时结合DNS查询,可以先把hosts文件内容加载到内存缓存,查询时优先查缓存,缓存没命中再走DNS:
import socket import os class HostsCache: def __init__(self): self.ip_to_info = {} self.name_to_ip = {} self._load_hosts() def _load_hosts(self): hosts_path = "/etc/hosts" if not os.path.exists(hosts_path): return with open(hosts_path, "r") as f: for line in f: line = line.strip() if not line or line.startswith("#"): continue parts = line.split() if len(parts) < 2: continue ip = parts[0] names = parts[1:] # 缓存IP对应的主机信息 self.ip_to_info[ip] = { "hostname": names[0], "aliases": names[1:], "canonical": names[0] } # 缓存域名到IP的映射 for name in names: self.name_to_ip[name] = ip def get_host_info(self, target): # 先查本地缓存 if target in self.ip_to_info: info = self.ip_to_info[target] return (info["hostname"], info["aliases"], info["canonical"], [target]) if target in self.name_to_ip: return self.get_host_info(self.name_to_ip[target]) # 缓存未命中,走DNS查询 try: if "." in target: # 正向解析域名 hostname, aliases, ips = socket.gethostbyname_ex(target) # 尝试反向解析规范名 try: canonical_name, _, _ = socket.gethostbyaddr(ips[0]) except socket.herror: canonical_name = hostname return (hostname, aliases, canonical_name, ips) else: # 反向解析主机名/IP hostname, aliases, canonical_name = socket.gethostbyaddr(target) ips = [socket.gethostbyname(hostname)] return (hostname, aliases, canonical_name, ips) except socket.herror: return (None, [], [], []) # 使用示例 hosts_cache = HostsCache() # 初始化时仅读取一次/etc/hosts result = hosts_cache.get_host_info("localhost") print(f"主机名: {result[0]}") print(f"别名: {result[1]}") print(f"规范名: {result[2]}") print(f"IP地址: {result[3]}") result = hosts_cache.get_host_info("example.com") print(f"主机名: {result[0]}") print(f"别名: {result[1]}") print(f"规范名: {result[2]}") print(f"IP地址: {result[3]}")
这个方案的优势是:
/etc/hosts仅在初始化时读取一次,避免重复IO操作- 优先使用本地配置,同时兼容DNS远程解析,灵活性更高
额外小技巧:系统层面修改解析行为
如果你想让整个Python进程的socket调用都跳过/etc/hosts,可以设置RES_OPTIONS环境变量,但这个方法是全局的,且依赖系统resolv.conf支持,跨平台性一般,谨慎使用:
import os import socket # 让解析器跳过hosts文件 os.environ['RES_OPTIONS'] = 'nohosts' # 重新初始化解析器(部分系统需要) socket.getaddrinfo('localhost', 80) # 此时调用socket.gethostbyaddr会直接查DNS hostname, aliases, canonical_name = socket.gethostbyaddr('example.com') print(hostname, aliases, canonical_name)
内容的提问来源于stack exchange,提问作者Jonathan Allen Grant
相关产品推荐
相关产品推荐

