繁体   English   中英

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

[英]Finding local IP addresses using Python's stdlib

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

我刚刚发现了这个,但它似乎有点骇人听闻,但是他们说在 *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,并且没有本地代理。

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

这并不总是有效(在主机名在/etc/hosts127.0.0.1的机器上返回127.0.0.1 ),gimel 显示的是缓和,请改用socket.getfqdn() 当然,您的机器需要一个可解析的主机名。

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

  • 根本不需要可路由的网络访问或任何连接。
  • 即使所有接口都从网络中拔出,也可以工作。
  • 不需要甚至尝试去其他任何地方
  • 适用于 NAT、公共、私有、外部和内部 IP
  • 没有外部依赖的纯 Python 2(或 3)。
  • 适用于 Linux、Windows 和 OSX。

Python 3 或 2:

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

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

如果您在家里的 wifi 路由器等 NAT 防火墙后面,那么这将不会显示您的公共 NAT IP,而是显示您在本地网络上的私有 IP,该 IP 具有到本地 WIFI 路由器的默认路由。 如果您需要外部 IP:

  • 在那个外部设备(wifi路由器)上运行这个功能,或者

  • 连接到外部服务,例如https://www.ipify.org/可以反映从外部世界看到的 IP

...但是这些想法与原始问题完全不同。 :)

作为名为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 的机器,将不会返回正确的结果。
  • 据报道,这不适用于最新版本的 macOS。

注意:如果您打算在 Python 程序中使用类似的东西,正确的方法是使用支持 IPv6 的 Python 模块。


与上面相同,但只有 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 和 Windows 上的 Python 2 和 3,但不能处理多个网络设备或 IPv6。 但是,它在最近的 Linux 发行版上停止工作,所以我尝试了这种替代技术。 它尝试在8.8.8.8端口53连接到 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 地址可能比此处列出的任何方法都更加健壮和可靠。

您可以使用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 的路由,即使 /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]

套接字 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 地址,根据用例,这可能更可取。)

您可以查询一些站点,例如 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(以防止中间人攻击假装),则可能的攻击向量成为服务器)

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

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

我使用以下模块:

#!/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 版本,这些版本采用了 systemd v197 关于可预测接口名称的更改,正如Alexander指出的那样。 在这种情况下,您需要手动将列表替换为系统上的接口名称,或者使用其他解决方案,例如netifaces

[仅限 Windows]如果您不想使用外部软件包并且不想依赖外部 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。

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

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

这行不通。

忍者的答案的变化。 这应该适用于任何允许 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())

在 Debian(经过测试)上,我怀疑大多数 Linux 的..

import commands

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

在 MS Windows 上(已测试)

import socket

socket.gethostbyname(socket.gethostname())

一个我不相信的版本已经发布了。 我在 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'

这是 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

对于 linux,您可以像这样使用hostname -I系统命令的check_output

from subprocess import check_output
check_output(['hostname', '-I'])

恐怕除了连接到另一台计算机并让它向您发送您的 IP 地址之外,没有任何独立于平台的好的方法可以做到这一点。 例如: findmyipaddress 请注意,如果您需要一个位于 NAT 后面的 IP 地址,除非您要连接的计算机也在 NAT 后面,否则这将不起作用。

这是一种适用于 Linux 的解决方案:获取与网络接口关联的 IP 地址

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

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

适用于 OS X (10.6,10.5)、Windows XP 和管理良好的 RHEL 部门服务器。 它不适用于一个非常小的 CentOS 虚拟机,我只是对它进行了一些内核黑客攻击。 因此,对于这种情况,您只需检查 127.0.0.1 地址,在这种情况下执行以下操作:

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

然后从输出中解析 ip 地址。 需要注意的是 ifconfig 默认不在普通用户的 PATH 中,这就是我在命令中给出完整路径的原因。 我希望这有帮助。

通过命令行工具产生“干净”输出的一种简单方法:

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

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

这将适用于大多数 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()

这个答案是我个人尝试解决获取局域网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())
import socket
[i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]

如果您正在寻找与您的本地主机 IP 地址127.0.0.1不同的 IPV4 地址,这里有一段简洁的 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 地址。

使用 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)]

那么您可以在 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'])

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)

享受!

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

async def 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 的出色回答

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

import socket, subprocess

def get_ip_and_hostname():
    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 = get_ip_and_hostname()

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 上,您可以使用SIOCGIFCONF ioctl。

注意:这不是使用标准库,但很简单。

$ pip 安装 pif

from pif import get_public_ip
get_public_ip()
import netifaces as ni 

ni.ifaddresses('eth0')
ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr']
print(ip)

这将返回您在 Ubuntu 系统以及 MacOS 中的 IP 地址。 输出将是系统 IP 地址,就像我的 IP:192.168.1.10。

对于 *nix 系统上的 IP 地址列表,

import subprocess
co = subprocess.Popen(['ifconfig'], stdout = subprocess.PIPE)
ifconfig = co.stdout.read()
ip_regex = re.compile('((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?))')
[match[0] for match in ip_regex.findall(ifconfig, re.MULTILINE)]

虽然这个答案有点晚了,但我认为其他人可能会觉得它很有用:-)

PS:它也会返回广播地址和网络掩码。

我必须解决“弄清楚一个 IP 地址是否是本地的”这个问题,我的第一个想法是建立一个本地 IP 列表,然后与之匹配。 这就是导致我提出这个问题的原因。 但是,我后来意识到有一种更直接的方法可以做到这一点:尝试绑定该 IP 并查看它是否有效。

_local_ip_cache = []
_nonlocal_ip_cache = []
def ip_islocal(ip):
    if ip in _local_ip_cache:
        return True
    if ip in _nonlocal_ip_cache:
        return False
    s = socket.socket()
    try:
        try:
            s.bind((ip, 0))
        except socket.error, e:
            if e.args[0] == errno.EADDRNOTAVAIL:
                _nonlocal_ip_cache.append(ip)
                return False
            else:
                raise
    finally:
        s.close()
    _local_ip_cache.append(ip)
    return True

我知道这并不能直接回答这个问题,但这应该对任何试图解决相关问题并且遵循相同思路的人有所帮助。 这具有成为跨平台解决方案的优势(我认为)。

好的,这是特定于 Windows 的,并且需要安装python WMI 模块,但它似乎比不断尝试调用外部服务器要少得多。 这只是另一种选择,因为已经有很多好的选择,但它可能非常适合您的项目。

Import WMI

def getlocalip():
    local = wmi.WMI()
    for interface in local.Win32_NetworkAdapterConfiguration(IPEnabled=1):
        for ip_address in interface.IPAddress:
            if ip_address != '0.0.0.0':
                localip = ip_address
    return localip







>>>getlocalip()
u'xxx.xxx.xxx.xxx'
>>>

顺便说一句,WMI 非常强大……如果您正在对窗口机器进行任何远程管理,您绝对应该检查一下它可以做什么。

import socket
socket.gethostbyname(socket.getfqdn())

这不是 Pythonic,但它可以在 Windows 上可靠地工作。

def getWinIP(version = 'IPv4'):
    import subprocess
    if version not in ['IPv4', 'IPv6']:
        print 'error - protocol version must be "IPv4" or "IPv6"'
        return None
    ipconfig = subprocess.check_output('ipconfig')
    my_ip = []
    for line in ipconfig.split('\n'):
        if 'Address' in line and version in line:
            my_ip.append(line.split(' : ')[1].strip())
    return my_ip

print getWinIP()

是的,这是一个 hack,但有时我不想对操作系统进行第二次猜测,而是继续使用内置且有效的东西。

from netifaces import interfaces, ifaddresses, AF_INET
iplist = [ifaddresses(face)[AF_INET][0]["addr"] for face in interfaces() if AF_INET in ifaddresses(face)]
print(iplist)
['10.8.0.2', '192.168.1.10', '127.0.0.1']

先前答案的另一个变体可以保存到名为my-ip-to的可执行脚本中:

#!/usr/bin/env python

import sys, socket

if len(sys.argv) > 1:
    for remote_host in sys.argv[1:]:
        # determine local host ip by outgoing test to another host
        # use port 9 (discard protocol - RFC 863) over UDP4
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.connect((remote_host, 9))
            my_ip = s.getsockname()[0]
            print(my_ip, flush=True)
else:
    import platform

    my_name = platform.node()
    my_ip = socket.gethostbyname(my_name)
    print(my_ip)

它需要任意数量的远程主机,并打印出本地 ip 以一一访问它们:

$ my-ip-to z.cn g.cn localhost
192.168.11.102
192.168.11.102
127.0.0.1
$

并在没有给出 arg 时打印最佳选择。

$ my-ip-to
192.168.11.102

对于 linux 环境,读取 /proc/net/tcp,第二个(本地地址)和第三个(远程地址)将以 hexa 格式给出 IP。

提示:如果第二列归零(00000000:0000),那么它是一个监听端口:)

https://github.com/romol0s/python/blob/master/general/functions/getTcpListenIpsByPort.py

https://www.kernel.org/doc/Documentation/networking/proc_net_tcp.txt

您提到的一台机器可以有多个网络接口(包括本地环回 127.0.0.1)。 就操作系统而言,它也是一个“真实IP地址”。

如果您想跟踪所有接口,请查看以下 Puthon 包: http ://alastairs-place.net/netifaces/

如果您从主机文件中省略环回条目,我认为您可以避免让 gethostbyname 返回 127.0.0.1 。 (待验证)。

Windows 解决方案,要么接受,要么离开。

在当前活动的 wlan[无线 LAN] 上仅获取自身 ip,即(wifi 路由器或网络交换机)上的计算机 ip。

注意:它不是设备的公共 ip,不涉及任何外部请求或包或公共 api。

核心思想是解析shell command: ipconfig ,或者linux上的 ifconfig 。 我们正在使用 subprocess 来获取输出。

def wlan_ip():
    import subprocess
    result=subprocess.run('ipconfig',stdout=subprocess.PIPE,text=True).stdout.lower()
    scan=0
    for i in result.split('\n'):
        if 'wireless' in i: #use "wireless" or wireless adapters and "ethernet" for wired connections
            scan=1
        if scan:
            if 'ipv4' in i:
                return i.split(':')[1].strip()
print(wlan_ip())

这是 CMD:'ipconfig' 之后发生的事情:

我们得到这个输出,我们使用子进程输出在 python 中捕获它。

C:\用户\戴尔>ipconfig

Wireless LAN adapter Wi-Fi:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::f485:4a6a:e7d5:1b1c%4
   IPv4 Address. . . . . . . . . . . : 192.168.0.131
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.0.1

我们在 python 中解析了字符串,以选择当前网络上无线适配器的 ip 的方式。

您可以在具有iproute2实用程序的现代 *NIX 系统上轻松执行此操作,方法是通过 subprocess.run( subprocess.run()调用它,因为您可以使用-j开关以 JSON 格式输出,然后使用json.loads()模块和方法进行转换到python数据结构。 以下代码显示第一个非环回 IP 地址。

import subprocess
import json

ip = json.loads(subprocess.run('ip -j a'.split(),capture_output=True).stdout.decode())[1]['addr_info'][0]['local'] 

print(ip)

或者,如果您有多个 IP,并且想要找到用于连接到特定目的地的 IP,您可以使用ip -j route get 8.8.8.8 ,如下所示:

import subprocess 
import json 

ip = json.loads(subprocess.run('ip -j route get 8.8.8.8'.split(),capture_output=True).stdout.decode())[0]['prefsrc']

print(ip)

如果您要查找所有 IP 地址,您可以遍历ip -ja返回的字典列表

import subprocess
import json

list_of_dicts = json.loads(subprocess.run('ip -j a'.split(),capture_output=True).stdout.decode())

for interface in list_of_dicts:
    try:print(f"Interface: {interface['ifname']:10} IP: {interface['addr_info'][0]['local']}")
    except:pass

您可以查看whatismyip模块中的代码来执行此操作。 它在 Python 3 标准库之外没有依赖项。 它连接到公共 STUN 服务器和 what-is-my-ip 网站以查找 IPv4 或 IPv6 地址。 运行pip install whatismyip

例子:

>>> import whatismyip
>>> whatismyip.amionline()
True
>>> whatismyip.whatismyip()  # Prefers IPv4 addresses, but can return either IPv4 or IPv6.
'69.89.31.226'
>>> whatismyip.whatismyipv4()
'69.89.31.226'
>>> whatismyip.whatismyipv6()
'2345:0425:2CA1:0000:0000:0567:5673:23b5'

我已经在此处修改了模块中的代码,因此您可以复制/粘贴它:

import urllib.request

IP_WEBSITES = (
           'https://ipinfo.io/ip',
           'https://ipecho.net/plain',
           'https://api.ipify.org',
           'https://ipaddr.site',
           'https://icanhazip.com',
           'https://ident.me',
           'https://curlmyip.net',
           )

def getIp():
    for ipWebsite in IP_WEBSITES:
        try:
            response = urllib.request.urlopen(ipWebsite)

            charsets = response.info().get_charsets()
            if len(charsets) == 0 or charsets[0] is None:
                charset = 'utf-8'  # Use utf-8 by default
            else:
                charset = charsets[0]

            userIp = response.read().decode(charset).strip()

            return userIp
        except:
            pass  # Network error, just continue on to next website.

    # Either all of the websites are down or returned invalid response
    # (unlikely) or you are disconnected from the internet.
    return None

print(getIp())

pyroute2 是一个很棒的库,它不仅可以用来获取 IP 地址,还可以用来获取网关信息和其他有用的信息。 以下代码可以获取任意接口的ipv4地址。

from pyroute2 import IPRoute
ip = IPRoute()

def get_ipv4_address(intf):
    return dict(ip.get_addr(label=intf)[0]['attrs'])['IFA_LOCAL']

print(get_ipv4_address('eth0'))

简单又甜美!

def getip():

    import socket
    hostname= socket.gethostname()
    ip=socket.gethostbyname(hostname)

    return(ip)

这与以前发布的答案非常相似,但我找不到任何使用这种调用的答案。 这是我用于 ipv4 的。 对于 ipv6 更改“。” 到“:”中

import socket
print next(i[4][0] for i in socket.getaddrinfo(
    socket.gethostname(), 80) if '127.' not in i[4][0] and '.' in i[4][0]);"

我决定使用ipfy的服务和/或 API: https ://www.ipify.org。

#!/usr/bin/env python3
from urllib.request import urlopen


def public_ip():
    data = urlopen('https://api.ipify.org').read()
    return str(data, encoding='utf-8')


print(public_ip())

也可以JSONJSONP格式获取响应。

Github 上有一个ipify Python 库

import socket
print(socket.gethostbyname(socket.getfqdn()))

@fatal_error 解决方案应该是公认的答案! 这是他在 nodejs 中的解决方案的实现,以防人们需要它:

const dgram = require('dgram');

async function get_local_ip() {
    const s = new dgram.createSocket('udp4');
    return new Promise((resolve, reject) => {
        try {
            s.connect(1, '8.8.8.8', function () {
                const ip = s.address();
                s.close();
                resolve(ip.address)
            });
        } catch (e) {
            console.error(e);
            s.close();
            reject(e);
        }
    })
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM