如何在Python中从NIC获取IP地址?
在Unix系统上,当Python脚本出错时,会发送一封邮件。
我被要求在邮件的主题行中添加{测试环境},前提是IP地址是192.168.100.37,因为这是测试服务器。这样我们就可以用一个版本的脚本,并且能知道邮件是来自测试服务器上出现的问题数据。
不过,当我在网上搜索时,总是找到这段代码:
import socket
socket.gethostbyname(socket.gethostname())
但是,这段代码给我的IP地址是127.0.1.1。当我使用ifconfig
命令时,我得到的是这个:
eth0 Link encap:Ethernet HWaddr 00:1c:c4:2c:c8:3e
inet addr:192.168.100.37 Bcast:192.168.100.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:75760697 errors:0 dropped:411180 overruns:0 frame:0
TX packets:23166399 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:59525958247 (59.5 GB) TX bytes:10142130096 (10.1 GB)
Interrupt:19 Memory:f0500000-f0520000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:25573544 errors:0 dropped:0 overruns:0 frame:0
TX packets:25573544 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:44531490070 (44.5 GB) TX bytes:44531490070 (44.5 GB)
首先,我不知道它是从哪里得到127.0.1.1的,但无论如何,这不是我想要的。每次我搜索时,总是看到相同的语法,像是Bash脚本或netifaces,而我想用的是标准库。
那么,我该如何在Python中获取eth0的IP地址呢?
15 个回答
如果你只需要在Unix系统上工作,可以使用一个系统调用(参考Stack Overflow上的问题 用Bash解析ifconfig只获取我的IP地址):
import os
f = os.popen('ifconfig eth0 | grep "inet\ addr" | cut -d: -f2 | cut -d" " -f1')
your_ip=f.read()
因为大多数答案都使用 ifconfig
来从 eth0 接口提取IPv4地址,但在大多数Linux系统中,这个命令已经不再推荐使用,取而代之的是 ip addr
,所以可以用下面的代码来替代:
import os
ipv4 = os.popen('ip addr show eth0 | grep "\<inet\>" | awk \'{ print $2 }\' | awk -F "/" \'{ print $1 }\'').read().strip()
ipv6 = os.popen('ip addr show eth0 | grep "\<inet6\>" | awk \'{ print $2 }\' | awk -F "/" \'{ print $1 }\'').read().strip()
另外,你也可以把部分解析工作交给Python解释器来做,使用 split()
方法,而不是用 grep 和 AWK,正如 Sergiy Kolodyazhnyy 在评论中提到的:
import os
ipv4 = os.popen('ip addr show eth0').read().split("inet ")[1].split("/")[0]
ipv6 = os.popen('ip addr show eth0').read().split("inet6 ")[1].split("/")[0]
不过在这种情况下,你需要检查每次 split()
调用返回的数组的边界。
还有一个使用正则表达式的版本:
import os
import re
ipv4 = re.search(re.compile(r'(?<=inet )(.*)(?=\/)', re.M), os.popen('ip addr show eth0').read()).groups()[0]
ipv6 = re.search(re.compile(r'(?<=inet6 )(.*)(?=\/)', re.M), os.popen('ip addr show eth0').read()).groups()[0]
一个简单的方法可以返回一个包含接口IP地址的字符串,代码如下:
from subprocess import check_output
ips = check_output(['hostname', '--all-ip-addresses'])
想了解更多信息,可以查看 hostname。
另外,如果你想获取连接到网络的接口的IP地址,而不需要知道它的名字,可以使用这个方法:
import socket
def get_ip_address():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
return s.getsockname()[0]
我知道这和你的问题有点不同,但其他人可能会看到这里,觉得这个方法更有用。使用这个方法时,你不需要有通往8.8.8.8的路由。它只是打开了一个连接,但并没有发送任何数据。
有两种方法:
方法一(使用外部包)
你需要获取绑定到你的 eth0
接口的IP地址。这个信息可以通过 netifaces包 来获取。
import netifaces as ni
ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
print(ip) # should print "192.168.100.37"
你还可以通过下面的方式获取所有可用接口的列表:
ni.interfaces()
方法二(不使用外部包)
这里有一种不使用Python包的方法来获取IP地址:
import socket
import fcntl
import struct
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
get_ip_address('eth0') # '192.168.0.110'
注意:通过检测IP地址来判断你正在使用的环境其实是一种比较“hack”的做法。几乎所有的框架都提供了非常简单的方法来设置或修改环境变量,以指示当前的环境。你可以查看一下相关文档,应该很简单,比如:
if app.config['ENV'] == 'production':
# send production email
else:
# send development email