87

当 Unix 上的 Python 脚本发生错误时,会发送一封电子邮件。

如果 IP 地址是测试服务器 192.168.100.37,我被要求将 {Testing Environment} 添加到电子邮件的主题行。这样我们就可以拥有一个脚本版本和一种方法来判断电子邮件是否来自测试服务器上的混乱数据。

然而,当我用谷歌搜索时,我一直在寻找这段代码:

import socket
socket.gethostbyname(socket.gethostname())

但是,这给了我 127.0.1.1 的 IP 地址。当我使用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 地址呢?

4

14 回答 14

198

两种方法:

方法#1(使用外部包)

您需要询问绑定到您的eth0接口的 IP 地址。这可以从netifaces 包中获得

import netifaces as ni
ni.ifaddresses('eth0')
ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
print(ip)  # should print "192.168.100.37"

您还可以通过以下方式获取所有可用接口的列表

ni.interfaces()

方法#2(无外包装)

这是一种无需使用 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 地址以确定您使用的是什么环境是相当困难的。几乎所有框架都提供了一种非常简单的方法来设置/修改环境变量以指示当前环境。尝试查看您的文档。它应该像做一样简单

if app.config['ENV'] == 'production':
  #send production email
else:
  #send development email
于 2014-06-13T02:49:36.883 回答
146

或者,如果您想获取用于连接网络的任何接口的 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 的路由即可使用它。它所做的只是打开一个套接字,但不发送任何数据。

于 2015-06-22T22:04:25.823 回答
35

返回带有接口 IP 地址的字符串的简单方法是:

from subprocess import check_output

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

有关更多信息,请参阅主机名

于 2016-09-05T20:18:18.327 回答
19

由于大多数答案都用于ifconfigeth0接口中提取 IPv4 地址,该接口已被弃用,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()

或者,您可以使用split()而不是grepAWK将部分解析任务转移到 Python 解释器,正如Sergiy Kolodyaznyy 在评论中指出的那样:

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]
于 2016-07-15T10:55:31.537 回答
16

如果您只需要在 Unix 上工作,您可以使用系统调用(参考 Stack Overflow 问题Parse ifconfig to get my IP address using Bash):

import os
f = os.popen('ifconfig eth0 | grep "inet\ addr" | cut -d: -f2 | cut -d" " -f1')
your_ip=f.read()
于 2015-10-20T19:47:32.277 回答
4

基于jeremyjjbrown 的答案,这是另一个版本,如他的答案的评论中所述,它会自行清理。

此版本还允许提供不同的服务器地址以用于专用内部网络等。

import socket

def get_my_ip_address(remote_server="google.com"):
    """
    Return the/a network-facing IP number for this system.
    """
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
        s.connect((remote_server, 80))
        return s.getsockname()[0]
于 2018-10-05T16:43:13.560 回答
2

这将收集主机上的所有 IP 地址并过滤掉 loopback/link-local 和IPv6。这也可以编辑为仅允许 IPv6,或IPv4和 IPv6,以及允许 IP 地址列表中的环回/本地链接。

from socket import getaddrinfo, gethostname
import ipaddress

def get_ip(ip_addr_proto="ipv4", ignore_local_ips=True):
    # By default, this method only returns non-local IPv4 addresses
    # To return IPv6 only, call get_ip('ipv6')
    # To return both IPv4 and IPv6, call get_ip('both')
    # To return local IPs, call get_ip(None, False)
    # Can combine options like so get_ip('both', False)

    af_inet = 2
    if ip_addr_proto == "ipv6":
        af_inet = 30
    elif ip_addr_proto == "both":
        af_inet = 0

    system_ip_list = getaddrinfo(gethostname(), None, af_inet, 1, 0)
    ip_list = []

    for ip in system_ip_list:
        ip = ip[4][0]

        try:
            ipaddress.ip_address(str(ip))
            ip_address_valid = True
        except ValueError:
            ip_address_valid = False
        else:
            if ipaddress.ip_address(ip).is_loopback and ignore_local_ips or ipaddress.ip_address(ip).is_link_local and ignore_local_ips:
                pass
            elif ip_address_valid:
                ip_list.append(ip)

    return ip_list

print(f"Your IP address is: {get_ip()}")

退货

您的 IP 地址是:['192.168.1.118']

如果我运行 get_ip('both', False),它会返回

您的 IP 地址是:['::1', 'fe80::1', '127.0.0.1', '192.168.1.118', 'fe80::cb9:d2dd:a505:423a']

于 2020-10-26T01:37:44.810 回答
1

试试下面的代码。它适用于Mac OS X v10.10.2 (Yosemite):

import subprocess

if __name__ == "__main__":
    result = subprocess.check_output('ifconfig en0 |grep -w inet', shell=True) # you may need to use eth0 instead of en0 here!!!
    print 'output = %s' % result.strip()
    # result = None
    ip = ''
    if result:
        strs = result.split('\n')
        for line in strs:
            # remove \t, space...
            line = line.strip()
            if line.startswith('inet '):
                a = line.find(' ')
                ipStart = a+1
                ipEnd = line.find(' ', ipStart)
                if a != -1 and ipEnd != -1:
                    ip = line[ipStart:ipEnd]
                    break
    print 'ip = %s' % ip
于 2015-12-30T09:58:41.733 回答
1

使用 Python从NIC获取 IP 地址的另一种方法。

我把它作为我很久以前开发的应用程序的一部分,我不想简单地git rm script.py. 因此,subprocess为了功能性方法和更少的代码行,我在这里提供了使用和列表推导的方法:

import subprocess as sp

__version__ = "v1.0"
__author__ = "@ivanleoncz"

def get_nic_ipv4(nic):
    """
        Get IP address from a NIC.

        Parameter
        ---------
        nic : str
            Network Interface Card used for the query.

        Returns
        -------
        ipaddr : str
            Ipaddress from the NIC provided as parameter.
    """
    result = None
    try:
        result = sp.check_output(["ip", "-4", "addr", "show", nic],
                                                  stderr=sp.STDOUT)
    except Exception:
        return "Unkown NIC: %s" % nic
    result = result.decode().splitlines()
    ipaddr = [l.split()[1].split('/')[0] for l in result if "inet" in l]
    return ipaddr[0]

此外,您可以使用类似的方法来获取 NIC 列表:

def get_nics():
    """
        Get all NICs from the Operating System.

        Returns
        -------
        nics : list
            All Network Interface Cards.
    """
    result = sp.check_output(["ip", "addr", "show"])
    result = result.decode().splitlines()
    nics = [l.split()[1].strip(':') for l in result if l[0].isdigit()]
    return nics

这是作为Gist的解决方案。

你会有这样的东西:

python3

Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
>>> import helpers
>>>
>>> helpers.get_nics()
['lo', 'enp1s0', 'wlp2s0', 'docker0']
>>> helpers.get_nic_ipv4('docker0')
'172.17.0.1'
>>> helpers.get_nic_ipv4('docker2')
'Unkown NIC: docker2'
于 2019-08-11T02:45:13.513 回答
1

如果你想用硬(但可能很快?)的方式来做,这里有一些粗略的Netlink ( RFC 3549 ) 代码(可能仅限 Linux),它同时获得 IPv4 和 IPv6,只有标准库中的一个 import 语句:

    import socket
    # https://www.man7.org/linux/man-pages/man7/rtnetlink.7.html
    # https://github.com/torvalds/linux/blob/master/include/uapi/linux/rtnetlink.h
    RTM_NEWADDR = 20
    RTM_GETADDR = 22
    # https://www.man7.org/linux/man-pages/man7/netlink.7.html
    # https://github.com/torvalds/linux/blob/master/include/uapi/linux/netlink.h
    NLM_F_REQUEST = 0x01
    NLM_F_ROOT = 0x100
    s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW)
    req = (
        # nlmsghdr
        int.to_bytes(0, 4, 'little', signed=False) +  # nlmsg_len
        int.to_bytes(RTM_GETADDR, 2, 'little', signed=False) +  # nlmsg_type
        int.to_bytes(NLM_F_REQUEST | NLM_F_ROOT, 2, 'little', signed=False) +  # nlmsg_flags
        int.to_bytes(0, 2, 'little', signed=False) +  # nlmsg_seq
        int.to_bytes(0, 2, 'little', signed=False) +  # nlmsg_pid
        # ifinfomsg
        b'\0' * 8
    )
    req = int.to_bytes(len(req), 4, 'little') + req[4:]
    s.sendall(req)
    full_resp = s.recv(4096)
    while full_resp:
        resp = full_resp
        # nlmsghdr
        nlmsg_len = int.from_bytes(resp[0:4], 'little', signed=False)
        nlmsg_type = int.from_bytes(resp[4:6], 'little', signed=False)
        assert not nlmsg_len % 4, nlmsg_len
        resp = resp[16:nlmsg_len]
        full_resp = full_resp[nlmsg_len:]
        if nlmsg_type == 3:  # NLMSG_DONE
            assert not full_resp, full_resp
            break
        if not full_resp:
            full_resp = s.recv(4096)
        assert nlmsg_type == RTM_NEWADDR, (nlmsg_type, resp[:32])
        # ifaddrmsg
        ifa_family = int.from_bytes(resp[0:1], 'little', signed=False)
        ifa_index = int.from_bytes(resp[4:8], 'little', signed=False)
        resp = resp[8:]
        while resp:
            # rtattr
            rta_len = int.from_bytes(resp[0:2], 'little', signed=False)
            rta_type = int.from_bytes(resp[2:4], 'little', signed=False)
            data = resp[4:rta_len]
            if rta_type == 1:  # IFLA_ADDRESS
                if ifa_family == socket.AF_INET:
                    ip = '.'.join('%d' % c for c in data)
                if ifa_family == socket.AF_INET6:
                    ip = ':'.join(('%02x%02x' % (chunk[0], chunk[1]) if chunk != b'\0\0' else '') for chunk in [data[0:2], data[2:4], data[4:6], data[6:8], data[8:10], data[10:12], data[12:14], data[14:16]])
                print('interface #%s has %s' % (ifa_index, ip))
            if rta_type == 3:  # IFLA_IFNAME
                ifname = data.rstrip(b'\0').decode()
                print('interface #%s is named %s' % (ifa_index, ifname))
            # need to round up to multiple of 4
            if rta_len % 4:
                rta_len += 4 - rta_len % 4
            resp = resp[rta_len:]
    s.close()

如果您只需要 IPv4,SIOCGIFADDR ioctl另一个答案中的老派方法可能更直接。对于 IPv6,有/proc/net/if_inet6.

于 2022-01-13T18:12:54.077 回答
0

它对我有用

 import subprocess
 my_ip = subprocess.Popen(['ifconfig eth0 | awk "/inet /" | cut -d":" -f 2 | cut -d" " -f1'], stdout=subprocess.PIPE, shell=True)
 (IP,errors) = my_ip.communicate()
 my_ip.stdout.close()
 print IP
于 2016-02-01T16:48:10.707 回答
0

在 ifconfig 中找到正在运行的第一个 eth/wlan 条目的 IP 地址:

import itertools
import os
import re

def get_ip():
    f = os.popen('ifconfig')
    for iface in [' '.join(i) for i in iter(lambda: list(itertools.takewhile(lambda l: not l.isspace(), f)), [])]:
        if re.findall('^(eth|wlan)[0-9]', iface) and re.findall('RUNNING', iface):
            ip = re.findall('(?<=inet\saddr:)[0-9\.]+', iface)
            if ip:
                return ip[0]
    return False
于 2016-05-15T01:01:19.850 回答
0

这是 ifconfig 的结果:

ifconfig

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.2.24  netmask 255.255.255.0  broadcast 192.168.2.255
        inet6 fe80::88e9:4d2:c057:2d5f  prefixlen 64  scopeid 0x20<link>
        ether b8:27:eb:d0:9a:f3  txqueuelen 1000  (Ethernet)
        RX packets 261861  bytes 250818555 (239.1 MiB)
        RX errors 0  dropped 6  overruns 0  frame 0
        TX packets 299436  bytes 280053853 (267.0 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 74  bytes 16073 (15.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 74  bytes 16073 (15.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether b8:27:eb:85:cf:a6  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

削减一点输出,我们有:

ifconfig eth0 | grep "inet 192" | cut -c 14-25

192.168.2.24

现在,我们可以去 Python 做:

import os
mine = os.popen('ifconfig eth0 | grep "inet 192" | cut -c 14-25')
myip = mine.read()
print (myip)
于 2019-05-19T15:36:05.570 回答
-1

你可以试试这个代码片段:

import netifaces as ni

def test_network():
    interfaces = ni.interfaces()

    for i in interfaces: #Will cycle through all available interfaces and check each one.
        if i != "lo": #This will remove lo from the interfaces it checks.
            try:
                ni.ifaddresses(i)
                gws = ni.gateways()
                gateway = gws['default'][ni.AF_INET][0]
                ip = ni.ifaddresses(i)[ni.AF_INET][0]['addr']
                sm = ni.ifaddresses(i)[ni.AF_INET][0]['netmask']
                print ("Network information for " + i + ":")
                print ("IP address: " + ip)
                print ("Subnet Mask: " + sm)
                print ("Gateway: " + gateway)
                print ()
            except: #Error case for a disconnected Wi-Fi or trying to test a network with no DHCP
                print (i + " is not connected or DHCP is not available. Try setting a static IP address.")
test_network()

这可能会产生以下结果:

Network information for eth0:

IP address: 192.168.1.172

Subnet Mask: 255.255.255.0

Gateway: 192.168.1.254

wlan0 is not connected or DHCP is not available.
于 2021-03-08T17:32:47.890 回答