Files
crawler-plugin/source_code/tool/devices.py
2026-03-20 18:13:47 +08:00

249 lines
7.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import hashlib
import platform
import subprocess
import uuid
import os
from typing import Optional
class DeviceIDGenerator:
"""
Windows设备唯一ID生成器
通过收集多个硬件特征来生成稳定的设备唯一标识符
"""
def __init__(self, use_cache: bool = False, cache_file: str = ".device_id"):
"""
初始化设备ID生成器
Args:
use_cache: 是否使用本地缓存
cache_file: 缓存文件名
"""
self.use_cache = use_cache
self.cache_file = cache_file
def _run_wmic_command(self, command: str) -> Optional[str]:
"""
执行WMIC命令并返回结果
Args:
command: WMIC命令
Returns:
命令执行结果失败则返回None
"""
try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
except (subprocess.TimeoutExpired, Exception):
pass
return None
def _get_motherboard_serial(self) -> Optional[str]:
"""获取主板序列号"""
return self._run_wmic_command("wmic baseboard get serialnumber /value")
def _get_cpu_id(self) -> Optional[str]:
"""获取CPU ID"""
return self._run_wmic_command("wmic cpu get processorid /value")
def _get_bios_serial(self) -> Optional[str]:
"""获取BIOS序列号"""
return self._run_wmic_command("wmic bios get serialnumber /value")
def _get_disk_serial(self) -> Optional[str]:
"""获取系统盘序列号"""
return self._run_wmic_command("wmic diskdrive get serialnumber /value")
def _get_machine_guid(self) -> Optional[str]:
"""获取Windows机器GUID"""
try:
result = subprocess.run(
'reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography" /v MachineGuid',
shell=True,
capture_output=True,
text=True,
timeout=10
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if 'MachineGuid' in line:
return line.split()[-1]
except Exception:
pass
return None
def _extract_value(self, wmic_output: str) -> str:
"""从WMIC输出中提取实际值"""
if not wmic_output:
return ""
lines = wmic_output.split('\n')
for line in lines:
if '=' in line and not line.strip().endswith('='):
return line.split('=', 1)[1].strip()
return ""
def _collect_hardware_info(self) -> dict:
"""
收集硬件信息
Returns:
包含各种硬件信息的字典
"""
hardware_info = {}
# 主板序列号
motherboard = self._get_motherboard_serial()
hardware_info['motherboard'] = self._extract_value(motherboard) if motherboard else ""
# CPU ID
cpu_id = self._get_cpu_id()
hardware_info['cpu'] = self._extract_value(cpu_id) if cpu_id else ""
# BIOS序列号
bios = self._get_bios_serial()
hardware_info['bios'] = self._extract_value(bios) if bios else ""
# 硬盘序列号
disk = self._get_disk_serial()
hardware_info['disk'] = self._extract_value(disk) if disk else ""
# Windows机器GUID
machine_guid = self._get_machine_guid()
hardware_info['machine_guid'] = machine_guid if machine_guid else ""
# 计算机名称
hardware_info['computer_name'] = platform.node()
# MAC地址作为备用
hardware_info['mac_address'] = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff)
for elements in range(0, 2*6, 2)][::-1])
return hardware_info
def _generate_device_id(self, hardware_info: dict) -> str:
"""
基于硬件信息生成设备ID
Args:
hardware_info: 硬件信息字典
Returns:
32位十六进制设备ID
"""
# 过滤掉空值,并按键排序确保一致性
filtered_info = {k: v for k, v in hardware_info.items() if v and v.strip()}
# 如果没有任何硬件信息使用MAC地址作为后备方案
if not filtered_info:
filtered_info = {'mac_address': hardware_info.get('mac_address', str(uuid.getnode()))}
# 将所有信息连接成字符串
info_string = '|'.join(f"{k}:{v}" for k, v in sorted(filtered_info.items()))
# 使用SHA256生成哈希值
hash_object = hashlib.sha256(info_string.encode('utf-8'))
device_id = hash_object.hexdigest()
return device_id
def _load_cached_device_id(self) -> Optional[str]:
"""从缓存文件加载设备ID"""
try:
if os.path.exists(self.cache_file):
with open(self.cache_file, 'r', encoding='utf-8') as f:
cached_id = f.read().strip()
if len(cached_id) == 64: # SHA256哈希长度
return cached_id
except Exception:
pass
return None
def _save_device_id_to_cache(self, device_id: str) -> None:
"""将设备ID保存到缓存文件"""
try:
with open(self.cache_file, 'w', encoding='utf-8') as f:
f.write(device_id)
except Exception:
pass
def get_device_id(self) -> str:
"""
获取设备唯一ID
Returns:
64字符的十六进制设备ID
"""
# 如果启用缓存,先尝试从缓存加载
if self.use_cache:
cached_id = self._load_cached_device_id()
if cached_id:
return cached_id
# 收集硬件信息
hardware_info = self._collect_hardware_info()
# 生成设备ID
device_id = self._generate_device_id(hardware_info)
# 保存到缓存
if self.use_cache:
self._save_device_id_to_cache(device_id)
return device_id
def get_device_id_short(self, length: int = 16) -> str:
"""
获取短版本的设备ID
Args:
length: 返回ID的长度
Returns:
指定长度的设备ID
"""
full_id = self.get_device_id()
return full_id[:length]
def get_hardware_info(self) -> dict:
"""
获取硬件信息(用于调试)
Returns:
硬件信息字典
"""
return self._collect_hardware_info()
# 使用示例
def main():
"""使用示例"""
# 创建设备ID生成器实例
device_generator = DeviceIDGenerator()
# 获取完整设备ID64字符
device_id = device_generator.get_device_id()
print(f"完整设备ID: {device_id}")
# 获取短版本设备ID16字符
short_id = device_generator.get_device_id_short(16)
print(f"短设备ID: {short_id}")
# 查看硬件信息(调试用)
hardware_info = device_generator.get_hardware_info()
print("\n硬件信息:")
for key, value in hardware_info.items():
print(f" {key}: {value}")
if __name__ == "__main__":
main()