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


75

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

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

但是,当我在Google上搜索时,我会不断找到以下代码:

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,但是无论哪种方式都不是我想要的。当我使用google时,我一直使用相同的语法,Bash脚本或netifaces,而我正尝试使用标准库。

那么如何在Python中获取eth0的IP地址呢?


文件中的“我不知道它从哪里得到的127.0.1.1” /etc/hosts
伊万·德·帕兹

您提到了“ 127.0.1.1”。您是说“ 127.0.0.1”吗?因为那是您的本地回送接口,loifconfig输出中的第二个条目);如果您是第一个条目cat /etc/hosts
声音

Answers:


182

两种方法:

方法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

1
netifaces是标准库吗?
Memor-X 2014年

4
我知道我参加晚会很晚,但是有人可以解释一下Edit 2的法术吗?我是套接字库的新手,从未使用过其他两个。
奥斯丁

2
为什么使用ifname [:15]而不是ifname?
bdrx

32
对于Python 3,ifname [:15]应该是字节(ifname [:15],'utf-8')
Taranjeet

12
鉴于它是Python,将netifaces作为ni导入特别适合……至少对于某些骑士而言。😉;-)
Ubuntourist 2016年

130

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


3
是的,可能不会到任何地方,但是某些局域网可能不会受到影响。
jeremyjjbrown 2015年

登录只是为了对此信息+1。IMO,该IP最接近“真实IP地址”
Sergey Alaev

3
很棒的解决方案。
Mike Guelfi

这里不需要关闭插座吗?
krizajb

1
@krizajb我从来没有打扰过,因为我从来没有对每个pid重复多次,并且如果在gc期间没有实现,那么使用pid可以清除单个文件描述符。如果由于某种原因您需要做很多事情,那么使用上下文管理器将是
pythonic

31

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

from subprocess import check_output

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

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


我特别喜欢这个容器,因为它在容器内部时确实提供了Docker容器的正确IP。否则,将针对第一个被举报的人。精彩!
Guido U. Draheim,

2
这特定于Linux的net-tools版本hostname。Mac / BSD主机名命令不提供此功能。如果您使用的是Linux版本,这可能是最简单的方法。
theferrit32

17

由于大多数答案都用于ifconfig从eth0接口中提取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()代替grep和awk来将部分解析任务转移到python解释器,如@serg在注释中指出:

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()调用返回的数组范围。

更新2:

使用正则表达式的另一个版本:

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]

3
使用好工作,ip addr但实际上不需要将输出通过管道传递到grep和awk。Python具有使用split()解析输出的强大功能
Sergiy Kolodyazhnyy

要使它更具机器可解析性,请尝试使用该-oneline选项。"output each record on a single line, replacing line feeds with the '\' character. This is convenient when you want to count records with wc(1) or to grep(1) the output."
CivFan

1
此外,iproute2(您的ip一部分)较新的发行版-j, --json对大多数符合您期望的命令提供了支持。
iamkroot '20

15

如果只需要在Unix上工作,则可以使用系统调用(请参阅堆栈溢出问题Parse ifconfig以使用Bash仅获取我的IP地址):

import os
f = os.popen('ifconfig eth0 | grep "inet\ addr" | cut -d: -f2 | cut -d" " -f1')
your_ip=f.read()

2
这是特定于发行版的。例如,在RHEL上,cut命令切断错误的术语。因此,该版本不能跨* nix发行版移植。
埃里克·莱斯钦斯基

2
grep并且cut在这里实际上没有必要,只需获取输出并解析它即可。同样,ip addr命令可能更可取。
Sergiy Kolodyazhnyy'9

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]

我建议使用IP地址而不是主机名作为“ remote_server”的默认值,这样您就不会触发DNS查找。
plugwash

1

试试下面的代码,它在Mac10.10.2中对我有用:

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

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                                                

这是要点的解决方案。

并且您将有这样的事情:

$ 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'

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

我的猜测是有人-1这样做了,因为Linux在ifconfig输出中不使用“:”分隔符。此命令仅在某些包含此类内容的Unix上有效。
Mike S

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

0

这是ifconfig的结果:

pi@raspberrypi:~ $ 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

pi@raspberrypi:~ $ 

减少一点输出,我们有:

pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ ifconfig eth0 | grep "inet 192" | cut -c 14-25
192.168.2.24
pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ 

现在,我们可以转到python并执行以下操作:

import os
mine = os.popen('ifconfig eth0 | grep "inet 192" | cut -c 14-25')
myip = mine.read()
print (myip)

0

这将收集主机上的所有IP,并过滤掉环回/本地链接和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 combime 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']

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.