6.4 KiB
6.4 KiB
IpGetter 与 OutIP 模块技术文档
概述
本模块提供了一个用于获取当前公网 IP 地址的 Python 工具类,支持从多个公开 IP 查询服务中获取 IP,并具备以下特性:
- 支持多源 IP 获取
- 自动统计请求耗时并按响应速度排序调用
- 内置 IP 格式校验
- 可扩展自定义解析器
- 容错处理(异常捕获、失败降级)
主要包含两个核心类:
IpGetter:单个 IP 获取器,封装对一个 URL 的请求和解析逻辑。OutIP:管理多个IpGetter实例,智能选择最快可用的服务。
安装依赖
pip install requests
说明:该模块依赖
requests库发送 HTTP 请求,需提前安装。
类与方法详解
✅ class IpGetter(url: str, parser: Callable[[str], str])
表示一个从指定 URL 获取公网 IP 的客户端。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
url |
str |
提供 IP 地址的公共 API 端点 URL |
parser |
Callable[[str], str] |
一个函数,用于从 HTTP 响应文本中提取原始 IP 字符串 |
属性
| 属性名 | 类型 | 初始值 | 说明 |
|---|---|---|---|
cnt |
int |
0 |
成功请求次数(用于计算平均耗时) |
total_time |
float |
0 |
所有请求累计耗时(秒) |
avg_time |
float |
0 |
平均每次请求耗时(秒),失败后设为 10000 秒以降低优先级 |
方法
get() -> Optional[str]
发起一次 IP 获取请求。
流程:
- 记录开始时间
- 使用
requests.get()请求目标 URL - 调用
parser函数从响应文本中提取 IP - 验证 IP 格式是否合法(通过正则)
- 更新性能统计数据
- 返回有效 IP 或
None
返回值:
- 成功时返回形如
"123.45.67.89"的字符串 - 失败时打印错误信息并返回
None
⚠️ 若解析或格式校验失败,会将此实例的
avg_time设为10000,使其在后续调度中被排到末尾。
check_ip(ip: str) -> Optional[str]
使用正则表达式验证输入字符串是否为标准 IPv4 地址。
正则模式: r'(\d+\.\d+\.\d+\.\d+)'
返回值:
- 匹配成功:返回提取出的 IP 字符串
- 匹配失败:打印警告日志并返回
None
get_average_time() -> float
返回当前实例的历史平均请求耗时(单位:秒)。
__str__() -> str
返回调试字符串,格式如下:
self.url='...', self.avg_time=...
✅ class OutIP()
管理一组 IpGetter 实例,自动选择响应最快的可用服务来获取公网 IP。
属性
| 属性名 | 类型 | 说明 |
|---|---|---|
getters |
List[IpGetter] |
所有注册的 IP 获取器列表 |
方法
__init__()
初始化 OutIP 实例,并调用 set_known_getters() 注册默认服务源。
set_known_getters()
预注册一组常用的公网 IP 查询服务,包括:
| URL | 解析方式 |
|---|---|
http://ipinfo.io/ip |
原样返回 |
https://api.ipify.org |
原样返回 |
https://ident.me |
原样返回 |
http://myip.dnsomatic.com |
原样返回 |
https://checkip.amazonaws.com |
去除首尾空白字符 |
http://checkip.dyndns.com |
正则提取:Address: X.X.X.X |
💡 注释掉的服务示例:
# g = IpGetter('https://ipapi.co/ip/', lambda x: x)
add_getter(getter: IpGetter)
向管理器添加一个新的 IpGetter 实例。
get() -> Optional[str]
尝试从所有已注册的 IpGetter 中获取有效的公网 IP。
策略:
- 将所有
getters按get_average_time()升序排序(最快优先) - 依次调用每个 getter 的
get()方法 - 返回第一个成功的 IP
- 若全部失败,返回
None
🔄 动态优化:随着运行时间增加,系统会学习哪些服务更快更稳定,优先使用它们。
使用示例
基本用法
from outip import OutIP # 假设保存为 outip.py
import time
oi = OutIP()
for i in range(10):
ip = oi.get()
print(f"Public IP: {ip}")
time.sleep(1)
输出示例:
Public IP: 203.0.113.45
Public IP: 203.0.113.45
...
自定义 Getter 添加
def custom_parser(text):
match = re.search(r'current_ip["\']?: ?["\']?(\d+\.\d+\.\d+\.\d+)', text)
return match.group(1) if match else None
custom_getter = IpGetter("https://myip.customservice.com", custom_parser)
oi = OutIP()
oi.add_getter(custom_getter)
ip = oi.get()
性能与容错机制
| 特性 | 描述 |
|---|---|
| 响应时间记录 | 每次成功请求都更新平均耗时,用于排序 |
| 失败惩罚机制 | 失败时设置 avg_time = 10000,避免频繁重试不可用服务 |
| 异常捕获 | 所有网络/解析异常被捕获,不影响主流程 |
| 服务降级 | 当首选服务失败时自动切换至备选服务 |
已知服务列表(内置)
| 服务名称 | URL | 是否启用 | 特点 |
|---|---|---|---|
| ipinfo.io | http://ipinfo.io/ip | ✅ 是 | 快速简洁 |
| ipify | https://api.ipify.org | ✅ 是 | 国际知名 |
| ident.me | https://ident.me | ✅ 是 | 支持多种格式 |
| dnsomatic | http://myip.dnsomatic.com | ✅ 是 | 老牌动态 DNS |
| Amazon AWS | https://checkip.amazonaws.com | ✅ 是 | AWS 官方服务 |
| DynDNS | http://checkip.dyndns.com | ✅ 是 | 返回 HTML,需正则提取 |
🔒 所有服务均为 HTTPS(除少数兼容 HTTP),建议生产环境优先使用加密连接。
注意事项
- 线程安全:当前实现非线程安全,多线程环境下请加锁或每个线程独立实例。
- 频率限制:部分服务可能对请求频率有限制,请合理控制调用间隔(如
time.sleep(1))。 - 代理影响:若程序运行于 NAT 或代理之后,返回的是出口网关的公网 IP。
- IPv6 支持:目前仅支持 IPv4;如需 IPv6,需修改正则及解析逻辑。
TODO 扩展建议
- 支持异步 (
asyncio+aiohttp) - 加入缓存机制(避免短时间内重复请求)
- 支持配置文件加载自定义服务
- 提供 CLI 接口
- 增加单元测试覆盖率
- 支持 IPv6 格式校验
版权与许可
© 2025 开源工具模块
可自由用于个人与商业项目,保留原作者注释即可。
📌 提示:将此代码保存为 outip.py 后可直接导入使用。