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

相關文章
相關標籤/搜索