当前位置: 首页 > news >正文

手把手教你用 Gitee 替代 DDNS:家庭 IP 自动更新 + 本地快捷访问

## 前言

很多人家中搭建了 NAS、树莓派或小型服务器,希望通过固定域名或地址随时访问内网服务。传统的 DDNS 需要依赖域名服务商,并且要开放端口、配置防火墙。
本文提供一种极简替代方案:**利用 Gitee(码云)私有仓库存储家庭公网 IP,通过 Python 脚本自动更新 IP,并在本地电脑生成快捷方式**,实现类似 DDNS 的效果,无需公网域名,完全免费。

---

## 一、方案设计思路

1. **家庭服务器**:定时获取当前公网 IPv4 和 IPv6,通过 Gitee API 写入私有仓库的两个文件 `ipv4.txt` 和 `ipv6.txt`。
2. **本地电脑**:无 Git 依赖,通过 Gitee API 直接读取最新 IP,并在脚本所在目录生成可直接双击打开的网页快捷方式(`.url` / `.webloc` / `.desktop`)。
3. **运行环境**:Windows / macOS / Linux 均可,最终可将脚本打包为单文件 exe,方便多台设备使用。

优点:
- **无需购买域名**,完全利用 Gitee 免费私有仓库。
- **同时支持 IPv4 和 IPv6**,未来兼容性好。
- **纯 Python 实现**,可打包为独立可执行文件,无需安装 Python 环境也能运行。
- **安全可控**:私有仓库 + 个人令牌,IP 不会公开。

---

## 二、准备工作

### 1. 注册 Gitee 并创建私有仓库
- 访问 [gitee.com](https://gitee.com) 注册账号。
- 创建私有仓库,例如命名为 `homeddns`(本文使用用户名 `georgecn`,仓库 `homeddns`)。
- 在 **设置 → 私人令牌** 生成一个新令牌,勾选 `projects` 权限,**复制保存好**(只显示一次)。

### 2. 确认家庭网络具备公网 IP
- 访问 [ipv4.ip.sb](https://ipv4.ip.sb) 和 [ipv6.ip.sb](https://ipv6.ip.sb) 检查是否有公网地址。
- 若没有公网 IPv4,可考虑使用 IPv6 或内网穿透方案(本文暂不展开)。

### 3. 路由器端口转发
如果家庭 Web 服务运行在某个内网设备(如 `192.168.1.100:8080`),需在路由器中设置端口转发(NAT),将外网请求的 8080 端口映射到该内网地址。

---

## 三、家庭服务器端:自动更新 IP 到 Gitee

### 1. 脚本功能
- 依次尝试多个 IP 查询服务(`ipv4.ip.sb`、`v4.ident.me` 等),获取当前公网 IPv4 和 IPv6。
- 通过 Gitee API 将 IP 写入仓库文件(若文件不存在则创建;若存在则更新,自动处理 `sha`)。
- 若 IP 未变化则跳过更新,节省 API 调用。

### 2. 完整代码 `update_ip_to_gitee.py`

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import io
import base64
import requests

# 解决 Windows 控制台 GBK 编码问题
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

# ========== 配置区域 ==========
GITEE_TOKEN = '你的私人令牌'
OWNER = '你的用户名'
REPO = 'homeddns' # 仓库名
BRANCH = 'master' # 分支名
# =============================

IPV4_SERVICES = [
'https://ipv4.ip.sb',
'https://v4.ident.me',
'https://api.ipify.org',
'https://ipv4.icanhazip.com'
]

IPV6_SERVICES = [
'https://ipv6.ip.sb',
'https://v6.ident.me',
'https://api6.ipify.org',
'https://ipv6.icanhazip.com'
]

def fetch_ip(service_list, ip_type='IPv4'):
for url in service_list:
try:
print(f'尝试从 {url} 获取{ip_type}...')
resp = requests.get(url, timeout=5)
if resp.status_code == 200:
ip = resp.text.strip()
if ip_type == 'IPv4' and '.' in ip and ':' not in ip:
return ip
elif ip_type == 'IPv6' and ':' in ip:
return ip
except Exception as e:
print(f'从 {url} 获取失败: {e}')
continue
return None

def get_file_sha(file_path):
api_url = f'https://gitee.com/api/v5/repos/{OWNER}/{REPO}/contents/{file_path}'
headers = {'Authorization': f'token {GITEE_TOKEN}'}
params = {'ref': BRANCH}
try:
resp = requests.get(api_url, headers=headers, params=params)
if resp.status_code == 200:
data = resp.json()
if isinstance(data, dict):
return data.get('sha')
elif isinstance(data, list):
print(f'警告:{file_path} 是目录,不是文件')
return None
elif resp.status_code == 404:
return None
else:
print(f'获取文件信息异常: {resp.status_code}')
return None
except Exception as e:
print(f'请求异常: {e}')
return None

def update_gitee_file(file_path, content):
api_url = f'https://gitee.com/api/v5/repos/{OWNER}/{REPO}/contents/{file_path}'
headers = {'Authorization': f'token {GITEE_TOKEN}'}
new_content_b64 = base64.b64encode(content.encode()).decode('utf-8')
data = {
'access_token': GITEE_TOKEN,
'content': new_content_b64,
'message': f'Update {file_path}',
'branch': BRANCH
}
try:
resp = requests.put(api_url, json=data, headers=headers)
if resp.status_code in (200, 201):
print(f'[OK] 文件 {file_path} 更新成功')
return True
elif resp.status_code == 400 and 'sha is missing' in resp.text:
print(f'文件 {file_path} 已存在,获取 sha 重试...')
sha = get_file_sha(file_path)
if sha:
data['sha'] = sha
retry_resp = requests.put(api_url, json=data, headers=headers)
if retry_resp.status_code in (200, 201):
print(f'[OK] 文件 {file_path} 更新成功')
return True
print(f'[ERROR] 更新失败: {resp.status_code}')
return False
except Exception as e:
print(f'请求异常: {e}')
return False

def main():
print("===== 开始获取公网IP =====")
ipv4 = fetch_ip(IPV4_SERVICES, 'IPv4')
if ipv4:
print(f'[INFO] IPv4: {ipv4}')
update_gitee_file('ipv4.txt', ipv4)
else:
print('[ERROR] 无法获取 IPv4')

ipv6 = fetch_ip(IPV6_SERVICES, 'IPv6')
if ipv6:
print(f'[INFO] IPv6: {ipv6}')
update_gitee_file('ipv6.txt', ipv6)
else:
print('[WARN] 无法获取 IPv6')

print("===== 执行完成 =====")

if __name__ == '__main__':
main()
```

### 3. 部署到家庭服务器
- 将脚本保存为 `update_ip_to_gitee.py`。
- 安装依赖:`pip install requests`。
- 手动执行测试:`python update_ip_to_gitee.py`。
- 设置定时任务(例如每 5 分钟运行一次):
- **Windows**:使用任务计划程序。
- **Linux**:`crontab -e` 添加 `*/5 * * * * /usr/bin/python3 /path/to/update_ip_to_gitee.py`
- **宝塔面板**:直接添加计划任务,执行命令 `python /path/to/update_ip_to_gitee.py`

---

## 四、本地电脑:一键生成访问快捷方式

我们希望在任何电脑上(无需 Git)拉取最新 IP,并生成一个双击就能打开家庭网页的快捷方式。

### 1. 脚本功能
- 通过 Gitee API 直接获取 `ipv4.txt` 和 `ipv6.txt` 的内容。
- 自动识别操作系统(Windows/macOS/Linux),生成对应格式的快捷方式(`.url` / `.webloc` / `.desktop`)。
- 快捷方式保存在脚本所在目录,方便移动使用。

### 2. 完整代码 `sync_and_create_shortcut.py`

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import requests
import base64

# ========== 配置区域 ==========
GITEE_TOKEN = '你的私人令牌'
OWNER = '你的用户名'
REPO = 'homeddns'
BRANCH = 'master'
WEB_PORT = 8080 # 你的家庭 Web 服务端口
# =============================

# 获取脚本所在目录(兼容 PyInstaller 打包)
if getattr(sys, 'frozen', False):
SCRIPT_DIR = os.path.dirname(sys.executable)
else:
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))

# 如果当前目录不可写,降级到桌面
if not os.access(SCRIPT_DIR, os.W_OK):
SCRIPT_DIR = os.path.join(os.path.expanduser('~'), 'Desktop')
print(f'[WARN] 原目录不可写,改用桌面目录: {SCRIPT_DIR}')

def get_file_content(file_path):
url = f'https://gitee.com/api/v5/repos/{OWNER}/{REPO}/contents/{file_path}'
headers = {'Authorization': f'token {GITEE_TOKEN}'}
params = {'ref': BRANCH}
try:
resp = requests.get(url, headers=headers, params=params, timeout=10)
if resp.status_code == 200:
data = resp.json()
content_b64 = data.get('content', '')
if content_b64:
return base64.b64decode(content_b64).decode('utf-8').strip()
elif resp.status_code == 404:
print(f'[ERROR] 文件 {file_path} 不存在')
else:
print(f'[ERROR] 获取 {file_path} 失败,状态码 {resp.status_code}')
return None
except Exception as e:
print(f'[ERROR] 网络错误: {e}')
return None

def create_shortcut_windows(filepath, url):
content = f"""[InternetShortcut]
URL={url}
IconFile=%SystemRoot%\\System32\\SHELL32.dll
IconIndex=13
"""
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)

def create_shortcut_macos(filepath, url):
content = f"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>URL</key>
<string>{url}</string>
</dict>
</plist>
"""
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)

def create_shortcut_linux(filepath, url):
content = f"""[Desktop Entry]
Type=Link
Name=Home Web
URL={url}
Icon=text-html
"""
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)

def main():
if sys.platform == 'win32':
create_func = create_shortcut_windows
ext = '.url'
elif sys.platform == 'darwin':
create_func = create_shortcut_macos
ext = '.webloc'
else:
create_func = create_shortcut_linux
ext = '.desktop'

print(f'快捷方式将保存在: {SCRIPT_DIR}')
ipv4 = get_file_content('ipv4.txt')
ipv6 = get_file_content('ipv6.txt')

if ipv4:
url4 = f'http://{ipv4}:{WEB_PORT}/'
path4 = os.path.join(SCRIPT_DIR, f'Home_IPv4{ext}')
create_func(path4, url4)
print(f'[OK] IPv4 快捷方式: {path4}')
print(f' URL: {url4}')
else:
print('[WARN] 无法获取 IPv4')

if ipv6:
url6 = f'http://[{ipv6}]:{WEB_PORT}/'
path6 = os.path.join(SCRIPT_DIR, f'Home_IPv6{ext}')
create_func(path6, url6)
print(f'[OK] IPv6 快捷方式: {path6}')
print(f' URL: {url6}')
else:
print('[WARN] 无法获取 IPv6')

# 非打包模式或打包但不需要等待时,可省略 input
if not getattr(sys, 'frozen', False):
input('按 Enter 退出...')

if __name__ == '__main__':
main()
```

### 3. 使用方式
- **直接运行 Python 脚本**:需安装 `requests` 库。
- **打包为 exe**(推荐,适合无 Python 环境):
```bash
pip install pyinstaller
pyinstaller --onefile --noconsole sync_and_create_shortcut.py
```
生成的 exe 文件可以放在任意文件夹(如 U 盘、桌面),双击运行即生成快捷方式。

### 4. 高级用法:定时自动更新
Windows 下可将 exe 添加到任务计划程序,设置每天运行一次,确保快捷方式指向最新 IP。

---

## 五、常见问题与解决方案

### 1. 控制台报错 `UnicodeEncodeError: 'gbk' codec can't encode character`
**原因**:Windows 默认控制台编码为 GBK,而脚本中使用了 emoji(✅❌)或中文字符。
**解决**:已在脚本开头强制将 stdout 编码设为 UTF-8,并将 emoji 替换为 `[OK]`、`[ERROR]` 等 ASCII 文本。

### 2. 打包后出现 `RuntimeError: input0: lost sys.stdIn`
**原因**:使用 `--noconsole` 打包后没有控制台,`input()` 无法调用。
**解决**:在脚本中判断是否打包环境,仅当非打包时调用 `input`,或完全删除该行。

### 3. Gitee API 返回 `sha is missing`
**原因**:文件已存在但更新时未提供 `sha` 字段。
**解决**:脚本已实现自动获取 `sha` 并重试。

### 4. 获取不到公网 IPv6
**原因**:家庭网络可能未分配公网 IPv6 或被光猫/路由器阻止。
**解决**:检查 [ipv6.ip.sb](https://ipv6.ip.sb) 是否显示地址;若没有,可只用 IPv4 方案。

### 5. 访问快捷方式时打不开网页
- 确认路由器已正确配置端口转发。
- 确认家庭 Web 服务正在运行。
- 尝试用 IPv6 地址直接访问(如果支持),IPv6 通常不需要端口转发。

---

## 六、总结

通过这套方案,你拥有了一个完全自主可控、免费且稳定的动态 IP 同步系统。
- **服务器端**:每 5 分钟自动将 IPv4/IPv6 写入 Gitee 私有仓库。
- **客户端**:一键生成快捷方式,随时访问家庭服务。

你可以将客户端 exe 分享给家人或朋友,他们只需双击即可访问你的家庭网页,无需安装任何环境。

**扩展思路**:
- 搭配 `frp` 或 `ZeroTier` 实现内网穿透,彻底解决无公网 IP 的问题。
- 将快捷方式部署在手机桌面(iOS 可通过“添加到主屏幕”,Android 可使用 URL 快捷方式)。

如果你在使用中遇到任何问题,欢迎在评论区留言交流。

---

**项目源码已开源**:
Gitee 仓库地址:`https://gitee.com/你的用户名/homeddns`(私有,仅自己可见)
客户端脚本可任意分发,只需修改配置中的令牌和用户名即可。

**感谢阅读,祝玩得开心!**

http://www.cnnetsun.cn/news/2569883.html

相关文章:

  • 云 PACS 系统全院级影像数字化落地方案
  • 构建数据管道深度监控体系:从质量契约到工程实践
  • Python TDD实战入门:从red-green-refactor到高覆盖率测试套件
  • 从一次CAN总线‘丢帧’排查说起:深入理解扩展帧过滤器的‘列表模式’与‘掩码模式’到底怎么选
  • 用51单片机和MJ-8000模块,做个自己的扫码小助手(附完整代码和接线图)
  • 低成本AI网站审计工具架构:批处理与纯函数设计实现0.03美元单次成本
  • 保姆级教程:用STM32F103驱动TM1620数码管,从看懂手册到点亮第一个数字
  • DeepSeek评估被90%团队忽略的关键漏洞:上下文长度突变下的稳定性崩塌(附自动化检测脚本)
  • Excel时间计算底层原理:序列号机制与[h]:mm格式解析
  • 硬件在环(HIL)测试入门:如何用自制的60通道万能BOB盒搭建你的第一个汽车ECU测试台架?
  • AArch64虚拟化调试:HDFGWTR2_EL2寄存器原理与应用
  • Godot4节点生命周期与GDScript交互开发入门
  • AMD Ryzen处理器深度调优解决方案:SMUDebugTool实战指南与原理剖析
  • 为什么架构师越老越值钱?越陈越香的IT界茅台
  • 基于RAG与向量数据库构建代码库智能问答系统
  • C#游戏物理引擎的SIMD向量加速实战
  • 告别外设不足:用MCP2517FD给ESP32或树莓派Pico扩展CAN FD接口实战
  • PMP考试选机构,守住“双授权+本地考场”两条红线!
  • 从西门子/欧姆龙转过来?台达DVP50MC11T Modbus寻址的‘异类’解读
  • 4-20mA回路供电显示模块设计:低功耗高精度工业仪表方案
  • Unity多人游戏架构解析:GC2+Photon的权衡与裂缝
  • Excel频率分布四大方法实战指南:FREQUENCY、透视表、分析工具库与COUNTIFS深度对比
  • 机器学习在热电材料发现中的应用:数据分割与特征选择策略
  • SAP财务凭证替代避坑指南:从VF01销售发票到MIRO发票校验,AC_DOCUMENT BADI的字段映射与性能考量
  • vshell:面向红队实战的命令执行与会话管理框架
  • 基于规则引擎的AI代码生成:构建可靠后端服务的实践
  • Android 12 ART符号隐藏与Frida Hook适配实战
  • 嵌入式实时紧急车辆警笛检测系统设计与优化
  • 别再折腾pip了!Windows下用Python 3.8+一键搞定pygame游戏开发环境(附阿里云镜像)
  • 【紧急预警】DeepSeek升级v3.1后P99延迟飙升300%?3个必须验证的Tokenizer兼容性陷阱