如何在Python中从NIC获取IP地址?

107 投票
15 回答
270149 浏览
提问于 2025-04-18 09:38

在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 个回答

17

如果你只需要在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()
25

因为大多数答案都使用 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() 方法,而不是用 grepAWK,正如 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]
40

一个简单的方法可以返回一个包含接口IP地址的字符串,代码如下:

from subprocess import check_output

ips = check_output(['hostname', '--all-ip-addresses'])

想了解更多信息,可以查看 hostname

161

另外,如果你想获取连接到网络的接口的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的路由。它只是打开了一个连接,但并没有发送任何数据。

229

有两种方法:

方法一(使用外部包)

你需要获取绑定到你的 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

撰写回答