如何在Python平台中独立地找到本地IP地址(即192.168.xx或10.0.xx)并仅使用标准库?
我刚发现这个,但它看起来有点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()
这假设您具有Internet访问权限,并且没有本地代理.
import socket socket.gethostbyname(socket.gethostname())
这总是不起作用(127.0.0.1
在主机/etc/hosts
名为as的机器上返回127.0.0.1
),gimel显示的是paliative,socket.getfqdn()
而是使用.当然,您的机器需要可解析的主机名.
此方法返回本地框(具有默认路由的IP)上的"主"IP.
不需要可路由的网络访问或任何连接.
即使从网络上拔下所有接口,也能正常工作.
不需要甚至尝试去其他任何地方.
适用于NAT,公共,私有,外部和内部IP
纯Python 2(或3)没有外部依赖.
适用于Linux,Windows和OSX.
Python 2或3:
import socket def get_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # doesn't even have to be reachable s.connect(('10.255.255.255', 1)) IP = s.getsockname()[0] except: IP = '127.0.0.1' finally: s.close() return IP
这将返回一个主要的IP(具有默认路由的IP).如果您需要将所有IP连接到所有接口(包括localhost等),请参阅此答案.
如果你在家里的wifi防火墙之后就像NAT防火墙一样,那么这将不会显示你的公共NAT IP,而是显示本地网络上的私有NAT IP,它具有到本地WIFI路由器的默认路由; 获取你的无线路由器的外部IP要么需要在这个盒子上运行,要么连接到可以反映IP的外部服务,例如whatismyip.com/whatismyipaddress.com ......但这与原始问题完全不同.:)
在评论中根据Pedro的建议更新了connect()调用.(如果您需要特定的许可声明,这是公共域/免费用于任何用途,或者根据您选择的Stack Overflow代码/内容许可证的MIT/CC2-BY-SA.)
作为别名myip
,它应该在任何地方都可以使用:
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代码:
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地址,则会引发异常.
在没有互联网连接的LAN上也可以使用的版本:
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])
这适用于Linux和Windows上的Python 2和3,但不涉及多个网络设备或IPv6.但是,它停止了最近的Linux发行版,所以我尝试了这种替代技术.它尝试连接到8.8.8.8
端口的Google DNS服务器53
:
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地址可能比这里列出的任何方法都更加健壮和可靠.
您可以使用netifaces模块.只需输入:
pip 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.
套接字API方法
请参阅/sf/ask/17360801/
缺点:
不是跨平台的.
需要更多的后备代码,与互联网上特定地址的存在相关联
如果你在NAT后面,这也行不通
可能会创建一个UDP连接,而不是独立于(通常是ISP)的DNS可用性(请参阅其他答案,例如使用8.8.8.8:Google的(巧合的是DNS)服务器)
确保将目标地址设置为UNREACHABLE,例如规格保证未使用的数字IP地址.请勿使用fakesubdomain.google.com或somefakewebsite.com等域名; 您仍然会在该方(现在或将来)发送垃圾邮件,并在此过程中向您自己的网络框发送垃圾邮件.
反射器方法
(请注意,这不符合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 = 'Current IP Check Current IP Address: 65.96.168.198\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 = 'Current IP Check Current IP Address: 65.96.168.198\r\n' return re.compile(r'Address: (\d+\.\d+\.\d+\.\d+)').search(data).group(1)
好处:
这种方法的一个优点是它是跨平台的
它适用于丑陋的NAT(例如你的家用路由器).
缺点(和解决方法):
需要这个网站,不改变的格式(几乎肯定不会),你的DNS服务器工作.通过在发生故障时查询其他第三方IP地址反射器,可以缓解此问题.
如果你不查询多个反射器(防止被泄露的反射器告诉你你的地址不是这样的话),或者如果你不使用HTTPS(以防止中间人攻击假装),可能的攻击向量成为服务器)
编辑:虽然最初我认为这些方法非常糟糕(除非你使用很多后备,但代码可能在很多年后都无关紧要),它确实提出了"什么是互联网?"的问题.计算机可能有许多指向许多不同网络的接口.有关该主题的更全面描述,请访问google gateways and routes
.计算机可以通过内部网关访问内部网络,或者通过例如路由器(通常是这种情况)上的网关访问万维网.OP询问的本地IP地址仅针对单个链路层进行了明确定义,因此您必须指定("它是网卡,还是以太网电缆,我们正在谈论它?") .提出的问题可能有多个非独特的答案.然而,万维网上的全球IP地址可能是明确定义的(在没有大规模网络碎片的情况下):可能是通过可以访问TLD的网关的返回路径.
如果计算机具有到Internet的路由,则即使未正确设置/ etc/hosts ,也始终可以获取首选本地IP地址.
import 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]
在Linux上:
>>> 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' >>>
我正在使用以下模块:
#!/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的LAN中的系统.
固定的接口名称列表不适用于最近的Linux版本,这些版本采用了有关可预测接口名称的systemd v197更改,正如Alexander所指出的那样.在这种情况下,您需要使用系统上的接口名称手动替换列表,或使用其他解决方案,如netifaces.
我在我的ubuntu机器上使用它:
import commands commands.getoutput("/sbin/ifconfig").split("\n")[1].split()[1][5:]
这不起作用.
如果您不想使用外部程序包并且不想依赖外部Internet服务器,这可能会有所帮助.这是我在Google代码搜索中找到并修改为返回所需信息的代码示例:
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.
关于Debian(测试),我怀疑大多数Linux的..
import commands RetMyIP = commands.getoutput("hostname -I")
在MS Windows上(已测试)
import socket socket.gethostbyname(socket.gethostname())
我认为这个版本尚未发布.我在Ubuntu 12.04上使用python 2.7进行了测试.
在以下网址找到此解决方案:http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/
import socket import fcntl import struct def get_ip_address(ifname): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) return socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR struct.pack('256s', ifname[:15]) )[20:24])
结果示例:
>>> get_ip_address('eth0') '38.113.228.130'
ninjagecko答案的变化.这应该适用于允许UDP广播的任何LAN,并且不需要访问LAN或Internet上的地址.
import socket def getNetworkIp(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) s.connect(('', 0)) return s.getsockname()[0] print (getNetworkIp())
我担心除了连接到另一台计算机并让它向您发送您的IP地址之外,没有任何好的平台独立方法可以做到这一点.例如: findmyipaddress.请注意,如果您需要NAT后面的IP地址,除非您连接的计算机也在NAT后面,否则这将不起作用.
这是一个适用于Linux的解决方案: 获取与网络接口关联的IP地址.
通过命令行工具生成"干净"输出的一种简单方法:
import commands ips = commands.getoutput("/sbin/ifconfig | grep -i \"inet\" | grep -iv \"inet6\" | " + "awk {'print $2'} | sed -ne 's/addr\:/ /p'") print ips
它将显示系统上的所有IPv4地址.
仅供参考我可以验证方法:
import socket addr = socket.gethostbyname(socket.gethostname())
适用于OS X(10.6,10.5),Windows XP和管理良好的RHEL部门服务器.它在一个非常小的CentOS VM上无法工作,我只是做了一些内核攻击.因此,对于该实例,您只需检查127.0.0.1地址,在这种情况下执行以下操作:
if addr == "127.0.0.1": import commands output = commands.getoutput("/sbin/ifconfig") addr = parseaddress(output)
然后从输出中解析ip地址.应该注意的是,默认情况下ifconfig不在普通用户的PATH中,这就是我在命令中给出完整路径的原因.我希望这有帮助.
这是UnkwnTech的答案的变体 - 它提供了一个get_local_addr()
函数,它返回主机的主LAN IP地址.我发布它是因为这增加了许多东西:ipv6支持,错误处理,忽略localhost/linklocal addrs,并使用TESTNET addr(rfc5737)连接.
# imports import errno import socket # 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: 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
import socket [i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)]
这适用于大多数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()
这个答案是我个人尝试解决获取局域网IP的问题,因为socket.gethostbyname(socket.gethostname())
还返回了127.0.0.1.此方法不需要Internet仅LAN连接.代码适用于Python 3.x,但可以轻松转换为2.x. 使用UDP广播:
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(('', 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'), (' ', 8888)) try: address = queue.get(False) except Empty: pass else: waiting = False return address[0] if __name__ == '__main__': print(get_local_ip())