
1. 项目概述与核心价值在日常工作中我经常遇到这样的场景新设备需要连接WiFi但密码早就忘了或者帮同事、朋友调试网络时需要从已连接的电脑上获取密码。每次都要翻箱倒柜找路由器或者问别人效率很低。其实对于使用Windows系统并已成功连接过WiFi的电脑来说密码就安全地存储在系统里只是被隐藏了起来。用Python写个小工具一键查看所有已保存的WiFi密码这个需求非常实际。这个项目本质上是一个系统信息查询工具它不涉及任何网络破解或入侵行为仅仅是读取操作系统自身存储的、用户已授权连接的网络凭证。它的核心价值在于提升效率和解锁便利性。对于运维人员、IT支持或者仅仅是喜欢折腾的电脑用户手头有一个自己写的、干净透明的密码查看器远比去网上下载来历不明的第三方软件要安全可靠得多。接下来我将拆解如何用Python实现这一功能并深入每一步背后的原理和避坑要点。2. 技术原理与系统交互拆解2.1 Windows WiFi凭证存储机制要读取密码首先得知道密码存在哪儿、怎么存的。在Windows系统中当你第一次成功连接一个WiFi并输入密码后系统会将这些网络配置信息SSID、认证类型、加密方式以及密码加密后存储在一个特定的地方。对于Windows 10及更新版本这些信息通常保存在C:\ProgramData\Microsoft\Wlansvc\Profiles\Interfaces目录下各个网络接口对应的子目录中的XML配置文件里。关键在于密码并非明文存储而是使用一种基于当前登录用户安全上下文User SID的加密方式。这意味着只有在该用户登录的会话中系统才有密钥解密这些信息。我们的Python脚本需要模拟系统原生工具netsh的行为向系统发起一个合法的查询请求系统会在内部完成解密后将结果返回给我们。这解释了为什么我们自己编写的脚本能读出密码——我们是在请求系统帮我们解密它自己存储的数据而不是暴力破解加密文件。2.2 Python的实现路径选择实现这个功能主要有两条技术路径调用系统命令subprocess模块这是最直接、最稳定的方法。即让Python脚本在后台执行Windows系统自带的netsh wlan show profiles和netsh wlan show profile name”SSID” keyclear命令然后捕获并解析命令输出的文本结果。netsh是微软官方提供的网络配置命令行工具其行为稳定可靠兼容性好。直接调用Windows APIpywin32库这种方法更底层通过win32api和win32security等模块直接与Windows的WLAN API交互枚举配置文件和查询密钥内容。这种方式更“程序员”灵活性更高但代码更复杂且严重依赖pywin32这个第三方库在打包分发时可能增加复杂度。对于绝大多数场景路径一调用系统命令是首选。理由如下零额外依赖仅需Python标准库、代码简洁易懂、行为与用户在CMD中手动操作完全一致避免了因API版本或系统权限导致的意外问题。本项目的核心讲解也将围绕这条路径展开。2.3 安全与权限考量这是一个必须严肃对待的问题。脚本之所以能运行成功前提是当前Python解释器是以具有管理员权限的用户身份运行的。因为读取WiFi配置信息属于系统级操作。如果你在普通的VSCode或IDLE中直接运行脚本很可能会在尝试执行netsh命令时收到“拒绝访问”的错误。注意务必在管理员权限下运行你的Python脚本或集成开发环境IDE。右键点击你的命令行工具CMD、PowerShell或IDE图标选择“以管理员身份运行”。3. 核心代码实现与逐步解析我们将采用“调用系统命令”的方案构建一个健壮、实用的脚本。整个过程分为三步获取所有已保存的WiFi配置文件列表、逐个查询其详细配置并提取密码、格式化输出结果。3.1 获取已保存的WiFi列表首先我们需要知道系统里都存了哪些WiFi的名字SSID。import subprocess import re def get_wifi_profiles(): 获取本机所有已保存的WiFi配置文件名称SSID。 返回一个SSID列表。 try: # 执行命令获取所有配置文件 result subprocess.run([netsh, wlan, show, profiles], capture_outputTrue, textTrue, encodinggbk, shellTrue) # 检查命令是否成功执行 if result.returncode ! 0: print(f“执行命令失败: {result.stderr}”) return [] # 使用正则表达式从输出中提取所有SSID # 命令输出中每个SSID所在行通常包含‘:’字符 profiles [] for line in result.stdout.split(‘\n’): # 匹配类似 ‘ 所有用户配置文件 : WiFi-Name’ 的行 match re.search(r‘所有用户配置文件\s*:\s*(.)$’, line) if match: ssid match.group(1).strip() if ssid: # 避免空字符串 profiles.append(ssid) return profiles except Exception as e: print(f“获取WiFi列表时发生异常: {e}”) return [] # 测试函数 if __name__ ‘__main__’: profiles get_wifi_profiles() print(“已保存的WiFi列表:”) for p in profiles: print(f“ - {p}”)代码解析与避坑subprocess.run(): 这是执行外部命令的核心函数。参数capture_outputTrue表示捕获命令的输出stdout和stderrtextTrue和encoding‘gbk’确保返回的是字符串而非字节并且使用中文Windows系统的默认编码避免乱码。shellTrue: 这个参数有时是必需的特别是在某些涉及系统环境变量的命令执行时。对于netsh加上它通常兼容性更好。编码问题大坑Windows命令行输出中文默认使用GBK编码。如果你设置encoding‘utf-8’当WiFi名称包含中文时你会得到一堆乱码后续的正则匹配也会失败。务必使用‘gbk’。正则表达式r‘所有用户配置文件\s*:\s*(.)$’用于匹配输出行。\s*匹配任意空白字符(.)捕获冒号后的所有内容即SSID。这个模式需要根据你的系统语言调整英文系统对应的行可能是‘All User Profile’。3.2 查询指定WiFi的详细配置与密码拿到SSID列表后我们需要对每一个SSID执行另一个命令来获取其详细信息其中就包含密码。def get_wifi_password(ssid): 根据给定的SSID查询其详细信息并提取密码。 返回一个字典包含SSID和密码如果查询失败或密码为空则返回None。 try: # 执行命令查询特定配置文件的详细信息并指定keyclear以显示明文密钥 cmd [‘netsh’, ‘wlan’, ‘show’, ‘profile’, f‘name{ssid}’, ‘keyclear’] result subprocess.run(cmd, capture_outputTrue, textTrue, encoding‘gbk’, shellTrue) if result.returncode ! 0: # 可能该配置文件已损坏或权限不足 print(f“查询SSID ‘{ssid}’ 失败: {result.stderr}”) return {‘ssid’: ssid, ‘password’: None} # 在输出结果中查找‘关键内容’行 for line in result.stdout.split(‘\n’): # 匹配密码行。中文系统可能是‘关键内容’英文系统是‘Key Content’ if ‘关键内容’ in line or ‘Key Content’ in line: # 使用split分割取冒号后面的部分并去除首尾空格 password line.split(‘:’)[1].strip() return {‘ssid’: ssid, ‘password’: password} # 如果循环结束都没找到密码行说明这个网络可能没有密码如开放网络或者密码为空 return {‘ssid’: ssid, ‘password’: None} except Exception as e: print(f“查询SSID ‘{ssid}’ 时发生异常: {e}”) return {‘ssid’: ssid, ‘password’: None} # 测试单个查询 if __name__ ‘__main__’: test_ssid ‘Your-WiFi-Name’ # 替换为你的一个WiFi名 info get_wifi_password(test_ssid) print(f“SSID: {info[‘ssid’]}”) print(f“Password: {info[‘password’]}”)关键点解析keyclear: 这是命令中最关键的参数。clear指示netsh在解密后以明文形式显示密钥即密码。没有这个参数密码字段会被隐藏。密码行匹配逻辑这里采用了更稳健的if ‘关键内容’ in line判断而非完全依赖正则。因为命令输出格式稳定包含特定关键词的行就是密码行。中英文系统关键词不同所以做了兼容处理。返回None的情况函数返回None并不总是意味着错误。有两种正常情况1) 该WiFi是开放网络无密码2) 该配置文件保存的密码本身就是空的虽然不常见。真正的错误如权限不足、配置文件损坏会通过stderr或异常体现。3.3 整合与优雅输出最后我们将两个函数组合起来并添加一些友好的输出和错误处理。import subprocess import re import json from datetime import datetime def get_all_wifi_passwords(output_format‘text’): 主函数获取所有WiFi密码。 :param output_format: 输出格式‘text’ 或 ‘json’ :return: 根据格式返回字符串或字典列表 print(“[*] 正在获取已保存的WiFi配置文件列表...”) profiles get_wifi_profiles() if not profiles: print(“[!] 未找到任何已保存的WiFi配置文件。”) return [] if output_format ‘json’ else “” print(f“[] 共找到 {len(profiles)} 个配置文件。开始查询密码...”) print(“-” * 50) wifi_list [] for ssid in profiles: # 添加一个简单的进度提示 print(f“正在查询: {ssid}”) info get_wifi_password(ssid) wifi_list.append(info) # 根据要求的格式返回 if output_format.lower() ‘json’: return wifi_list else: # 文本格式输出 output_lines [] output_lines.append(“” * 60) output_lines.append(f“WiFi密码查询报告 - {datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’)}”) output_lines.append(“” * 60) for item in wifi_list: ssid item[‘ssid’] pwd item[‘password’] status pwd if pwd is not None else ‘无密码或查询失败’ output_lines.append(f“SSID: {ssid:30} 密码: {status}”) output_lines.append(“” * 60) return ‘\n’.join(output_lines) if __name__ ‘__main__’: # 以文本形式输出到屏幕 result_text get_all_wifi_passwords(output_format‘text’) print(result_text) # 如果需要保存为JSON文件 # result_json get_all_wifi_passwords(output_format‘json’) # with open(‘wifi_passwords.json’, ‘w’, encoding‘utf-8’) as f: # json.dump(result_json, f, ensure_asciiFalse, indent2) # print(“密码已保存至 wifi_passwords.json”)功能增强点进度反馈在循环中打印当前正在查询的SSID让用户感知程序正在运行。多种输出格式支持纯文本方便直接查看和JSON格式方便被其他程序调用或保存为结构化文件。时间戳在报告头部添加生成时间增加记录的可追溯性。友好的空值显示将None密码显示为无密码或查询失败比直接显示None更易理解。4. 进阶优化与异常处理一个健壮的工具必须考虑各种边界情况和潜在错误。4.1 处理特殊字符与空格有些WiFi名称SSID可能包含空格或特殊字符如,(等在拼接命令时可能导致问题。最安全的做法是始终用双引号将SSID包裹起来。# 在 get_wifi_password 函数中修改命令构建部分 def get_wifi_password(ssid): try: # 使用双引号包裹SSID确保命令行解析正确 cmd [‘netsh’, ‘wlan’, ‘show’, ‘profile’, f‘name”{ssid}”’, ‘keyclear’] # ... 其余代码不变 ...4.2 超时与子进程控制极端情况下netsh命令可能卡住。我们可以为subprocess.run设置超时。try: result subprocess.run(cmd, capture_outputTrue, textTrue, encoding‘gbk’, shellTrue, timeout10) # 设置10秒超时 except subprocess.TimeoutExpired: print(f“查询 ‘{ssid}’ 超时跳过。”) return {‘ssid’: ssid, ‘password’: ‘查询超时’} except Exception as e: # ... 其他异常处理 ...4.3 权限检测与提示可以在脚本开始时主动检测是否具有管理员权限。import ctypes import sys def is_admin(): 检查当前是否以管理员权限运行 try: return ctypes.windll.shell32.IsUserAnAdmin() except: return False if __name__ ‘__main__’: if not is_admin(): print(“[!] 请以管理员身份运行此脚本”) print(“[!] 右键点击你的命令行或IDE选择‘以管理员身份运行’。”) sys.exit(1) # 非正常退出 # 正常执行主逻辑 result_text get_all_wifi_passwords() print(result_text)5. 打包与分发制作成独立EXE工具为了让没有安装Python环境的同事或朋友也能使用我们可以用PyInstaller将脚本打包成单个的.exe文件。安装PyInstaller:pip install pyinstaller执行打包命令: 在脚本所在目录打开命令行管理员权限非必须但建议执行pyinstaller --onefile --console --name “WiFiPasswordViewer” wifi_password_viewer.py--onefile: 打包成单个exe文件。--console: 程序运行时显示控制台窗口用于显示输出结果。--name: 指定生成的exe文件名。找到生成的文件命令执行成功后在项目目录下的dist文件夹里就能找到WiFiPasswordViewer.exe。将这个文件发给别人他们双击运行同样需要右键“以管理员身份运行”就能直接看到结果了。实操心得使用PyInstaller打包时如果脚本引用了某些动态库或资源可能会遇到路径问题。一个常见的技巧是在脚本中访问资源时使用sys._MEIPASS这是PyInstaller运行时的一个临时目录。不过对于我们这个纯系统命令调用的脚本一般不会遇到此问题。6. 常见问题与排查实录在实际编写和运行过程中你可能会遇到以下问题问题现象可能原因解决方案运行脚本后无任何输出或立即闪退。1. 没有以管理员身份运行。2. 脚本编码或语法错误。1. **务必右键“以管理员身份运行”**你的CMD、PowerShell或IDE。2. 在命令行用python your_script.py手动运行查看具体报错信息。输出结果中WiFi名称是乱码。控制台输出编码不匹配。subprocess捕获时未使用gbk编码。确保subprocess.run()中设置了encoding‘gbk’。能列出WiFi但所有密码都显示无密码或查询失败。1. 命令语法错误特别是SSID包含空格时。2. 系统语言非中文密码行关键词匹配失败。1. 修改命令构建确保SSID被双引号包裹f‘name”{ssid}”’。2. 修改密码匹配逻辑同时检查中英文关键词if ‘关键内容’ in line or ‘Key Content’ in line:。报错‘netsh’ 不是内部或外部命令...netsh命令路径不在环境变量中极罕见通常只在精简版系统中出现。可以尝试使用绝对路径C:\Windows\System32\netsh。打包成exe后在别的电脑上运行报错。目标电脑缺少必要的运行时库如VC Redistributable。使用PyInstaller时可尝试添加--hidden-import参数导入可能缺失的模块但更通用的办法是让目标电脑安装对应版本的Microsoft Visual C Redistributable。对于我们的简单脚本这种情况较少。一个重要的伦理提醒这个工具只能查看当前用户在本机上已成功连接并保存过密码的WiFi。它无法查看未保存的密码更无法破解他人或未知的WiFi密码。请仅在你自己拥有管理权限的设备上用于合法的密码管理和恢复用途。