使用Python的stdlib查找本地IP地址


545

如何仅使用标准库在Python平台中独立查找本地IP地址(即192.168.xx或10.0.xx)?


7
本地IP?还是公共IP?您将如何处理具有多个IP的系统?
Sargun Dhillon

使用ifconfig -a并使用那里的输出...
Fredrik Pihl

16
@Fredrik这是一个坏主意。首先,您不必要地分叉了一个新过程,这可能会阻止您的程序在严格锁定的配置中工作(或者,您必须允许程序不需要的权限)。其次,您将为不同语言环境的用户引入错误。第三,如果您决定完全启动一个新程序,则不应该启动一个已过时的程序- ip addr它更适合(并且更易于解析和引导)。
phihag 2011年

12
@phihag,您绝对正确,感谢您纠正我的愚蠢
Fredrik

1
这里一个更基本的问题是,在正确编写的现代网络程序中,正确的(一组)本地IP地址取决于对等方或潜在对等方的组。如果bind特定接口的套接字需要本地IP地址,则这是策略问题。如果需要本地IP地址将其移交给对等方,以便对等方可以“回叫”,即打开与本地计算机的连接,则情况取决于是否有任何NAT(网络地址转换)框之间。如果没有NAT,getsockname则是一个不错的选择。
Pekka Nikander '04

Answers:


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

这将永远无法正常工作(返回127.0.0.1主机/etc/hosts名为as的机器127.0.0.1),将是gimel显示的一种称呼,socket.getfqdn()而是使用。当然,您的计算机需要一个可解析的主机名。


43
应当注意,这不是平台无关的解决方案。使用这种方法,许多Linux会返回127.0.0.1作为您的IP地址。
杰森·贝克

20
一个变体:socket.gethostbyname(socket.getfqdn())
吉梅尔

55
这似乎只返回一个IP地址。如果机器有多个地址怎么办?
詹森·库姆斯

26
在Ubuntu上,由于某种原因,它返回127.0.1.1。
滑动

13
@Jason R. Coombs,使用以下代码检索属于主机的IPv4地址列表:socket.gethostbyname_ex(socket.gethostname())[-1]
Barmaley,2015年

459

我刚刚发现了它,但是似乎有点破烂,但是他们说在* nix上尝试过,而我在Windows上做了,它起作用了。

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])
s.close()

这假设您可以访问互联网,并且没有本地代理。


31
不错,如果您的计算机上有多个接口,并且需要一个可以路由到例如gmail.com的接口
elzapp 2010年

4
捕获s.connect()可能引发的socket.error异常可能是一个好主意!
phobie 2011年

39
最好使用IP地址而不是域名-它必须更快并且独立于DNS可用性。例如,我们可以使用8.8.8.8 IP-Google的公共DNS服务器。
wobmene'4

10
非常聪明,完美的作品。除了适用于gmail或8.8.8.8的服务器,您还可以使用要从中显示服务器的IP或地址。
Falken教授的合同

3
此示例具有能够实际解析gmail.com的外部依赖关系。如果将其设置为不在本地局域网上的ip地址(不管它是开还是关),它将在没有依赖项且没有网络流量的情况下运行。
2015年

253

此方法在本地设备(具有默认路由的设备)上返回“主要” IP

  • 完全不需要可路由的网络访问或任何连接。
  • 即使所有接口都从网络上拔下,它也可以工作。
  • 不需要甚至尝试到达其他任何地方
  • 与NAT,公共IP,专用IP,外部IP和内部IP一起使用
  • 没有外部依赖项的纯Python 2(或3)。
  • 适用于Linux,Windows和OSX。

Python 3或2:

import socket
def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except Exception:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

这将返回一个作为主IP的IP(具有默认路由的IP)。如果您需要将所有IP附加到所有接口(包括localhost等),请参阅此答案

如果您像家庭中的wifi盒一样位于NAT防火墙的后面,则不会显示您的公共NAT IP,而是显示您本地IP上的私有IP,该IP具有到您本地WIFI路由器的默认路由;获取您的wifi路由器的外部IP要么需要在THAT盒子上运行它,要么连接到可能反映IP的外部服务,例如whatismyip.com/whatismyipaddress.com ...,但这与原始问题完全不同。:)


7
在带有Python 2和3的Raspbian中工作!
pierce.jason 2015年

3
辉煌。适用于Win7、8、8.1 + Linux Mint&Arch,包括VM。
谢里米

2
在Windows 10专业版中可以使用!谢谢杰米森·贝克尔!
varantes

3
由于某些原因,这在Mac OS X El Capitan 10.11.6上不起作用(它会产生OS异常错误:[Errno 49]无法分配请求的地址)。将端口从“ 0”更改为“ 1”:s.connect(('10.255.255.255',1))在Mac OS X和Linux Ubuntu 17.04上对我都有效
Pedro Scarapicchia Junior

9
这应该是公认的答案。 socket.gethostbyname(socket.gethostname())给出可怕的结果。
杰森·弗洛伊德

142

作为别名myip,应该在任何地方都有效:

alias myip="python -c 'import socket; print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith(\"127.\")][:1], [[(s.connect((\"8.8.8.8\", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])'"
  • 可与Python 2.x,Python 3.x,现代和旧版Linux发行版,OSX / macOS和Windows一起正常使用,以查找当前的IPv4地址。
  • 对于具有多个IP地址,IPv6,没有配置的IP地址或没有Internet访问的计算机,不会返回正确的结果。

与上述相同,但只有Python代码:

import socket
print([l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1], [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0][0])
  • 如果未配置IP地址,这将引发异常。

在没有Internet连接的情况下也可以在LAN上运行的版本:

import socket
print((([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + ["no IP found"])[0])

(感谢@ccpizza


背景

使用socket.gethostbyname(socket.gethostname())在这里不起作用,因为我所在的其中一台计算机上有/etc/hosts重复的条目并对其自身进行了引用。socket.gethostbyname()仅返回中的最后一项/etc/hosts

这是我最初的尝试,清除了所有以以下地址开头的地址"127."

import socket
print([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1])

这适用于Linux 2和Windows上的Python 2和3,但不适用于多个网络设备或IPv6。但是,它停止了在最近的Linux发行版上的工作,因此我尝试了这种替代技术。它尝试通过8.8.8.8以下端口连接到Google DNS服务器53

import socket
print([(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])

然后,我将上述两种技术组合成一种在任何地方都可以使用的单行myip代码,并在此答案的顶部创建了别名和Python代码段。

随着IPv6的日益普及以及对于具有多个网络接口的服务器,使用第三方Python模块查找IP地址可能比此处列出的任何方法都更可靠和可靠。


2
@Alexander:只是说这个答案比以前有用得多(而且不像过滤掉重复项很重要;)。根据说明,文档socket.getaddrinfo()应该在所有平台上均能一致地工作-但我只在Linux上进行过检查,并没有理会任何其他操作系统。
Wladimir Palant

1
@Alexander,/etc/resolve.conf: No such file or directory我有所示的本地IPv4地址ifconfig
anatoly techtonik 2013年

2
我可以确认更新的版本可以在具有Python2和Py3k的Ubuntu 14.04上使用。
UliKöhler2014年

4
“更新”显示了在UDP套接字上使用connect()的一个不错的技巧。它不发送流量,但可以让您找到将数据包发送到指定收件人的发件人地址。该端口可能无关紧要(即使0也可以)。在多宿主主机上,在正确的子网中选择一个地址很重要。
彼得·汉森

16
仅仅因为您可以在一行上编写该代码,并不意味着您应该...
JackLeo

91

您可以使用netifaces模块。只需输入:

pip install netifaces

在命令外壳中,它将在默认的Python安装中自行安装。

然后,您可以像这样使用它:

from netifaces import interfaces, ifaddresses, AF_INET
for ifaceName in interfaces():
    addresses = [i['addr'] for i in ifaddresses(ifaceName).setdefault(AF_INET, [{'addr':'No IP addr'}] )]
    print '%s: %s' % (ifaceName, ', '.join(addresses))

在我的计算机上打印:

{45639BDC-1050-46E0-9BE9-075C30DE1FBC}:192.168.0.100
{D43A468B-F3AE-4BF9-9391-4863A4500583} :: 10.5.9.207

该模块的作者声称它应该可以在Windows,UNIX和Mac OS X上运行。


20
如问题中所述,我希望从默认安装中获得一些东西,因为不需要其他安装。
UnkwnTech,

4
这是我最喜欢的答案,除了netifaces在Windows上不支持IPv6且看起来没有维护。有谁知道如何在Windows上获取IPv6地址?
Jean-Paul Calderone

3
netifaces不支持py3k,并且需要一个C编译器,该编译器是Windows上的PITA。
马特·乔纳

4
@MattJoiner这两种情况都不再成立(最新版本在PyPI上具有Windows二进制文件,并且确实支持Py3K)。
alastair 2014年

4
@ Jean-PaulCalderone FWIW,最新版本的netifaces 确实在Windows 支持IPv6。
alastair 2014年

47

套接字API方法

参见https://stackoverflow.com/a/28950776/711085

缺点:

  • 不是跨平台的。
  • 需要更多后备代码,与互联网上特定地址的存在相关
  • 如果您在使用NAT,这也将不起作用
  • 可能会创建UDP连接,而不依赖于(通常是ISP的)DNS可用性(请参阅其他答案,以获取使用8.8.8.8的想法:Google的(偶然也是DNS)服务器)
  • 确保将目标地址设置为UNREACHABLE,例如指定不使用的数字IP地址。请勿使用诸如fakesubdomain.google.com或somefakewebsite.com之类的域;您仍然会向该方发送垃圾邮件(无论现在还是将来),并在此过程中向您自己的网络邮箱发送垃圾邮件。

反射法

(请注意,这不能回答OP的本地IP地址问题,例如192.168 ...;它为您提供了公共IP地址,根据使用情况,可能更需要此IP地址。)

您可以查询某些网站,例如whatismyip.com(但使用API​​),例如:

from urllib.request import urlopen
import re
def getPublicIp():
    data = str(urlopen('http://checkip.dyndns.com/').read())
    # data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'

    return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)

或者如果使用python2:

from urllib import urlopen
import re
def getPublicIp():
    data = str(urlopen('http://checkip.dyndns.com/').read())
    # data = '<html><head><title>Current IP Check</title></head><body>Current IP Address: 65.96.168.198</body></html>\r\n'

    return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)

优点:

  • 这种方法的一个好处是它是跨平台的
  • 它可以从丑陋的NAT(例如您的家庭路由器)后面运行。

缺点(和解决方法):

  • 要求此网站正常运行,格式不得更改(几乎肯定不会更改),并且您的DNS服务器正常工作。如果发生故障,还可以通过查询其他第三方IP地址反射器来缓解此问题。
  • 如果您不查询多个反射器(以防止受损的反射器告诉您您的地址不是它的东西),或者如果您不使用HTTPS(以防止中间人的攻击),则可能是攻击向量成为服务器)

编辑:尽管起初我以为这些方法确实很糟糕(除非您使用了许多后备功能,否则从现在起很多年后代码都将是无关紧要的),但确实提出了“什么是互联网?”的问题。一台计算机可能具有指向许多不同网络的许多接口。有关主题的更详尽说明,请使用googlegateways and routes。计算机可能能够通过内部网关访问内部网络,或者通过例如路由器上的网关访问万维网(通常是这种情况)。OP询问的本地IP地址仅在单个链路层上定义明确,因此您必须指定(“我们正在谈论的是网卡还是以太网电缆?”) 。提出的这个问题可能有多个非唯一答案。但是,万维网上的全局IP地址可能是定义明确的(在没有大量网络碎片的情况下):可能是通过可以访问TLD的网关返回的路径。


如果您位于NAT之后,这将返回您的LAN范围的地址。如果要连接到Internet,则可以连接到返回您的公共IP地址之一的Web服务。
phihag 2011年

它不会创建TCP连接,因为会创建UDP连接。
2013年

2
作为套接字API版本中的替代方法,将s.connect(('INSERT SOME TARGET WEBSITE.com',0))替换为s.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1); s.connect((' broadcast>',0)),以避免DNS查找。(我想如果有防火墙,广播可能会出问题)
dlm

45

如果计算机具有通往Internet的路由,即使/ etc / hosts设置不正确,它始终可以获取首选的本地ip地址。

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 1))  # connect() for UDP doesn't send packets
local_ip_address = s.getsockname()[0]

这是如何运作的 ?,8.8.8.8是Google dns服务器吗,我们可以使用本地dns服务器吗?
Ciasto piekarz

39

在Linux上:

>>> import socket, struct, fcntl
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> sockfd = sock.fileno()
>>> SIOCGIFADDR = 0x8915
>>>
>>> def get_ip(iface = 'eth0'):
...     ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
...     try:
...         res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
...     except:
...         return None
...     ip = struct.unpack('16sH2x4s8x', res)[2]
...     return socket.inet_ntoa(ip)
... 
>>> get_ip('eth0')
'10.80.40.234'
>>> 

因此,这有效地打开了一个无用的套接字,并且您检查了有关该套接字的原始数据以获取本地IP?
戴夫

1
打开套接字以获取fd与内核通信(通过ioctl)。套接字未绑定要添加其相关信息的接口,它只是用户空间和内核之间的一种通信机制。en.wikipedia.org/wiki/Ioctl lxr.free-electrons.com/source/net/socket.c
TMC

2
在Python3上进行了一次修改:struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)可替换为struct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
pepoluan 2014年

1
@ChristianFischer ioctl是一个传统接口,我认为它不支持IPv6,而且可能永远也不会。我认为“正确”的方法是通过Netlink,这在Python中不是很简单。我认为libc应该具有getifaddrs可以通过ctypes可能有效的pythons
tMC 2014年

1
@Maddy ioctl是一个传统接口,我认为它不支持IPv6,而且可能永远也不会。我认为“正确”的方法是通过Netlink,这在Python中不是很简单。我认为libc应该具有可以通过pythons ctypes模块访问的getifaddrs函数,该模块可能有效-man7.org/linux/man-pages/man3/getifaddrs.3.html
tMC 2014年

27

即时通讯使用以下模块:

#!/usr/bin/python
# module for getting the lan ip address of the computer

import os
import socket

if os.name != "nt":
    import fcntl
    import struct
    def get_interface_ip(ifname):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        return socket.inet_ntoa(fcntl.ioctl(
                s.fileno(),
                0x8915,  # SIOCGIFADDR
                struct.pack('256s', bytes(ifname[:15], 'utf-8'))
                # Python 2.7: remove the second argument for the bytes call
            )[20:24])

def get_lan_ip():
    ip = socket.gethostbyname(socket.gethostname())
    if ip.startswith("127.") and os.name != "nt":
        interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
        for ifname in interfaces:
            try:
                ip = get_interface_ip(ifname)
                break;
            except IOError:
                pass
    return ip

经过Windows和Linux(并不需要其他模块)测试,旨在用于基于单个IPv4的LAN中的系统。

接口名称的固定列表不适用于最新的Linux版本,这些版本采用了Alexander指出的有关可预测接口名称的systemd v197更改。在这种情况下,您需要使用系统上的接口名称手动替换列表,或使用其他解决方案(如netifaces)


2
这与新的可预测Linux接口名称不兼容,例如enp0s25。有关更多信息,请参阅wiki.archlinux.org/index.php/Network_Configuration#Device_names
Alexander

2
我正在使用python 3.4,需要将'struct.pack(...)'部分更改为'struct.pack('256s',bytes(ifname [:15],'utf-8'))'。看到这个问题:stackoverflow.com/q/27391167/76010
Bakanekobrain 2015年

1
在带有Python 2.7.3的Raspbian上-bytes()不喜欢第二个参数。但这有效: struct.pack('256s', bytes(ifname[:15]))
colm.anseo,2015年

24

我在我的ubuntu机器上使用它:

import commands
commands.getoutput("/sbin/ifconfig").split("\n")[1].split()[1][5:]

这行不通。


漂亮又简单。也可以在Amazon的Linux AMI上运行,但前提是我是root。否则我会得到一个错误:'sh:ifconfig:命令未找到'
IgorGanapolsky 2010年

因此,您应该像gavaletz所说的那样使用“ / sbin / ifconfig”。它还适用于Red Hat 4.1.2-48。
IgorGanapolsky's

7
自2.6起不推荐使用。使用子流程模块运行命令。
Colin Dunklau 2013年

5
而且不建议使用ifconfig。使用iproute2。
Helmut Grohne

获取所有ips:import sh; [ip.split()[1] [5:]用于过滤器中的ip(lambda x:x中的'inet addr',sh.ifconfig()。split(“ \ n”))]
Gabriel Littman

20

如果您不想使用外部软件包,也不想依赖外部Internet服务器,则可能会有所帮助。这是我在Google代码搜索中找到并修改为返回所需信息的代码示例:

def getIPAddresses():
    from ctypes import Structure, windll, sizeof
    from ctypes import POINTER, byref
    from ctypes import c_ulong, c_uint, c_ubyte, c_char
    MAX_ADAPTER_DESCRIPTION_LENGTH = 128
    MAX_ADAPTER_NAME_LENGTH = 256
    MAX_ADAPTER_ADDRESS_LENGTH = 8
    class IP_ADDR_STRING(Structure):
        pass
    LP_IP_ADDR_STRING = POINTER(IP_ADDR_STRING)
    IP_ADDR_STRING._fields_ = [
        ("next", LP_IP_ADDR_STRING),
        ("ipAddress", c_char * 16),
        ("ipMask", c_char * 16),
        ("context", c_ulong)]
    class IP_ADAPTER_INFO (Structure):
        pass
    LP_IP_ADAPTER_INFO = POINTER(IP_ADAPTER_INFO)
    IP_ADAPTER_INFO._fields_ = [
        ("next", LP_IP_ADAPTER_INFO),
        ("comboIndex", c_ulong),
        ("adapterName", c_char * (MAX_ADAPTER_NAME_LENGTH + 4)),
        ("description", c_char * (MAX_ADAPTER_DESCRIPTION_LENGTH + 4)),
        ("addressLength", c_uint),
        ("address", c_ubyte * MAX_ADAPTER_ADDRESS_LENGTH),
        ("index", c_ulong),
        ("type", c_uint),
        ("dhcpEnabled", c_uint),
        ("currentIpAddress", LP_IP_ADDR_STRING),
        ("ipAddressList", IP_ADDR_STRING),
        ("gatewayList", IP_ADDR_STRING),
        ("dhcpServer", IP_ADDR_STRING),
        ("haveWins", c_uint),
        ("primaryWinsServer", IP_ADDR_STRING),
        ("secondaryWinsServer", IP_ADDR_STRING),
        ("leaseObtained", c_ulong),
        ("leaseExpires", c_ulong)]
    GetAdaptersInfo = windll.iphlpapi.GetAdaptersInfo
    GetAdaptersInfo.restype = c_ulong
    GetAdaptersInfo.argtypes = [LP_IP_ADAPTER_INFO, POINTER(c_ulong)]
    adapterList = (IP_ADAPTER_INFO * 10)()
    buflen = c_ulong(sizeof(adapterList))
    rc = GetAdaptersInfo(byref(adapterList[0]), byref(buflen))
    if rc == 0:
        for a in adapterList:
            adNode = a.ipAddressList
            while True:
                ipAddr = adNode.ipAddress
                if ipAddr:
                    yield ipAddr
                adNode = adNode.next
                if not adNode:
                    break

用法:

>>> for addr in getIPAddresses():
>>>    print addr
192.168.0.100
10.5.9.207

由于它依赖windll,因此仅在Windows上有效。


上面的一种衬管解决方案通常在窗户上工作。问题是Linux操作系统。
ricree

14
+1此技术至少尝试返回机器上的所有地址。
詹森·库姆斯

1
返回第一个地址后,该脚本在我的机器上失败。错误是“ AttributeError:'LP_IP_ADDR_STRING'对象没有属性'ipAddress'”我怀疑它与IPv6地址有关。
詹森·库姆斯

1
事实证明,问题在于除了第一个IP地址外,adNode均未取消引用。在while循环中向示例添加另一行,它对我有用:adNode = adNode.contents
Jason R. Coombs 2009年

18

在Debian(经过测试)上,我怀疑大多数Linux都可以。

import commands

RetMyIP = commands.getoutput("hostname -I")

在MS Windows上(已测试)

import socket

socket.gethostbyname(socket.gethostname())

1
在macOS上不起作用:hostname: illegal option -- I\nusage: hostname [-fs] [name-of-host]
Derek朕会功夫

18

我不认为该版本已经发布。我在Ubuntu 12.04上使用python 2.7进行了测试。

在以下位置找到了此解决方案:http : //code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/

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')
'38.113.228.130'

2
适用于Python3,Ubuntu 18.04;字符串必须是字节:>>> socket.inet_ntoa(fcntl.ioctl(s.fileno(),0x8915,struct.pack('256s','enp0s31f6'[:15] .encode('utf-8')) ))[20:24])'192.168.1.1'–
始于

12

Ninjagecko的答案有所不同。这应该在允许UDP广播并且不需要访问LAN或Internet上的地址的任何LAN上都有效。

import socket
def getNetworkIp():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.connect(('<broadcast>', 0))
    return s.getsockname()[0]

print (getNetworkIp())

等待,<broadcast>有效的主机名如何?这些口头主机名中有多少有效?
Dev Aggarwal,

这对我在Ubuntu 20.04上有效-获取192.168.0.24而不是127.0.0.1
Lewis Morris


8

通过命令行实用程序产生“干净”输出的一种简单方法:

import commands
ips = commands.getoutput("/sbin/ifconfig | grep -i \"inet\" | grep -iv \"inet6\" | " +
                         "awk {'print $2'} | sed -ne 's/addr\:/ /p'")
print ips

它将显示系统上的所有IPv4地址。


1
它不会显示所有IPv4地址,因为ifconfig仅告诉您主要地址。您需要使用iproute2中的“ ip”来查看所有地址。
Helmut Grohne 2013年

询问标准库的问题真是令人难为情……而且,解析ifconfig既不可移植,也无法在一台机器上可靠地工作。
多米尼克·乔治

7

仅供参考,我可以验证该方法:

import socket
addr = socket.gethostbyname(socket.gethostname())

在OS X(10.6,10.5),Windows XP和管理良好的RHEL部门服务器上均可使用。它不能在非常小的CentOS VM上工作,而我只是对其进行一些内核黑客攻击。因此,对于该实例,您只需检查127.0.0.1地址,然后执行以下操作:

if addr == "127.0.0.1":
     import commands
     output = commands.getoutput("/sbin/ifconfig")
     addr = parseaddress(output)

然后从输出中解析IP地址。应当注意,默认情况下ifconfig不在普通用户的PATH中,这就是为什么我在命令中提供完整路径的原因。我希望这有帮助。


7

这是UnkwnTech回答的一种变体,它提供了一个get_local_addr()函数,该函数返回主机的主要LAN ip地址。我发布它是因为这增加了许多东西:ipv6支持,错误处理,忽略localhost / linklocal地址,并使用TESTNET地址(rfc5737)连接。

# imports
import errno
import socket
import logging

# localhost prefixes
_local_networks = ("127.", "0:0:0:0:0:0:0:1")

# ignore these prefixes -- localhost, unspecified, and link-local
_ignored_networks = _local_networks + ("0.", "0:0:0:0:0:0:0:0", "169.254.", "fe80:")

def detect_family(addr):
    if "." in addr:
        assert ":" not in addr
        return socket.AF_INET
    elif ":" in addr:
        return socket.AF_INET6
    else:
        raise ValueError("invalid ipv4/6 address: %r" % addr)

def expand_addr(addr):
    """convert address into canonical expanded form --
    no leading zeroes in groups, and for ipv6: lowercase hex, no collapsed groups.
    """
    family = detect_family(addr)
    addr = socket.inet_ntop(family, socket.inet_pton(family, addr))
    if "::" in addr:
        count = 8-addr.count(":")
        addr = addr.replace("::", (":0" * count) + ":")
        if addr.startswith(":"):
            addr = "0" + addr
    return addr

def _get_local_addr(family, remote):
    try:
        s = socket.socket(family, socket.SOCK_DGRAM)
        try:
            s.connect((remote, 9))
            return s.getsockname()[0]
        finally:
            s.close()
    except socket.error:
        # log.info("trapped error connecting to %r via %r", remote, family, exc_info=True)
        return None

def get_local_addr(remote=None, ipv6=True):
    """get LAN address of host

    :param remote:
        return  LAN address that host would use to access that specific remote address.
        by default, returns address it would use to access the public internet.

    :param ipv6:
        by default, attempts to find an ipv6 address first.
        if set to False, only checks ipv4.

    :returns:
        primary LAN address for host, or ``None`` if couldn't be determined.
    """
    if remote:
        family = detect_family(remote)
        local = _get_local_addr(family, remote)
        if not local:
            return None
        if family == socket.AF_INET6:
            # expand zero groups so the startswith() test works.
            local = expand_addr(local)
        if local.startswith(_local_networks):
            # border case where remote addr belongs to host
            return local
    else:
        # NOTE: the two addresses used here are TESTNET addresses,
        #       which should never exist in the real world.
        if ipv6:
            local = _get_local_addr(socket.AF_INET6, "2001:db8::1234")
            # expand zero groups so the startswith() test works.
            if local:
                local = expand_addr(local)
        else:
            local = None
        if not local:
            local = _get_local_addr(socket.AF_INET, "192.0.2.123")
            if not local:
                return None
    if local.startswith(_ignored_networks):
        return None
    return local

我以为这本来可以是一个很好的答案..但是它总是会返回None
Jamie Lindsey

@JamieLindsey您是否了解有关操作系统,网络配置的详细信息?另外,类似的get_local_addr(remove="www.google.com")回报是什么?记录socket.error_get_local_addr()引发的记录可能会有助于诊断。
Eli Collins

6
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]

1
嗯...有两个网卡的服务器上这给一个指定的IP地址,但重复三次。在我的笔记本电脑上,它显示为“ 127.0.1.1”(重复了三遍...)...
bryn

['fe80::34e8:fe19:1459:2cde%22','fe80::d528:99fb:d572:e289%12', '192.168.56.1', '192.168.1.2']在Windows桌面上给我。
Nakilon 2014年

5

这将适用于大多数linux系统:

import socket, subprocess, re
def get_ipv4_address():
    """
    Returns IP address(es) of current machine.
    :return:
    """
    p = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE)
    ifc_resp = p.communicate()
    patt = re.compile(r'inet\s*\w*\S*:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
    resp = patt.findall(ifc_resp[0])
    print resp

get_ipv4_address()

5

这个答案是我个人为解决获得LAN IP问题而进行的个人尝试,因为它socket.gethostbyname(socket.gethostname())也返回了127.0.0.1。此方法不需要Internet,仅需要LAN连接即可。代码适用于Python 3.x,但可以轻松转换为2.x。使用UDP广播:

import select
import socket
import threading
from queue import Queue, Empty

def get_local_ip():
        def udp_listening_server():
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.bind(('<broadcast>', 8888))
            s.setblocking(0)
            while True:
                result = select.select([s],[],[])
                msg, address = result[0][0].recvfrom(1024)
                msg = str(msg, 'UTF-8')
                if msg == 'What is my LAN IP address?':
                    break
            queue.put(address)

        queue = Queue()
        thread = threading.Thread(target=udp_listening_server)
        thread.queue = queue
        thread.start()
        s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        waiting = True
        while waiting:
            s2.sendto(bytes('What is my LAN IP address?', 'UTF-8'), ('<broadcast>', 8888))
            try:
                address = queue.get(False)
            except Empty:
                pass
            else:
                waiting = False
        return address[0]

if __name__ == '__main__':
    print(get_local_ip())

1
如果同时在同一网络上的两台计算机上运行此命令,会发生什么情况?当您在网络上广播消息时,所有机器都将收到“我的LAN IP地址是什么。您的udp_listening_server可以回复消息“您的IP地址为xxx”。
Nicolas Defranoux 2015年

4

127.0.1.1 您的真实IP地址。一般来说,计算机可以具有任意数量的IP地址。您可以为专用网络过滤它们-127.0.0.0/8、10.0.0.0/8、172.16.0.0/12和192.168.0.0/16。

但是,没有跨平台的方法来获取所有IP地址。在Linux上,您可以使用SIOCGIFCONFioctl。


2
他表示他的外部可见IP。127。*。*。*范围通常是指localhost或内部网络,这显然不是他想要的。
塞林(Cerin)2012年

4

使用IP命令并返回IPv4和IPv6地址的命令版本略有改进:

import commands,re,socket

#A generator that returns stripped lines of output from "ip address show"
iplines=(line.strip() for line in commands.getoutput("ip address show").split('\n'))

#Turn that into a list of IPv4 and IPv6 address/mask strings
addresses1=reduce(lambda a,v:a+v,(re.findall(r"inet ([\d.]+/\d+)",line)+re.findall(r"inet6 ([\:\da-f]+/\d+)",line) for line in iplines))
#addresses1 now looks like ['127.0.0.1/8', '::1/128', '10.160.114.60/23', 'fe80::1031:3fff:fe00:6dce/64']

#Get a list of IPv4 addresses as (IPstring,subnetsize) tuples
ipv4s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if '.' in addr)]
#ipv4s now looks like [('127.0.0.1', 8), ('10.160.114.60', 23)]

#Get IPv6 addresses
ipv6s=[(ip,int(subnet)) for ip,subnet in (addr.split('/') for addr in addresses1 if ':' in addr)]

4

好了,您可以在GNU / Linux上使用命令“ ip route”来知道您当前的IP地址。

这显示了路由器/调制解调器上运行的DHCP服务器为接口提供的IP。通常,“ 192.168.1.1/24”是本地网络的IP,其中“ 24”表示DHCP服务器在掩码范围内指定的可能IP地址范围。

这是一个示例:请注意,PyNotify只是为了使我的观点更清楚而已,根本不需要

#! /usr/bin/env python

import sys , pynotify

if sys.version_info[1] != 7:
   raise RuntimeError('Python 2.7 And Above Only')       

from subprocess import check_output # Available on Python 2.7+ | N/A 

IP = check_output(['ip', 'route'])
Split_Result = IP.split()

# print Split_Result[2] # Remove "#" to enable

pynotify.init("image")
notify = pynotify.Notification("Ip", "Server Running At:" + Split_Result[2] , "/home/User/wireless.png")    
notify.show()    

这样做的好处是您无需指定网络接口。这在运行套接字服务器时非常有用

您可以使用easy_install甚至Pip安装PyNotify:

easy_install py-notify

要么

pip install py-notify

或在python脚本/解释器中

from pip import main

main(['install', 'py-notify'])

4

如果您正在寻找与本地IP地址不同的IPV4地址127.0.0.1,这是一个整洁的python代码:

import subprocess
address = subprocess.check_output(['hostname', '-s', '-I'])
address = address.decode('utf-8') 
address=address[:-1]

也可以单行编写:

address = subprocess.check_output(['hostname', '-s', '-I']).decode('utf-8')[:-1]

即使您输入localhost/etc/hostname,代码仍会提供您的本地IP地址。




3

netifaces可通过pip和easy_install获得。(我知道,它不在基础中,但是值得安装。)

netifaces的跨平台确实有些奇怪:

  • 不一定总是包含localhost / loop-back接口(Cygwin)。
  • 每个协议列出了地址(例如IPv4,IPv6),每个接口列出了协议。在某些系统(Linux)上,每个协议接口对都有自己的关联接口(使用interface_name:n表示法),而在其他系统(Windows)上,单个接口将具有每个协议的地址列表。在这两种情况下,都有一个协议列表,但是它可能只包含一个元素。

以下是一些netifaces代码可用于:

import netifaces

PROTO = netifaces.AF_INET   # We want only IPv4, for now at least

# Get list of network interfaces
# Note: Can't filter for 'lo' here because Windows lacks it.
ifaces = netifaces.interfaces()

# Get all addresses (of all kinds) for each interface
if_addrs = [netifaces.ifaddresses(iface) for iface in ifaces]

# Filter for the desired address type
if_inet_addrs = [addr[PROTO] for addr in if_addrs if PROTO in addr]

iface_addrs = [s['addr'] for a in if_inet_addrs for s in a if 'addr' in s]
# Can filter for '127.0.0.1' here.

上面的代码不会将地址映射回其接口名称(用于动态生成ebtables / iptables规则)。因此,这是一个将上述信息和接口名称保存在元组中的版本:

import netifaces

PROTO = netifaces.AF_INET   # We want only IPv4, for now at least

# Get list of network interfaces
ifaces = netifaces.interfaces()

# Get addresses for each interface
if_addrs = [(netifaces.ifaddresses(iface), iface) for iface in ifaces]

# Filter for only IPv4 addresses
if_inet_addrs = [(tup[0][PROTO], tup[1]) for tup in if_addrs if PROTO in tup[0]]

iface_addrs = [(s['addr'], tup[1]) for tup in if_inet_addrs for s in tup[0] if 'addr' in s]

而且,不,我不喜欢列表理解。这些天就是我的大脑运作的方式。

以下代码片段将全部打印出来:

from __future__ import print_function  # For 2.x folks
from pprint import pprint as pp

print('\nifaces = ', end='')
pp(ifaces)

print('\nif_addrs = ', end='')
pp(if_addrs)

print('\nif_inet_addrs = ', end='')
pp(if_inet_addrs)

print('\niface_addrs = ', end='')
pp(iface_addrs)

请享用!


netifaces在处理此问题时确实可以极大地减轻生活负担。
Drake Guan

3

使用新引入的asyncio软件包的Python 3.4版本。

async get_local_ip():
    loop = asyncio.get_event_loop()
    transport, protocol = await loop.create_datagram_endpoint(
        asyncio.DatagramProtocol,
        remote_addr=('8.8.8.8', 80))
    result = transport.get_extra_info('sockname')[0])
    transport.close()
    return result

这是基于UnkwnTech的出色回答


3

要获取IP地址,您可以直接在python中使用shell命令

import socket, subprocess

def getIpAndHostname():
    hostname =  socket.gethostname()

    shell_cmd = "ifconfig | awk '/inet addr/{print substr($2,6)}'"
    proc = subprocess.Popen([shell_cmd], stdout=subprocess.PIPE, shell=True)
    (out, err) = proc.communicate()

    ip_list = out.split('\n')
    ip = ip_list[0]

    for _ip in ip_list:
        try:
            if _ip != "127.0.0.1" and _ip.split(".")[3] != "1":
                ip = _ip
        except:
            pass
    return ip, hostname

ip_addr, hostname = getIpAndHostname()
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.