1. 项目概述:Python获取网卡信息的实用场景
在日常开发中,获取本机网卡信息是个看似简单却经常遇到的需求。比如最近我需要写一个自动化运维工具,需要自动识别服务器上的所有网络接口并检查其配置状态。刚开始我以为用简单的ifconfig或ipconfig命令就能解决,但实际需求往往更复杂——需要同时获取IPv4/IPv6地址、MAC地址、接口状态等信息,并且要跨平台兼容Windows、Linux和macOS。
Python作为自动化脚本的首选语言,完全能满足这个需求。通过标准库和少量第三方库,我们可以用不到50行代码实现一个健壮的网卡信息采集工具。这个工具可以用于:
- 服务器自动化运维检查
- 网络设备管理后台
- 本地开发环境配置检查
- 网络故障排查辅助工具
注意:本文方法仅获取本地网卡信息,不涉及任何网络探测或远程主机信息收集,符合安全规范。
2. 核心实现方案对比
2.1 方案选型:为什么选择psutil+netifaces组合?
经过多个项目的实践验证,我最终选择了psutil+netifaces的组合方案。这个选择基于以下对比测试:
| 方法 | 跨平台性 | 获取IP类型 | 获取MAC | 需要权限 | 代码复杂度 |
|---|---|---|---|---|---|
| os.popen+ifconfig | 差 | 全 | 是 | 低 | 高(需解析) |
| socket.gethostname | 中 | 仅IPv4 | 否 | 低 | 低 |
| netifaces | 优 | 全 | 是 | 低 | 中 |
| psutil | 优 | 全 | 否 | 低 | 低 |
| 组合方案 | 优 | 全 | 是 | 低 | 中 |
单独使用netifaces虽然功能全面,但无法直接获取网卡友好名称;而psutil可以补足这个缺陷。两者组合后,既保持了代码简洁性,又获得了最完整的网卡信息。
2.2 环境准备与依赖安装
推荐使用Python 3.8+环境,先安装必要依赖:
pip install psutil netifaces --upgrade如果是内网环境,可以使用阿里云镜像加速:
pip install -i https://mirrors.aliyun.com/pypi/simple/ psutil netifaces3. 完整实现代码解析
3.1 获取所有网络接口
首先通过psutil获取网卡基础信息:
import psutil import netifaces def get_adapters(): """获取所有网络接口的适配器信息""" adapters = [] for name, addrs in psutil.net_if_addrs().items(): adapter = { 'name': name, 'ipv4': [], 'ipv6': [], 'mac': None } # 处理每个地址 for addr in addrs: if addr.family == psutil.AF_LINK: adapter['mac'] = addr.address elif addr.family == psutil.AF_INET: adapter['ipv4'].append({ 'address': addr.address, 'netmask': addr.netmask, 'broadcast': addr.broadcast }) elif addr.family == psutil.AF_INET6: adapter['ipv6'].append({ 'address': addr.address, 'netmask': addr.netmask }) adapters.append(adapter) return adapters这段代码已经可以获取大部分信息,但发现两个问题:
- 某些虚拟网卡的MAC地址可能显示为00-00-00-00-00-00
- Docker创建的虚拟接口会混杂在结果中
3.2 增强版实现:过滤与补充信息
改进后的版本增加了有效性检查和网关信息获取:
def get_enhanced_adapters(): adapters = [] stats = psutil.net_if_stats() for name, addrs in psutil.net_if_addrs().items(): # 跳过非活跃接口 if name not in stats or not stats[name].isup: continue adapter = { 'name': name, 'ipv4': [], 'ipv6': [], 'mac': None, 'gateway': None, 'isup': stats[name].isup, 'speed': stats[name].speed } # 处理MAC地址 mac_addrs = [addr.address for addr in addrs if addr.family == psutil.AF_LINK] if mac_addrs and not mac_addrs[0].startswith('00-00-00'): adapter['mac'] = mac_addrs[0] # 处理IP地址 for addr in addrs: if addr.family == psutil.AF_INET: adapter['ipv4'].append({ 'address': addr.address, 'netmask': addr.netmask, 'broadcast': addr.broadcast }) elif addr.family == psutil.AF_INET6: # 跳过本地IPv6地址 if not addr.address.startswith('fe80'): adapter['ipv6'].append({ 'address': addr.address, 'netmask': addr.netmask }) # 获取网关信息 try: gws = netifaces.gateways() adapter['gateway'] = gws.get(netifaces.AF_INET, [{}])[0].get('gateway') except Exception: pass adapters.append(adapter) return adapters4. 平台差异处理与常见问题
4.1 Windows系统特殊处理
在Windows平台上需要注意:
- 网卡名称可能包含中文和特殊字符
- 虚拟网卡(如VMware)需要特别过滤
- 某些企业环境MAC地址可能被隐藏
改进的Windows处理代码:
if os.name == 'nt': def is_real_adapter(name): exclude = ('VirtualBox', 'VMware', 'Bluetooth', 'Loopback') return not any(x in name for x in exclude) adapters = [a for a in adapters if is_real_adapter(a['name'])]4.2 Linux容器环境处理
在Docker等容器环境中:
def is_container_env(): return os.path.exists('/.dockerenv') or os.path.exists('/run/.containerenv') if is_container_env(): # 只获取eth0等主接口 adapters = [a for a in adapters if a['name'] in ('eth0', 'eth1', 'ens33')]5. 实用扩展功能
5.1 网络连接状态检测
结合socket检测网络连通性:
import socket def check_connection(adapter, timeout=2): """检查网络连接状态""" if not adapter['ipv4']: return False try: socket.create_connection(('8.8.8.8', 53), timeout=timeout) return True except: return False5.2 信息格式化输出
生成易读的报告:
def format_report(adapters): report = [] for adapter in adapters: line = [ f"接口: {adapter['name']}", f"状态: {'已连接' if adapter['isup'] else '断开'}", f"MAC: {adapter['mac'] or '无'}", f"IPv4: {', '.join(ip['address'] for ip in adapter['ipv4'])}", f"网关: {adapter['gateway'] or '无'}" ] report.append("\n".join(line)) return "\n\n".join(report)6. 性能优化与生产环境建议
- 缓存机制:网卡信息不常变化,可以添加缓存
from functools import lru_cache @lru_cache(maxsize=1, ttl=60) def get_cached_adapters(): return get_enhanced_adapters()- 异常处理增强:处理权限不足等情况
try: adapters = get_enhanced_adapters() except PermissionError: print("需要管理员权限获取完整网络信息") adapters = get_basic_adapters() # 降级方案- 日志记录:记录网卡变化情况
import logging logging.basicConfig(filename='network.log', level=logging.INFO) def log_network_changes(): current = {a['name']: a for a in get_enhanced_adapters()} if hasattr(log_network_changes, 'last_state'): for name, adapter in current.items(): if name not in log_network_changes.last_state: logging.info(f"新增接口: {name}") elif adapter != log_network_changes.last_state[name]: logging.info(f"接口变更: {name}") log_network_changes.last_state = current7. 实际应用案例
7.1 服务器健康检查脚本
def server_health_check(): adapters = get_enhanced_adapters() issues = [] for adapter in adapters: if not adapter['isup']: issues.append(f"{adapter['name']} 接口未启用") continue if adapter['name'].startswith('eth') and not adapter['ipv4']: issues.append(f"{adapter['name']} 未配置IPv4地址") if adapter['gateway'] and not check_connection(adapter): issues.append(f"{adapter['name']} 网关不可达") if issues: send_alert("\n".join(issues)) else: print("所有网络接口正常")7.2 多网卡绑定检测
def check_bonding(): adapters = get_enhanced_adapters() bond_adapters = [a for a in adapters if 'bond' in a['name'].lower()] if not bond_adapters: print("未检测到网卡绑定配置") return for bond in bond_adapters: slaves = [a for a in adapters if a['mac'] == bond['mac'] and a != bond] print(f"绑定接口 {bond['name']} 包含 {len(slaves)} 个从接口")8. 安全注意事项
- 敏感信息处理:MAC地址属于设备唯一标识符,在日志或输出中应考虑脱敏
def mask_mac(mac): if not mac or mac.count('-') != 5: return mac parts = mac.split('-') return f"{parts[0]}-XX-XX-XX-{parts[-1]}"- 权限控制:生产环境中应限制脚本执行权限
# 设置专用用户和权限 sudo useradd -r netcheck sudo setcap 'cap_net_raw,cap_net_admin+eip' /usr/bin/python3.8- 企业环境适配:某些企业网络会屏蔽MAC地址采集,需要备用方案
def get_mac_fallback(interface): try: with open(f'/sys/class/net/{interface}/address') as f: return f.read().strip() except: return None我在实际项目中遇到过一个典型问题:某台服务器的Docker容器突然无法联网,最终发现是主机网卡MAC地址被意外修改导致的。通过这个脚本快速定位了问题,比手动检查效率高了十倍不止。现在这个功能已经成为我们运维工具包的标准组件,每天自动检查数百台服务器的网络状态。