前言
由于实验室服务器的端口每天都在变化,每次连接服务器都要登录网页查看端口,再在配置文件中修改,有些繁琐。突发奇想,用ai写了一个python脚本并用批处理指令一键执行。
python脚本
这段代码的核心功能是自动获取cpolar内网穿透隧道的动态端口,并更新本地SSH配置文件。
- 需要的库
requests:用于发送HTTP请求,模拟浏览器访问cpolar的登录页面和隧道状态接口。- 提交登录表单(如用户名和密码)
- 获取隧道状态页面的HTML内容
- 处理会话保持(如登录后的Cookie管理)
python-dotenv:从.env文件中加载环境变量,避免敏感信息(如用户名、密码)硬编码到脚本中。.env文件存储cpolar的用户名和密码
re:正则表达式,python内置库,在这里用于从HTML中解析动态端口号
- 思路
- 最主要的是用了正则表达式,ai生成的还蛮好用,也可能是任务比较简单。
- 从本地.env文件加载用户名和密码、确定目标URL
- 敏感信息与代码分离
- 使用
os.environ.get()避免硬编码
- 登录网页
- 用正则表达式解析在线隧道
- SSH配置更新
- 精准匹配Host块(避免误修改)
- 保留原始缩进格式
- 仅更新指定Host的Port行
- 动创建
.bak备份文件 - 原子性操作(先读后写)
代码
- 保存的文件名:get_cpolar_port.py
import requests
import os
from dotenv import load_dotenv
import shutil
import re
# 加载 .env 文件(需要修改成自己的)
dotenv_path = 'path/to/.env'
load_dotenv(dotenv_path=dotenv_path)
USERNAME = os.environ.get("CPOLAR_USER") # 加载.env文件中的用户名
PASSWORD = os.environ.get("CPOLAR_PASS") # 加载.env文件中的密码
LOGIN_URL = "https://dashboard.cpolar.com/login"
TARGET_URL = "https://dashboard.cpolar.com/status"
# --- 配置结束 ---
def fetch_logged_in_page(username, password):
"""
模拟登录并获取登录后页面的源代码。
优化点:统一捕获所有网络请求异常,避免程序因网络问题崩溃
"""
with requests.Session() as session:
# 1. 获取登录页面(确保初始cookies)
try:
session.get(LOGIN_URL)
except requests.RequestException as e:
print(f"访问登录页面失败: {e}")
return None
# 2. 构造登录表单数据
login_payload = {'login': username, 'password': password}
# 3. 发送登录请求(统一异常捕获)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Referer': LOGIN_URL
}
print("正在尝试登录...")
try:
response = session.post(LOGIN_URL, data=login_payload, headers=headers)
except requests.RequestException as e:
print(f"登录请求失败: {e}")
return None
# 4. 检查登录状态
if response.status_code != 200 or "dashboard" not in response.url:
print(f"登录失败! 状态码: {response.status_code}")
print("响应内容:", response.text[:200]) # 保留调试信息
return None
print("登录成功!")
# 5. 获取目标页面(统一异常捕获)
print(f"正在获取目标页面: {TARGET_URL}")
try:
target_response = session.get(TARGET_URL, headers=headers)
except requests.RequestException as e:
print(f"获取目标页面失败: {e}")
return None
if target_response.status_code == 200:
print("成功获取登录后的页面源代码!")
return target_response.text
print(f"获取目标页面失败,状态码: {target_response.status_code}")
return None
html_source = fetch_logged_in_page(USERNAME, PASSWORD)
# 1. 定义目标隧道名称(需要修改成自己的)
target_tunnel_name = "your tunnel name"
# 2. 编写正则表达式(保持原逻辑,仅优化注释)
pattern = rf'<tr>.*?<td>{target_tunnel_name}</td>.*?<a[^>]*>(.*?)</a>.*?</tr>'
match = re.search(pattern, html_source, re.DOTALL)
# 3. 处理匹配结果(保持原逻辑)
if match:
extracted_url = match.group(1)
print(f"隧道名称 '{target_tunnel_name}' 对应的 URL 是: {extracted_url}")
else:
print(f"未找到隧道名称为 '{target_tunnel_name}' 的信息。")
exit(1) # 明确退出避免后续错误
# --- 配置区 ---(可能需要修改成自己的)
TARGET_HOST = extracted_url[6:-6] # 提取主机名部分(原逻辑不变)
NEW_PORT = extracted_url[-5:] # 获取后五位端口(原逻辑不变)
# vscode中ssh插件的配置文件位置(需要修改成自己的)
SSH_CONFIG_PATH = os.path.expanduser("path/to/.ssh/config")
def update_ssh_port_regex(host, new_port):
"""
使用正则表达式更新SSH配置文件。
优化点:
- 明确文件存在性检查
- 优化正则表达式注释
- 保持原逻辑不变
"""
if not os.path.exists(SSH_CONFIG_PATH):
print(f"错误: SSH配置文件未找到于 {SSH_CONFIG_PATH}")
return
# 创建备份
backup_path = f"{SSH_CONFIG_PATH}.bak"
shutil.copy2(SSH_CONFIG_PATH, backup_path)
print(f"已创建备份文件: {backup_path}")
try:
# 读取配置文件
with open(SSH_CONFIG_PATH, 'r') as f:
content = f.read()
# 匹配Host块的正则(优化注释说明)
# 说明:匹配以 "Host [host]" 开头的块,直到下一个Host或文件末尾
host_block_pattern = re.compile(
rf"(^Host\s+{re.escape(host)}\b.*?)(?=\s*^Host|\Z)",
re.MULTILINE | re.DOTALL
)
# 替换Port行的正则
port_pattern = re.compile(r"^(?P<indent>\s*)Port\s+\d+", re.MULTILINE)
def replacer(match):
block = match.group(1)
# 在块内替换Port行(保留缩进)
updated_block = port_pattern.sub(f"\g<indent>Port {new_port}", block)
return updated_block
# 执行替换
updated_content, count = host_block_pattern.subn(replacer, content, count=1)
if count > 0:
with open(SSH_CONFIG_PATH, 'w') as f:
f.write(updated_content)
print(f"在Host '{host}'中找到Port行,已更新为 {new_port}。")
else:
print(f"警告: 未找到Host '{host}' 或该Host下没有Port行。")
except Exception as e:
print(f"发生错误: {e}")
print("请检查备份文件并手动恢复。")
update_ssh_port_regex(TARGET_HOST, NEW_PORT)
bat批处理指令
由于电脑上没有安装python,遂用miniconda的python环境代替。
在这一阶段需要设置miniconda的安装路径,并需要将miniconda添加到系统的环境变量中。
此外,还需要指定conda的环境,该环境需要包含python脚本中用到的库。
最后,需要设置python脚本的位置,这使得py文件和bat文件可以不在同一目录下。
bat基本命令
:::两个英文冒号是注释的标记@echo off:在执行下面的命令时,不要把命令本身显示在屏幕上,保持界面干净整洁。setlocal enabledelayedexpansion:开启一个‘临时工作区’,并启用一个特殊功能。setlocal:创建一个“临时工作区”。在这个区域里设置的所有变量(比如后面要说的路径),等脚本执行完后都会自动消失,不会影响电脑的全局环境。- 结尾有
endlocal
- 结尾有
enabledelayedexpansion:开启“延迟变量扩展”。简单说,普通变量用%变量名%,电脑在读取命令时就会立刻替换成它的值。但有时候,我们需要在一段代码(比如if判断块)执行之后,再获取变量的最新值。这时就需要用!变量名!的形式,并开启这个功能。你可以把它理解为“等我用到的时候再去看这个变量里是什么,而不是现在就看”。
set "变量名=值":设置变量,文件路径也是变量,canda环境也是变量exit /b 1:退出脚本,并返回一个错误代码1。表示“任务失败”。pause:暂停脚本,屏幕上会显示“请按任意键继续…”,让你有时间看清错误信息。if not exist "路径":这是一个安全检查。电脑会去指定的路径看一看文件夹在不在。如果不在(not exist),就执行括号()里的命令。echo string:在屏幕上显示echo后的string,不需要引号。|:管道符,意思是把前一个命令的结果,传给后一个命令处理。findstr:在结果里查找指定的字符串。/C:string:使用指定字符串作为文字搜索字符串。- 如果
findstr找到了,errorlevel就是 0;否则是1或非零数字。
>nul:把 前面的结果扔进“黑洞”(nul),不让它显示在屏幕上。call:“调用”另一个程序或脚本。
基本思路
- 设置miniconda、python脚本位置以及使用的conda环境
- 检查1中的设置是否存在问题,使用if语句
- 调用miniconda自带的activate.bat激活所选环境
- 执行python脚本
- 退出
- 增加必要的文字输出以增强交互
代码
- bat代码中的中文输出可能乱码,保存成ANSI格式
@echo off
setlocal enabledelayedexpansion
:: ===================================================================
:: 用户配置区域
:: ===================================================================
:: 1. 设置你的 Miniconda 安装路径。
:: 如果你的路径不同,请手动修改下面的值。(anaconda也可以)
set "MINICONDA_PATH=path\to\miniconda3"
:: 2. 设置你为这个项目创建的 Conda 环境名称。
set "CONDA_ENV_NAME=base"
:: 3. 设置你的 Python 脚本的完整路径。
:: 现在批处理文件和Python脚本可以不在同一个目录了。
set "PYTHON_SCRIPT_PATH=path\to\get_cpolar_port.py"
:: ===================================================================
:: 脚本执行区域
:: ===================================================================
:: 检查 Miniconda 路径是否存在
if not exist "%MINICONDA_PATH%" (
echo 错误: 在以下位置未找到 Miniconda 安装:
echo %MINICONDA_PATH%
echo.
echo 请编辑此文件并正确设置 MINICONDA_PATH 变量。
pause
exit /b 1
)
:: 【修改】检查 Python 脚本是否存在
if not exist "%PYTHON_SCRIPT_PATH%" (
echo 错误: 在以下位置未找到 Python 脚本:
echo %PYTHON_SCRIPT_PATH%
echo.
echo 请编辑此文件并正确设置 PYTHON_SCRIPT_PATH 变量。
pause
exit /b 1
)
:: 检查 Conda 环境是否存在
echo 正在检查 Conda 环境 "%CONDA_ENV_NAME%"...
conda info --envs | findstr /C:"%CONDA_ENV_NAME%" >nul
if !errorlevel! neq 0 (
echo.
echo 错误: 未找到名为 "%CONDA_ENV_NAME%" 的 Conda 环境。
echo.
echo 请先打开 "Anaconda Prompt" 并运行以下命令来创建环境:
echo conda create -n %CONDA_ENV_NAME% python requests python-dotenv -c conda-forge
pause
exit /b 1
)
:: 激活 Conda 环境并运行脚本
echo 正在激活 Conda 环境...
call "%MINICONDA_PATH%\Scripts\activate.bat" %CONDA_ENV_NAME%
if !errorlevel! neq 0 (
echo.
echo 错误: 激活环境失败。请检查 MINICONDA_PATH 是否正确。
pause
exit /b 1
)
echo.
echo 环境已激活。正在运行 Python 脚本...
echo -------------------------------------------------
:: 【修改】使用变量来运行 Python 脚本
python "%PYTHON_SCRIPT_PATH%"
echo -------------------------------------------------
:: 检查 Python 脚本执行结果
if !errorlevel! equ 0 (
echo.
echo 脚本执行成功!
) else (
echo.
echo 脚本执行失败,错误代码: !errorlevel!
)
echo.
echo 操作完成。
pause
endlocal

被折叠的 条评论
为什么被折叠?



