使用Python标准库查找本地IP地址

719

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


11
本地IP?还是公共IP?你要如何处理有多个IP的系统? - Sargun Dhillon
使用 ifconfig -a 命令并使用其输出... - Fredrik Pihl
18
@Fredrik,那是个糟糕的想法。首先,你没有必要分叉一个新进程,这可能会防止你的程序在严格锁定的配置中工作(或者你将不得不授予你的程序不需要的权限)。其次,你会为不同语言环境的用户引入错误。第三,如果您决定启动一个新程序,您不应该启动一个已经弃用的程序 - ip addr 更加适合(而且更容易解析)。 - phihag
14
@phihag,你说得完全正确,感谢你指出我的错误。 - Fredrik Pihl
1
这里更根本的问题是,在一个正确编写的现代网络程序中,正确的(一组)本地IP地址取决于对等方或潜在对等方的集合。如果需要将本地IP地址用于将套接字“绑定”到特定接口,则这是一个策略问题。如果需要将本地IP地址交给对等方,以便对等方可以“回调”,即打开到本地机器的连接,则情况取决于是否存在任何NAT(网络地址转换)盒子。如果没有NAT,则getsockname是一个不错的选择。 - Pekka Nikander
如果存在网络地址转换(NAT),而我仍然想使用回调来连接套接字,那该怎么办? - Parth Lathiya
50个回答

0

0

另一种与之前答案不同的变体,可以保存为可执行脚本,命名为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
$

当没有参数时,打印最佳结果。

$ my-ip-to
192.168.11.102

0
这里有一个基于fatal_error的答案的解决方案,但它不依赖于try-except语句。
在macOS、Raspbian OS和Windows 11上进行了测试。
如果没有网络连接,返回127.0.0.1。
import socket


def get_ip():
    """
    Idea from https://dev59.com/m3VC5IYBdhLWcg3w1E_w#28950776
    """
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(0)
    # Doesn’t even have to be reachable.
    s.connect_ex(("10.254.254.254", 1))
    ip = s.getsockname()[0]
    ip = "127.0.0.1" if ip == "0.0.0.0" else ip
    s.close()
    return ip


IP = get_ip()
print(IP)

0

Windows解决方案,要就拿走,不要拉倒。

仅获取当前活动的无线局域网(WLAN)即计算机在WiFi路由器或网络交换机上的IP地址。

注意:这不是设备的公共IP地址,也不涉及任何外部请求、包或公共API。

核心思想是解析shell命令:ipconfig或Linux上的ifconfig的输出。我们使用子进程来获取输出。

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的subprocess output进行了捕获。

C:\Users\dell>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。


看起来这段代码可以通过删除标志变量 scan 并将三个 if 语句替换为一个来简化: if 'wireless' in i and 'ipv4' in i: - Georgy
@Georgy 不是因为我们对包含ipv4的行感兴趣,而是在找到我们电脑中第一个活动的无线适配器后。不同时包含“无线”和“ipv4”。你试过了吗?我试过你的方法,输出结果是“None”。 - nikhil swami
这不是OP所要求的纯Python解决方案。 - Chris Wolf
@ChrisWolf,大多数操作系统的网络代码都是用C或C++编写的。因此,没有一种纯Python访问网络信息的实际解决方案。我们只能使用各自操作系统的shell/kernel进行接口交互。 - nikhil swami
@nikhilswami 当我说“纯Python”时,我应该说“纯cPython”。重点是通过subprocess调用外部可执行文件这样的技巧不是“纯cPython”解决方案。 - Chris Wolf

-1

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

-2

简单而甜美!

def getip():

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

    return(ip)

从来没有。只是我的两分钱。 - Matt
在Ubuntu上如上所述失败。 - lxx

-2
这与先前发布的答案非常相似,但我找不到任何使用调用的答案。这是我用于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]);"

-3
我决定使用服务和/或API的 ipfy: https://www.ipify.org。该服务可以帮助我获取IP地址。
#!/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 上有一个名为 ipifyPython 库


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

你发布的差异很令人困惑,因为它与任何IDE都没有关系。这是交互式Python shell与脚本之间的区别。 - Dominik George

-3

@fatal_error 的解决方案应该被接受为答案!这是他的解决方案在 Node.js 中的实现,以防有人需要:

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);
        }
    })
}

2
我不确定你是否在正确的地方发布了这个问题,JS解决方案在Python问题上不合适。 - UnkwnTech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接