如何找到本地IP地址(即192.168.x。x或10.0.x.x)在Python平台独立,只使用标准库?
当前回答
另一个变体的前面的答案,可以保存到一个名为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时打印best-bet。
$ my-ip-to
192.168.11.102
其他回答
Socket API方法
参见https://stackoverflow.com/a/28950776/711085
缺点:
Not cross-platform. Requires more fallback code, tied to existence of particular addresses on the internet This will also not work if you're behind a NAT Probably creates a UDP connection, not independent of (usually ISP's) DNS availability (see other answers for ideas like using 8.8.8.8: Google's (coincidentally also DNS) server) Make sure you make the destination address UNREACHABLE, like a numeric IP address that is spec-guaranteed to be unused. Do NOT use some domain like fakesubdomain.google.com or somefakewebsite.com; you'll still be spamming that party (now or in the future), and spamming your own network boxes as well in the process.
反射器方法
(请注意,这并没有回答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(以防止假装是服务器的中间人攻击),则可能的攻击向量
edit: Though initially I thought these methods were really bad (unless you use many fallbacks, the code may be irrelevant many years from now), it does pose the question "what is the internet?". A computer may have many interfaces pointing to many different networks. For a more thorough description of the topic, google for gateways and routes. A computer may be able to access an internal network via an internal gateway, or access the world-wide web via a gateway on for example a router (usually the case). The local IP address that the OP asks about is only well-defined with respect to a single link layer, so you have to specify that ("is it the network card, or the ethernet cable, which we're talking about?"). There may be multiple non-unique answers to this question as posed. However the global IP address on the world-wide web is probably well-defined (in the absence of massive network fragmentation): probably the return path via the gateway which can access the TLDs.
这将在大多数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()
我刚发现这个,但它似乎有点hack,然而他们说尝试它在*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访问,并且没有本地代理。
这是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
这不是很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()
是的,这是一种黑客行为,但有时我不想事后怀疑操作系统,直接使用内置的和有效的操作系统就行了。