
在实际开发中获取 IP 地址这个需求远比看起来复杂。你以为是一条命令返回一个地址真正做下去会碰到内网地址与公网地址的差别、IPv4 与 IPv6 的取舍、第三方接口不稳定、下载站点拒绝特定 IP 访问等一连串问题。这里以“Get Your IP Address”这个简单工具思路为主线先拆解公网 IP 的获取原理再用 Python 写一个命令行小工具支持查询本机内网地址、公网 IP、归属地、ISP 组织信息最后结合“下载平台提示 download from your ip address is not allowed”的排障场景看看 IP 查询能力在真实工程中怎么用。全文不需要复杂框架也不需要数据库适合想在本地快速搭一个实用网络工具的人。1. 先想清楚获取 IP 地址到底在解决什么问题1.1 公网 IP 与内网 IP两条不同的查询路径大多数人在 Windows 上执行 ipconfig在 Linux 上执行 ip addr看到的是本机网卡地址。这类地址一般属于内网地址常见值比如 192.168.x.x、10.x.x.x、172.16.x.x 到 172.31.x.x。只有少数情况下比如服务器直接使用公网 IP 绑定网卡ipconfig 出来的地址才可能直接是公网地址。内网 IP 描述的是“本机在局域网里的位置”公网 IP 描述的是“当前网络出口在整个互联网里的位置”。两者之间隔着一层网络地址转换。家庭路由器创建局域网内部主机通过路由器上网路由器再把这个内网地址映射成运营商分配的公网 IP。内网 IP查询本地网卡即可速度最快但只能说明本机在局域网里的标识。公网 IP需要从网络出口之外“回看”本机直接获取不到。出口 IP是整个局域网对外访问时外部服务器看到的那一个地址。在写脚本时如果只做“获取本机 IP”用标准库 socket 就够了一旦要做“获取公网 IP”就要借助外部接口。先分清这两类后面才不会拿错信息去判断问题。1.2 一个看似简单的下载报错为什么会牵扯到 IP经常遇到的一个场景是进入某个软件官网下载安装包页面直接返回 download from your ip address is not allowed。此时用户第一个想法通常是清缓存、换浏览器或者换账号重试。实际上如果报错文本明确包含 IP 相关字样问题大概率发生在网络请求的入口层而不是登录态或浏览器环境。下载服务商可以在入口处检查请求来源 IP 是否属于某些国家或地区来源 IP 是否属于数据中心、云主机、共享出口等高风险网络段来源 IP 是否在服务商维护的黑名单中来源 IP 的归属地是否与当前网页语言或账号区域一致。要判断是上述哪一种必须先获取自己的公网 IP再查 IP 的归属地和网络类型。这就是“Get Your IP Address”这个工具最直接的工程价值。报错文本常见原因判断方向download from your ip address is not allowed地区限制查询 IP 归属地download from your ip address is not allowed出口网络类型被限制查询 ISP 或 org 字段download from your ip address is not allowedIP 黑名单换网络后重试download from your ip address is not allowedCDN 策略误判对比多个出口网络这种报错不是浏览器问题也不是登录问题而是 IP 在请求链路就被规则拦截了。2. 获取公网 IP 的原理出口地址与第三方回显服务2.1 为什么本机 ipconfig 拿不到公网地址本机网卡只关心下一个跳转的网络设备。在家庭网络中网卡获得的是路由器分配的地址路由器负责网络地址转换。当外部服务器收到请求时看到的是路由器的公网出口 IP。如果机器在宽带接入网之后公网 IP 甚至可能由运营商在宽带局端动态分配家里的路由器每次拨号都可能变化。所以“本机看到的 IP”和“外部服务看到的 IP”可能完全不同。中间隔了多少层网段、多少台转发设备本机并不感知。要想拿到外部视角的出口地址只能让一个外部服务“告诉”你。2.2 回显服务的工作方式与接口选择回显服务echo service的原理并不复杂服务端接收请求后直接从 TCP 连接或 HTTP 请求头中提取来源 IP再把 IP 返回给调用方。调用方拿到这个返回值就知道自己当前的出口 IP。这里需要注意这类服务和普通的“IP 查询网站”在实现上并没有本质区别区别只是返回格式是否简洁、是否适合脚本解析。个人工具应该优先选择纯文本或 JSON 接口不要选带大量页面的网页接口。常用接口可以先用下面几个做验证服务地址返回内容说明https://api.ipify.org纯文本 IP结构简单适合脚本https://ifconfig.me/ip纯文本 IP服务字段较多https://icanhazip.com纯文本 IP可能带换行双栈环境可能返回多行https://ipinfo.io/ip纯文本 IP同一服务可继续查归属地这些服务随时可能变更协议、增加鉴权或调整限流策略。落地前需要先查看当前服务页面的调用说明不要假设接口格式永远不变。2.3 用 curl 先验证一次再决定要不要写代码不要直接写代码先用 curl 验证接口在当前网络下是否可访问curl -4 --max-time 5 https://api.ipify.org curl -6 --max-time 5 https://ifconfig.me/ip-4和-6是告诉 curl 使用 IPv4 或 IPv6 协议栈。在双栈网络里不加参数时系统可能优先 IPv6导致你请求到的是 IPv6 地址但看起来却和 IPv4 结果很像。这是后面写工具时容易踩的第一个坑。再验证归属地接口curl --max-time 5 https://ipwho.is/203.0.113.7如果返回一段 JSON说明接口可用。如果超时或返回错误就需要换一个接口或者检查本机网络出口是否被防火墙拦截。下面示例中为了展示效果使用文档保留地址 203.0.113.7真实环境会返回当前公网地址。3. 用 Python 实现 Get Your IP Address 的最小工具3.1 环境准备与项目文件只需要 Python 3.6 以上版本不需要安装第三方库。使用标准库里的 socket、urllib、json、argparse 即可。项目结构get-ip/ ├── get_ip.py └── README.md在 get_ip.py 中按顺序实现本地内网 IP 获取、公网 IP 获取、归属地查询、命令行参数解析。最后用一段 main 入口串联。3.2 本地内网地址获取UDP 方式更可靠获取本机内网 IP 时直接调用 gethostbyname(gethostname()) 在多网卡环境下容易拿到 127.0.0.1推荐使用 UDP 套接字连接外部地址后获取本机地址import socket def get_local_ip() - str: try: s socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect((8.8.8.8, 80)) local_ip s.getsockname()[0] s.close() return local_ip except OSError: return 0.0.0.0这段代码不会真的向 8.8.8.8 发送 UDP 数据包。它只是让系统选择一条去往外网的默认路由然后读取该路由上绑定的本地地址。在没有外网连接时会返回 0.0.0.0。3.3 多接口降级获取公网 IP单接口并不可靠推荐按顺序尝试多个回显服务import urllib.error import urllib.request DEFAULT_TIMEOUT 5 IP_RETURN_SERVICES [ https://api.ipify.org, https://ifconfig.me/ip, https://icanhazip.com, ] def fetch_text(url: str, timeout: int DEFAULT_TIMEOUT) - str: req urllib.request.Request( url, headers{User-Agent: get-ip-tool/1.0}, ) with urllib.request.urlopen(req, timeouttimeout) as resp: return resp.read().decode(utf-8).strip() def get_public_ip(timeout: int DEFAULT_TIMEOUT) - str: last_error None for service in IP_RETURN_SERVICES: try: ip fetch_text(service, timeout) if ip: return ip except (urllib.error.URLError, TimeoutError, OSError) as exc: last_error exc raise RuntimeError(fall ip services failed: {last_error})这一步有三个关键点设置超时。公共接口一旦挂掉默认行为可能会一直等待所以每个请求都要有 timeout。多接口降级。第一个接口失败后继续尝试第二个不让单点故障影响整个工具。解析结果前先 strip。有些接口会在文本后面带换行不处理会污染输出。3.4 归属地、ISP 与地理位置查询有了公网 IP 之后下一步是查询归属地、城市和 ISP。这里以 ipwho.is 的 JSON 接口为例import json GEO_SERVICE https://ipwho.is/{ip} def query_location(ip: str, timeout: int DEFAULT_TIMEOUT) - dict: url GEO_SERVICE.format(ipip) try: text fetch_text(url, timeout) return json.loads(text) except Exception as exc: return {error: str(exc)}调用后可能返回的字段包括 success、ip、country、region、city、org、timezone 等。不同接口字段名不同实际项目要以所选服务的文档为准。免费查询服务通常有调用次数限制生产环境不要把它当成无限制接口使用。3.5 命令行参数与输出控制为了让工具既适合人看也适合脚本调用使用 argparse 提供两个关键参数--json 输出 JSON--location 追加归属地查询。import argparse def parse_args(): parser argparse.ArgumentParser(descriptionGet your public IP address.) parser.add_argument(--json, actionstore_true, helpoutput JSON) parser.add_argument(--location, actionstore_true, helpquery location and ISP) parser.add_argument(--timeout, typeint, default5, helprequest timeout in seconds) return parser.parse_args() def main(): args parse_args() local_ip get_local_ip() public_ip get_public_ip(args.timeout) result { local_ip: local_ip, public_ip: public_ip, } if args.location: result[location] query_location(public_ip, args.timeout) if args.json: print(json.dumps(result, ensure_asciiFalse, indent2)) else: print(f内网地址: {local_ip}) print(f公网 IP: {public_ip}) if location in result: loc result[location] if isinstance(loc, dict) and error not in loc: print(f归属地: {loc.get(country, )} {loc.get(region, )} {loc.get(city, )}) if __name__ __main__: main()注意 JSON 输出要设置 ensure_asciiFalse否则归属地中的中文会被转义成 \uXXXX不方便脚本直接读取。4. 运行验证从命令行输出到脚本化调用4.1 默认输出执行命令python get_ip.py输出示例内网地址: 192.168.1.23 公网 IP: 203.0.113.7为了使示例看起来清晰这里使用文档保留地址 203.0.113.7。实际运行时这个位置会显示你当前网络出口的真实公网地址。4.2 查询归属地执行命令python get_ip.py --location输出示例内网地址: 192.168.1.23 公网 IP: 203.0.113.7 归属地: United States California Los Angeles如果查询服务返回 org 或 isp 字段可以再打印运营商信息if loc.get(org): print(f运营商: {loc[org]})4.3 JSON 输出用于脚本联动执行命令python get_ip.py --json --location输出示例{ local_ip: 192.168.1.23, public_ip: 203.0.113.7, location: { success: true, ip: 203.0.113.7, country: United States, region: California, city: Los Angeles, org: Example Networks } }脚本可以借助 jq 提取公网 IPpython get_ip.py --json | jq -r .public_ip如果运行环境没有 jq也可以直接读取 JSON 的 public_ip 字段不一定依赖 jq。4.4 接口失效时的降级表现把 IP_RETURN_SERVICES 里的回显地址改成一个不可用的地址再运行工具会看到Traceback (most recent call last): ... RuntimeError: all ip services failed: ...这说明所有回显服务都不可用。排障时先 ping 外网再用 curl 手动请求回显服务判断是网络层问题还是服务端问题。5. 下载提示 “IP Address Is Not Allowed” 的排查链路5.1 先确认限制发生在哪一层当软件官网返回 download from your ip address is not allowed 时需要区分层次页面层浏览器 Cookie、登录态、前端脚本导致。下载入口层CDN 或网关根据来源 IP 拦截。文件服务器