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

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


#1楼

我不得不解决“找出IP地址是否在本地”这一问题,个人第一个想法是创建一个本地IP地址列表,而后与之匹配。 这就是致使我提出这个问题的缘由。 可是,我后来意识到有一种更直接的方法:尝试在该IP上绑定并查看它是否有效。 linux

_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

我知道这并不能直接回答问题,可是这对尝试解决相关问题的任何人以及遵循相同思路的人都应该有所帮助。 这具备做为跨平台解决方案的优点(我认为)。 服务器


#2楼

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

#3楼

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

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楼

这个答案是我我的尝试解决的问题,由于socket.gethostbyname(socket.gethostname())也返回了127.0.0.1。 此方法不须要Internet,仅须要LAN链接便可。 代码适用于Python 3.x,但能够轻松转换为2.x。 使用UDP广播: socket

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())

#5楼

做为名为myip的别名,该别名应可在任何地方使用: ide

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代码: spa

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上运行的版本: code

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

(感谢@ccpizzaserver


背景

使用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发行版上的工做,所以我尝试了这种替代技术。 它尝试经过端口538.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地址可能比此处列出的任何方法都更可靠和可靠。

相关文章
相关标签/搜索