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

时间:2020-03-06 15:04:16  来源:igfitidea点击:

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

解决方案

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

这将永远无法正常工作(在主机名在/ etc / hosts中的机器上以127.0.0.1的形式返回127.0.0.1),gimel显示了一种警告,请改用socket.getfqdn()。 。当然,计算机需要可解析的主机名。

恐怕除了连接到另一台计算机并将其发送给IP地址之外,没有任何独立于平台的良好方法可以做到这一点。例如:findmyipaddress。请注意,除非我们需要的IP地址位于NAT后面,否则这将无法工作,除非要连接的计算机也位于NAT后面。

这是在Linux中工作的一种解决方案:获取与网络接口关联的IP地址。

我刚刚找到了它,但似乎有点破烂,但是他们说在* 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()

这假定我们可以访问Internet,并且没有本地代理。

我们可以使用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上运行。

如果我们不想使用外部软件包,也不想依赖外部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上使用。

作为一个名为" 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发行版上的工作,所以我尝试了这种替代技术。它尝试通过端口" 53"连接到位于" 8.8.8.8"的Google DNS服务器:

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地址可能比此处列出的任何方法都更加可靠和可靠。

即时通讯使用以下模块:

#!/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)。

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

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中,这就是为什么我在命令中提供完整路径的原因。我希望这有帮助。

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

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

这是行不通的。