apppublic/aidocs/outip.md
2025-10-05 11:23:33 +08:00

228 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `IpGetter` 与 `OutIP` 模块技术文档
---
## 概述
本模块提供了一个用于获取当前公网 IP 地址的 Python 工具类,支持从多个公开 IP 查询服务中获取 IP并具备以下特性
- 支持多源 IP 获取
- 自动统计请求耗时并按响应速度排序调用
- 内置 IP 格式校验
- 可扩展自定义解析器
- 容错处理(异常捕获、失败降级)
主要包含两个核心类:
- `IpGetter`:单个 IP 获取器,封装对一个 URL 的请求和解析逻辑。
- `OutIP`:管理多个 `IpGetter` 实例,智能选择最快可用的服务。
---
## 安装依赖
```bash
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 获取请求。
**流程:**
1. 记录开始时间
2. 使用 `requests.get()` 请求目标 URL
3. 调用 `parser` 函数从响应文本中提取 IP
4. 验证 IP 格式是否合法(通过正则)
5. 更新性能统计数据
6. 返回有效 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` |
> 💡 注释掉的服务示例:
> ```python
> # g = IpGetter('https://ipapi.co/ip/', lambda x: x)
> ```
##### `add_getter(getter: IpGetter)`
向管理器添加一个新的 `IpGetter` 实例。
##### `get() -> Optional[str]`
尝试从所有已注册的 `IpGetter` 中获取有效的公网 IP。
**策略:**
1. 将所有 `getters``get_average_time()` 升序排序(最快优先)
2. 依次调用每个 getter 的 `get()` 方法
3. 返回第一个成功的 IP
4. 若全部失败,返回 `None`
> 🔄 动态优化:随着运行时间增加,系统会学习哪些服务更快更稳定,优先使用它们。
---
## 使用示例
### 基本用法
```python
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 添加
```python
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建议生产环境优先使用加密连接。
---
## 注意事项
1. **线程安全**:当前实现非线程安全,多线程环境下请加锁或每个线程独立实例。
2. **频率限制**:部分服务可能对请求频率有限制,请合理控制调用间隔(如 `time.sleep(1)`)。
3. **代理影响**:若程序运行于 NAT 或代理之后,返回的是出口网关的公网 IP。
4. **IPv6 支持**:目前仅支持 IPv4如需 IPv6需修改正则及解析逻辑。
---
## TODO 扩展建议
- [ ] 支持异步 (`asyncio` + `aiohttp`)
- [ ] 加入缓存机制(避免短时间内重复请求)
- [ ] 支持配置文件加载自定义服务
- [ ] 提供 CLI 接口
- [ ] 增加单元测试覆盖率
- [ ] 支持 IPv6 格式校验
---
## 版权与许可
© 2025 开源工具模块
可自由用于个人与商业项目,保留原作者注释即可。
---
📌 **提示**:将此代码保存为 `outip.py` 后可直接导入使用。