今日目标:DevOps 日常工作本质上就是”处理文本”。今天我们将学习如何用 Python 优雅地读写 JSON/YAML 配置文件,并用 正则表达式 (Regex) 提取复杂的日志信息。今天不只是写代码,而是要彻底理解 “为什么 JSON/YAML 是配置的标准格式”、“正则表达式如何高效匹配文本” 以及 “如何避免常见的文件处理陷阱”。
学习内容 (30 mins)
在开始写代码前,先搞懂这些核心概念,否则后面的代码你会看得云里雾里。
什么是结构化数据?结构化数据是指有固定格式的数据,如 JSON、YAML、CSV。这些格式都有明确的语法规则,便于程序解析。为什么需要不同的数据格式?
- JSON:
- 优点:轻量级、易读、广泛支持(API 响应、配置文件)
- 缺点:不支持注释、格式较严格
- 应用:API 响应、配置文件、数据交换
- YAML:
- 优点:支持注释、可读性强、支持多行字符串
- 缺点:缩进敏感、解析较慢
- 应用:Kubernetes 配置、Docker Compose、CI/CD 配置
- CSV:
- 优点:简单、Excel 兼容、适合大数据
- 缺点:不支持嵌套结构、类型推断困难
- 应用:数据导出、报表生成、数据分析
Python 处理方式:
- JSON:内置库
json,无需安装
- YAML:第三方库
PyYAML,需要 pip install pyyaml
- CSV:内置库
csv,无需安装
什么是正则表达式?正则表达式(Regular Expression,简称 Regex)是一种描述字符串模式的语法,用于匹配、查找、替换文本。为什么需要正则表达式?
- 精确匹配:可以匹配复杂的文本模式(如 IP 地址、邮箱、日期)
- 批量处理:一次匹配多个模式,比字符串方法更强大
- 日志分析:从日志中提取关键信息(IP、时间、错误信息)
- 数据清洗:清理和格式化数据
常用正则模式:
\d+:匹配一个或多个数字(如:123、4567)
\w+:匹配一个或多个单词字符(字母、数字、下划线)
.*?:非贪婪匹配任意字符(尽可能少匹配)
\s+:匹配一个或多个空白字符(空格、制表符、换行)
^:匹配字符串开头
$:匹配字符串结尾
():捕获组,用于提取匹配的内容
Python 正则方法:
re.search():查找第一个匹配(返回 Match 对象或 None)
re.findall():查找所有匹配(返回列表)
re.sub():替换匹配的文本
re.match():从字符串开头匹配(类似 ^pattern)
代码任务 (90 mins)
环境准备
确保虚拟环境已激活,并安装必要的包:# 确保虚拟环境已激活(提示符前有 (.venv))
source .venv/bin/activate
# 安装 PyYAML(用于处理 YAML 文件)
pip install pyyaml
# 验证安装
python -c "import yaml; print('PyYAML installed successfully')"
任务 A:JSON 文件处理
编写 10_json_demo.py,演示 JSON 的读写操作。#!/usr/bin/env python3
"""
Day 10 - JSON 文件处理示例
演示 JSON 的读写和解析
"""
import json
# ========== 1. 写入 JSON 文件 ==========
print("=== 写入 JSON 文件 ===")
# 准备数据(字典格式)
config_data = {
"app": {
"name": "insightful-ops",
"version": "1.0.0",
"debug": False
},
"database": {
"host": "localhost",
"port": 3306,
"name": "infra_db"
},
"servers": ["web-01", "web-02", "db-01"]
}
# 写入 JSON 文件
# indent=4: 格式化输出,每级缩进 4 个空格(便于阅读)
# ensure_ascii=False: 允许非 ASCII 字符(如中文)
with open("config.json", "w", encoding="utf-8") as f:
json.dump(config_data, f, indent=4, ensure_ascii=False)
print("✅ 配置文件已写入 config.json")
# ========== 2. 读取 JSON 文件 ==========
print("\n=== 读取 JSON 文件 ===")
with open("config.json", "r", encoding="utf-8") as f:
loaded_data = json.load(f)
print(f"应用名称: {loaded_data['app']['name']}")
print(f"数据库端口: {loaded_data['database']['port']}")
print(f"服务器列表: {loaded_data['servers']}")
# ========== 3. JSON 字符串处理 ==========
print("\n=== JSON 字符串处理 ===")
# 模拟 API 返回的 JSON 字符串
api_response = '{"status": "success", "data": {"users": 100, "servers": 5}}'
# 解析 JSON 字符串
response_dict = json.loads(api_response)
print(f"状态: {response_dict['status']}")
print(f"用户数: {response_dict['data']['users']}")
# 将字典转换为 JSON 字符串
json_string = json.dumps(response_dict, indent=2)
print(f"\nJSON 字符串:\n{json_string}")
代码解释:
json.dump():将字典写入文件
json.load():从文件读取 JSON 并解析为字典
json.dumps():将字典转换为 JSON 字符串
json.loads():将 JSON 字符串解析为字典
运行脚本:验证步骤:
- 检查
config.json 文件是否创建
- 查看文件内容:
cat config.json
- 确认 JSON 格式正确(缩进、引号等)
任务 B:YAML 文件处理
编写 10_yaml_demo.py,演示 YAML 的读写操作。#!/usr/bin/env python3
"""
Day 10 - YAML 文件处理示例
演示 YAML 的读写和转换
"""
import yaml
import json
# ========== 1. 创建 YAML 内容 ==========
print("=== 创建 YAML 内容 ===")
# YAML 支持注释和多行字符串
yaml_content = """
# 应用配置
app:
name: insightful-ops
version: 1.0.0
debug: false
# 数据库配置
database:
host: localhost
port: 3306
name: infra_db
# 服务器列表
servers:
- web-01
- web-02
- db-01
"""
# ========== 2. 解析 YAML ==========
print("=== 解析 YAML ===")
# safe_load: 安全加载(只加载标准 YAML 标签,防止代码执行)
data = yaml.safe_load(yaml_content)
print(f"应用名称: {data['app']['name']}")
print(f"数据库端口: {data['database']['port']}")
# ========== 3. 写入 YAML 文件 ==========
print("\n=== 写入 YAML 文件 ===")
config_dict = {
"app": {
"name": "insightful-ops",
"version": "1.0.0"
},
"database": {
"host": "localhost",
"port": 3306
}
}
with open("config.yaml", "w", encoding="utf-8") as f:
yaml.dump(config_dict, f, default_flow_style=False, allow_unicode=True)
# default_flow_style=False: 使用块样式(更易读)
# allow_unicode=True: 允许 Unicode 字符
print("✅ 配置文件已写入 config.yaml")
# ========== 4. YAML 转 JSON ==========
print("\n=== YAML 转 JSON ===")
# 读取 YAML
with open("config.yaml", "r", encoding="utf-8") as f:
yaml_data = yaml.safe_load(f)
# 转换为 JSON
with open("config_from_yaml.json", "w", encoding="utf-8") as f:
json.dump(yaml_data, f, indent=4, ensure_ascii=False)
print("✅ YAML 已转换为 JSON")
代码解释:
yaml.safe_load():安全加载 YAML(推荐,防止代码执行)
yaml.dump():将字典写入 YAML 文件
default_flow_style=False:使用块样式,更易读
运行脚本:验证步骤:
- 检查
config.yaml 文件是否创建
- 查看文件内容:
cat config.yaml
- 确认 YAML 格式正确(缩进、注释等)
任务 C:正则表达式实战
编写 10_regex_demo.py,演示正则表达式的使用。#!/usr/bin/env python3
"""
Day 10 - 正则表达式实战
演示从日志中提取关键信息
"""
import re
# ========== 1. 基础匹配 ==========
print("=== 基础匹配 ===")
# 匹配 IP 地址
text = "Server IP: 192.168.1.100"
pattern = r'\d+\.\d+\.\d+\.\d+' # \d+ 匹配数字,\. 匹配点号
match = re.search(pattern, text)
if match:
print(f"找到 IP: {match.group()}") # 192.168.1.100
# ========== 2. 捕获组 ==========
print("\n=== 捕获组 ===")
# 从日志中提取 IP、方法和状态码
log_line = '192.168.1.10 - - [31/Jan/2026:14:00:00 +0800] "GET /index.html HTTP/1.1" 200 4096'
# 正则模式解释:
# (\d+\.\d+\.\d+\.\d+) - 捕获组1:IP 地址
# .*? - 非贪婪匹配任意字符
# "(\w+) - 捕获组2:请求方法(GET/POST等)
# .*?" - 匹配到引号结束
# \s(\d{3})\s - 捕获组3:状态码(3位数字)
pattern = r'(\d+\.\d+\.\d+\.\d+).*?"(\w+) .*?"\s(\d{3})\s'
match = re.search(pattern, log_line)
if match:
ip = match.group(1) # 第一个捕获组
method = match.group(2) # 第二个捕获组
status = match.group(3) # 第三个捕获组
print(f"IP: {ip}, 方法: {method}, 状态码: {status}")
# ========== 3. 查找所有匹配 ==========
print("\n=== 查找所有匹配 ===")
log_text = """
192.168.1.10 - - [31/Jan/2026:14:00:00] "GET /index.html" 200
192.168.1.11 - - [31/Jan/2026:14:01:00] "POST /api/data" 201
192.168.1.12 - - [31/Jan/2026:14:02:00] "GET /about.html" 404
"""
# 提取所有 IP 地址
ips = re.findall(r'\d+\.\d+\.\d+\.\d+', log_text)
print(f"所有 IP: {ips}")
# 提取所有状态码
status_codes = re.findall(r'\s(\d{3})\s', log_text)
print(f"所有状态码: {status_codes}")
# ========== 4. 文本替换 ==========
print("\n=== 文本替换 ===")
text = "Contact: email@example.com or phone: 138-0013-8000"
# 替换邮箱为 [EMAIL]
masked = re.sub(r'\w+@\w+\.\w+', '[EMAIL]', text)
print(f"脱敏后: {masked}")
# 替换手机号为 [PHONE]
masked = re.sub(r'\d{3}-\d{4}-\d{4}', '[PHONE]', masked)
print(f"进一步脱敏: {masked}")
# ========== 5. 复杂日志解析 ==========
print("\n=== 复杂日志解析 ===")
error_log = """
[2026-01-31 10:00:01] INFO: Service started
[2026-01-31 10:05:23] ERROR: Database connection timeout (IP: 192.168.1.5)
[2026-01-31 10:06:12] ERROR: Out of memory in worker process
[2026-01-31 10:07:00] INFO: Health check passed
"""
# 提取所有错误日志
error_pattern = r'\[([^\]]+)\]\s+ERROR:\s+(.+)'
errors = re.findall(error_pattern, error_log)
print("发现的错误:")
for timestamp, message in errors:
print(f" [{timestamp}] {message}")
# 从错误信息中提取 IP 地址
for timestamp, message in errors:
ip_match = re.search(r'\d+\.\d+\.\d+\.\d+', message)
if ip_match:
print(f" 错误 IP: {ip_match.group()}")
正则表达式详解:
\d+:一个或多个数字
\.:转义的点号(匹配字面量 .)
\w+:一个或多个单词字符
.*?:非贪婪匹配任意字符
():捕获组,用于提取匹配内容
\s:空白字符
运行脚本:验证步骤:
- 脚本能正常运行,无语法错误
- 观察匹配结果是否正确
- 确认捕获组提取的内容正确
- 确认文本替换功能正常
常见错误:
- ❌
re.error: bad escape - 正则表达式转义错误,检查反斜杠
- ❌
AttributeError: 'NoneType' object has no attribute 'group' - 匹配失败返回 None,应先检查
- ❌ 匹配结果不符合预期 - 检查正则模式是否正确,可以使用在线工具测试
拓展任务 (30 mins)
挑战 1:pathlib 实践
任务:使用 pathlib 库列出当前目录下所有的 .py 文件。提示:from pathlib import Path
# Path('.') 表示当前目录
# .glob('*.py') 查找所有 .py 文件
挑战 2:日志分析器
任务:编写一个日志分析器,从 Nginx 日志中统计:
- 每个 IP 的访问次数
- 每个状态码的出现次数
- 最常访问的 URL
提示:使用字典存储统计结果,正则表达式提取信息。
今日产出物
10_json_demo.py - JSON 处理示例
10_yaml_demo.py - YAML 处理示例
10_regex_demo.py - 正则表达式示例
config.json - 生成的 JSON 配置文件
config.yaml - 生成的 YAML 配置文件
参考代码
实际应用场景
文件处理在运维中的应用
- 配置管理:读取和更新应用配置文件(JSON/YAML)
- 数据导出:将监控数据导出为 CSV 格式
- API 集成:解析 API 返回的 JSON 响应
- 日志分析:从日志文件中提取关键信息
正则表达式在运维中的应用
- 日志解析:从日志中提取 IP、时间、错误信息
- 数据清洗:清理和格式化数据
- 文本替换:批量替换配置文件中的内容
- 模式匹配:验证输入格式(如邮箱、IP 地址)
与 Day 11 的关联:今天学习的文件处理和正则表达式,明天会学习如何用 Python 调用 Shell 命令(subprocess),将文件处理和系统命令结合起来。
上一天: 数据结构进阶
Day 09 | 数据结构与装饰器
下一天: 系统调用
Day 11 | Python 调用 Shell