本篇博文介绍如何利用 Python 的 uuid 标准库获取本机 MAC 地址。
利用 Python 获取本机 MAC 地址非常简便,使用 uuid 标准库之后,一行代码即可获取,这里先展示出来,之后对这行代码进行分析:
print(":".join([uuid.uuid1().hex[-12:][i : i + 2] for i in range(0, 11, 2)]))
我们运行一下这句代码,发现确实打印出了本机的 MAC 地址:
然后,我们来观察一下这句代码,发现它的核心代码是:
uuid.uuid1().hex[-12:]
这个代码是什么意思呢?我们打印它一下看看:
发现相对于刚才的结果,它的数字之间没有了冒号,这说明刚才的代码除了核心代码都是在做格式的相应处理,因此我们只需把目光关注在核心代码上,那么这句核心代码是干什么呢?
首先,我们要来了解一个库 uuid,uuid 是 Python 的标准库,代表的是通用唯一识别码(Universally Unique Identifier)的缩写,字面意思就是生成一个对任何东西都可以进行唯一识别的编码,它在多个领域都有应用。
uuid 有多个生成 UUID 的函数,其中 uuid1() 函数从 MAC 地址、序列号和当前时间生成 UUID,通过该函数生成的 UUID 的最后 12 个字符就是本机的 MAC 地址,因此可能会暴露 MAC 地址从而损害隐私。
为了深入一点了解,我们来分别打印:
uuid.uuid1()
uuid.uuid1().hex
可以看到它们分别其实没有太大区别,都包含本机 MAC 地址 a4:7e:5d:a3:a2:d4,不过 uuid.uuid1() 生成的是一个 uuid.UUID 的类对象,而 uuid.uuid1().hex 是该类对象的内容的十六进制表现形式,之前代码中的 [-12:] 就代表取最后 12 位(注意,最后 12 位前面的众多位数是根据序列号和当前时间生成的):
所以也可以使用以下代码将 uuid.UUID 类对象转换为字符串来获取本机 MAC 地址:
print(":".join([str(uuid.uuid1())[-12:][i : i + 2] for i in range(0, 11, 2)]))
刚才也说了,uuid.uuid1() 生成的是一个 uuid.UUID 的类对象,那么不由得就产生一个疑问,这个类对象怎么就可以获得本机 MAC 地址呢?难道是这个函数它自己获取到 MAC 地址,然后封装到对象里面了吗?带着这个疑问,我们可以看一下这个函数的源码,然而,当我们链入这个函数时,我们就可以发现答案已经给出来了:
uuid1() 函数的注释上明确说明,当 node 没有给定时,它会使用 getnode() 函数,然后,这个 getnode() 函数就可以获取本机的硬件地址也就是我们的 MAC 地址,那么我们接下来就可以直接去找这个 getnode() 函数就好了,这里给出它对应的源码,然后我们进行分析:
_node = None
_NODE_GETTERS_WIN32 = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
_NODE_GETTERS_UNIX = [_unix_getnode, _ifconfig_getnode, _ip_getnode,
_arp_getnode, _lanscan_getnode, _netstat_getnode]
def getnode(*, getters=None):
"""Get the hardware address as a 48-bit positive integer.
The first time this runs, it may launch a separate program, which could
be quite slow. If all attempts to obtain the hardware address fail, we
choose a random 48-bit number with its eighth bit set to 1 as recommended
in RFC 4122.
"""
global _node
if _node is not None:
return _node
if sys.platform == 'win32':
getters = _NODE_GETTERS_WIN32
else:
getters = _NODE_GETTERS_UNIX
for getter in getters + [_random_getnode]:
try:
_node = getter()
except:
continue
if (_node is not None) and (0 <= _node < (1 << 48)):
return _node
assert False, '_random_getnode() returned invalid value: {}'.format(_node)
观察该函数的注释,我们发现 getnode() 函数可以将 48 位的硬件地址转换成十进制正整数形式返回给我们,我们不妨测试一下,我们运行下面这句代码:
print(uuid.getnode())
可以看到打印出的一串十进制数:
我们验证一下,发现 180862643839700 确实是 MAC 地址 a4:7e:5d:a3:a2:d4 的十进制形式:
因此,我们又可以用一种形式去获得本机 MAC 地址:
print(":".join([hex(uuid.getnode())[-12:][i : i + 2] for i in range(0, 11, 2)]))
最后,总结一下,我们可以有以下几种简便的方式来获取本机 MAC 地址:
print(":".join([uuid.uuid1().hex[-12:][i : i + 2] for i in range(0, 11, 2)]))
print(":".join([str(uuid.uuid1())[-12:][i : i + 2] for i in range(0, 11, 2)]))
print(":".join([hex(uuid.getnode())[-12:][i : i + 2] for i in range(0, 11, 2)]))