如何在Python中独立于平台且仅使用标准库找到本地IP地址(即192.168.x.x或10.0.x.x)?
如何在Python中独立于平台且仅使用标准库找到本地IP地址(即192.168.x.x或10.0.x.x)?
我刚发现这个,但它似乎有点hackish。不过他们说他们在*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()
假设您有互联网访问,且没有本地代理。
import socket
socket.gethostbyname(socket.gethostname())
这并不总是有效的(在拥有主机名为127.0.0.1
的/etc/hosts
上返回127.0.0.1
),一个临时的解决方案是像gimel所展示的那样,改用socket.getfqdn()
。当然,您的计算机需要具有可解析的主机名。
socket.gethostbyname_ex(socket.gethostname())[-1]
- BarmaleyPython 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())
socket.gethostbyname(socket.gethostname())
得到的结果很糟糕。 - Jason Floydmyip
的别名: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程序中使用类似的功能,正确的方法是使用支持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])
适用于没有互联网连接的局域网的版本:
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])
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])
socket.getaddrinfo()
应该在所有平台上工作一致 - 但我只在 Linux 上进行了检查,没有关心其他操作系统。 - Wladimir Palantifconfig
显示本地IPv4地址。 - anatoly techtonikgethostbyname_ex
返回有效的IP地址,也会初始化到8.8.8.8的不必要的外部连接。这将在没有互联网的“有围墙花园”类型的局域网中出现问题。可以通过使用or
来条件地进行外部调用,例如:ips = [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]]
。 - ccpizzapip install netifaces
在您的命令行 shell 中运行该命令,它将安装到默认的 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 上正常工作。
sys
中,这似乎是一个很好的候选项。 - Jasenimport 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]
8.8.8.8
是谷歌DNS服务器,我们能否使用本地DNS服务器来完成它? - Ciasto piekarzSocket API 方法
参见 https://dev59.com/m3VC5IYBdhLWcg3w1E_w#28950776
缺点:
反射器方法
(请注意,这并没有回答 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)
优点:
缺点(及解决方法):
编辑:尽管最初我认为这些方法非常糟糕(除非您使用多个备选项,否则代码在未来很多年可能就不再适用了),但它确实引出了“互联网是什么?”这个问题。计算机可能具有指向许多不同网络的许多接口。有关该主题的更详细描述,请搜索“网关和路由”。计算机可能能够通过内部网关访问内部网络,或者通过例如路由器上的网关访问全球互联网(通常情况下)。 OP 询问的本地 IP 地址仅对于单个链路有明确定义,因此您必须指定它(“我们谈论的是网络卡还是以太网电缆?”)。对于这个问题的提出可能会有多个非唯一的答案。但是,在世界范围内的全局 IP 地址可能已经明确定义了(在没有大规模网络分片的情况下):可能是通过可以访问 TLD 的网关的返回路径。
>>> 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'
>>>
ioctl
)。套接字未绑定您想要有关addr信息的接口-它只是用户空间和内核之间的通信机制。https://en.wikipedia.org/wiki/Ioctl http://lxr.free-electrons.com/source/net/socket.c - tMCstruct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
替换为struct.pack('16sH14s', iface.encode('utf-8'), socket.AF_INET, b'\x00'*14)
。 - pepoluanioctl
是一个遗留接口,我不认为它支持 IPv6,也很可能永远不会支持。我认为“正确”的方法是通过 Netlink,但在 Python 中并不是非常直观。我认为 libc 应该有函数 getifaddrs
,可以通过 Python 的 ctypes
模块访问,这可能有效 - http://man7.org/linux/man-pages/man3/getifaddrs.3.html - tMC我正在使用以下模块:
#!/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 的局域网系统。
固定接口名称列表不适用于最近的 Linux 版本,它们采用了 systemd v197 更改以实现可预测的接口命名,正如 Alexander 指出的那样。 在这种情况下,您需要手动替换列表中的接口名称,或使用另一个解决方案,如netifaces。
enp0s25
)不兼容。有关更多信息,请参见https://wiki.archlinux.org/index.php/Network_Configuration#Device_names。 - Alexanderstruct.pack('256s', bytes(ifname[:15]))
- colm.anseo[仅适用于Windows] 如果你不想使用外部包并且不想依赖外部互联网服务器,这可能会有所帮助。这是我在Google Code Search上找到并修改以返回所需信息的代码示例:
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上工作。
ifconfig -a
命令并使用其输出... - Fredrik Pihlip addr
更加适合(而且更容易解析)。 - phihag