手把手教你用Python脚本自动刷题:以BugKu‘秋名山车神’为例(附正则表达式详解)
·
Python自动化刷题实战:从页面解析到正则表达式精讲
在CTF竞赛和编程练习中,经常会遇到需要快速计算并提交答案的题目。这类题目往往要求选手在极短时间内完成复杂的数学运算,手动操作不仅效率低下,还容易出错。本文将带你用Python实现一个自动化解题脚本,以经典题目"秋名山车神"为例,深入讲解requests库的使用、正则表达式匹配技巧以及会话保持等关键技术点。
1. 理解题目逻辑与自动化需求
"秋名山车神"这类题目通常具有以下特征:
- 每次刷新页面都会生成新的数学表达式
- 需要在极短时间内提交正确答案(通常3-5秒)
- 提交方式多为POST请求,参数名为value
- 正确答案会返回flag或进入下一关卡
手动解题的痛点显而易见:
- 需要反复刷新页面获取新题目
- 必须快速心算或使用计算器
- 容易因操作延迟而超时
- 重复劳动,无法积累可复用的技术
自动化脚本的价值在于:
- 毫秒级响应,确保在时限内提交
- 100%计算准确率
- 一次编写,永久复用
- 可扩展应用到类似题目场景
2. 环境准备与基础工具
2.1 必要Python库安装
确保已安装以下Python库,若未安装可通过pip命令安装:
pip install requests
核心库功能说明:
- requests :处理HTTP请求,支持会话保持
- re :Python内置正则表达式模块,无需额外安装
2.2 开发环境配置建议
推荐使用以下工具组合:
- Python 3.7+(支持f-string等现代语法)
- VS Code + Python插件(智能提示和调试)
- Postman(辅助调试HTTP请求)
- Chrome开发者工具(分析页面结构)
3. 核心脚本实现与逐行解析
3.1 基础版脚本实现
import requests
import re
url = 'http://114.67.175.224:10053/'
s = requests.Session()
source = s.get(url)
expression = re.search(r'(\d+[+\-*])+(\d+)', source.text).group()
result = eval(expression)
post = {'value': result}
print(s.post(url, data=post).text)
关键代码解析:
requests.Session():创建会话对象,自动保持cookiess.get(url):发送GET请求获取题目页面re.search():使用正则表达式提取数学表达式eval():执行字符串形式的数学运算s.post():提交计算结果并打印响应
3.2 增强版脚本实现
import requests
import re
request = requests.Session()
url = "http://114.67.175.224:10053/"
response = request.get(url)
n = re.findall("<div>(.*?)=", response.text)[0]
num = eval(n)
data = {"value": num}
response1 = request.post(url, data=data)
f = re.findall('flag{(.*?)}', response1.text)
flag = "flag{" + f[0] +"}"
print(flag)
改进点分析:
- 更精确的正则匹配
<div>(.*?)=定位题目区域 - 直接提取并拼接flag格式
- 变量命名更具可读性
- 完整展示从获取题目到提取flag的全流程
4. 正则表达式深度解析
4.1 基础匹配模式
原始脚本使用的正则表达式 (\d+[+\-*])+(\d+) 分解:
\d+:匹配1个或多个数字[+\-*]:匹配+、-或*(注意转义)():分组捕获+:前面的模式重复1次或多次
4.2 高级匹配技巧
增强版脚本使用的 <div>(.*?)= 解析:
<div>:匹配文字div标签.*?:非贪婪匹配任意字符=:以等号结尾():只保留括号内内容
常见正则表达式模式对比:
| 模式 | 类型 | 匹配行为 | 示例 |
|---|---|---|---|
.* |
贪婪 | 匹配最长可能字符串 | "aabb" → "aabb" |
.*? |
非贪婪 | 匹配最短可能字符串 | "aabb" → "a", "a", "b", "b" |
\d+ |
量词 | 1个或多个数字 | "123" → "123" |
[abc] |
字符集 | 匹配任意指定字符 | "apple" → "a", "p", "p", "l", "e" |
(abc) |
分组 | 捕获匹配内容 | "abc" → "abc" |
4.3 正则表达式调试技巧
- 使用在线工具验证(如regex101.com)
- 分步测试复杂表达式
- 注意特殊字符转义
- 优先考虑非贪婪模式
- 合理使用分组捕获关键内容
# 正则测试示例
test_str = "3+5*2-8/4="
pattern = r'(\d+[+\-*/])+(\d+)'
match = re.search(pattern, test_str)
if match:
print(f"完整匹配: {match.group(0)}")
print(f"第一部分: {match.group(1)}")
print(f"第二部分: {match.group(2)}")
5. 脚本优化与错误处理
5.1 异常处理增强
基础脚本缺乏错误处理,可能导致以下问题:
- 网络请求失败
- 正则匹配不到表达式
- 数学表达式执行错误
- 服务器返回异常响应
增强版异常处理:
try:
response = request.get(url, timeout=5)
response.raise_for_status()
n = re.findall("<div>(.*?)=", response.text)
if not n:
raise ValueError("未找到数学表达式")
num = eval(n[0].strip())
data = {"value": num}
response1 = request.post(url, data=data, timeout=5)
response1.raise_for_status()
f = re.findall('flag{(.*?)}', response1.text)
if f:
print(f"成功获取flag: flag{f[0]}")
else:
print("未找到flag,响应内容:", response1.text)
except requests.exceptions.RequestException as e:
print(f"网络请求错误: {e}")
except Exception as e:
print(f"处理错误: {e}")
5.2 性能优化建议
- 设置合理的超时时间(如timeout=5)
- 复用Session对象减少连接开销
- 避免不必要的正则匹配
- 使用连接池提高效率
- 考虑异步请求处理(如aiohttp)
5.3 可配置化改进
将硬编码参数提取为配置变量:
CONFIG = {
"base_url": "http://114.67.175.224:10053/",
"timeout": 5,
"retry_times": 3,
"expression_pattern": r"<div>(.*?)=",
"flag_pattern": r"flag{(.*?)}",
"post_field": "value"
}
6. 扩展应用与类似题目实战
6.1 适用题目特征识别
以下特征的题目都可采用类似方法:
- 动态生成数学表达式
- 需要快速计算并提交
- 使用POST方法提交答案
- 答案验证在服务端完成
- 有明确的时间限制
6.2 变种题目应对策略
- 多步计算 :维护会话状态,顺序处理多个页面
- 验证码干扰 :集成OCR识别(如pytesseract)
- JS动态生成 :使用Selenium等浏览器自动化工具
- IP限制 :配合代理池使用
- 答案加密 :分析前端JS加密逻辑
6.3 实战演练:修改脚本应对新题目
假设新题目有以下变化:
- 表达式放在
<span class="expr">标签内 - 需要提交两个字段:answer和token
- token从页面meta标签获取
适配脚本示例:
def solve_new_challenge():
try:
s = requests.Session()
# 获取初始页面
resp = s.get("http://new.challenge.url/")
# 提取表达式和token
expr = re.search(r'<span class="expr">(.*?)</span>', resp.text).group(1)
token = re.search(r'<meta name="token" content="(.*?)">', resp.text).group(1)
# 计算答案
answer = eval(expr)
# 构造提交数据
data = {
"answer": answer,
"token": token
}
# 提交答案
result = s.post("http://new.challenge.url/submit", data=data)
print(result.text)
except Exception as e:
print(f"Error: {e}")
7. 安全注意事项与最佳实践
7.1 eval的安全隐患
直接使用eval执行未知来源的字符串存在严重安全风险:
- 可能执行恶意代码
- 消耗系统资源
- 破坏数据完整性
更安全的替代方案:
import ast
import operator
def safe_eval(expr):
# 允许的操作符
allowed_operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv
}
# 解析表达式
node = ast.parse(expr, mode='eval')
# 验证节点类型
if not isinstance(node, ast.Expression):
raise ValueError("必须是表达式")
def _eval(node):
if isinstance(node, ast.Num): # 数字
return node.n
elif isinstance(node, ast.BinOp): # 二元操作
left = _eval(node.left)
right = _eval(node.right)
op = allowed_operators.get(type(node.op))
if op is None:
raise ValueError(f"不允许的操作: {type(node.op)}")
return op(left, right)
else:
raise ValueError(f"不允许的节点类型: {type(node)}")
return _eval(node.body)
7.2 网络请求最佳实践
- 始终验证HTTPS证书
- 设置合理的超时时间
- 限制重试次数
- 不信任任何用户输入
- 敏感信息不硬编码在脚本中
7.3 正则表达式性能优化
- 预编译常用正则表达式
- 避免过度复杂的匹配模式
- 使用非贪婪模式减少回溯
- 合理使用字符集替代分支
- 优先使用具体匹配而非通配符
# 预编译正则示例
EXPR_PATTERN = re.compile(r'<div>(.*?)=')
FLAG_PATTERN = re.compile(r'flag{(.*?)}')
# 使用预编译模式
expr_match = EXPR_PATTERN.search(response.text)
更多推荐


所有评论(0)