python 自动化
python 自动化
chrome version 查询
chrome://version/
chromedriver 下载
https://registry.npmmirror.com/binary.html
https://registry.npmmirror.com/binary.html?path=chrome-for-testing/
下载后,把 chromedriver.exe 放 python/Scripts 下目录即可
网页全屏
F11
exe 日志调试
pdfToImage.exe > log.txt 2>&1
B站相关
[:上一个
]:下一个
音量:滑轮
全屏:f
弹幕:d
工具
提取所有媒体文件(图片,视频)
这个工具结合了 Python 的易用性和 Robocopy 的强大文件处理能力。
import threading
import subprocess
import sys
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
# 定义常见的媒体文件扩展名(用于构建 Robocopy 命令的通配符)
MEDIA_EXTENSIONS = [
'*.jpg', '*.jpeg', '*.png', '*.gif', '*.bmp', '*.tiff', '*.webp', '*.ico',
'*.mp4', '*.avi', '*.mov', '*.wmv', '*.flv', '*.mkv', '*.webm', '*.3gp',
'*.m4v', '*.mts', '*.ogg'
]
def run_robocopy_task(source_folder, output_folder, status_var, root):
"""在子线程中构建并执行 Robocopy 命令,确保版本兼容性。"""
# 确保任务开始时进度条进入滚动状态
progress_bar.start()
# --- 1. 构建 Robocopy 命令 ---
# Robocopy 基础参数:源目录、目标目录。必须使用绝对路径字符串。
command_parts = ['robocopy', source_folder, output_folder]
# 添加文件通配符
command_parts.extend(MEDIA_EXTENSIONS)
# 添加 Robocopy 选项:
# /S: 递归查找 (包括子目录)
# /R:1 /W:1: 重试 1 次,等待 1 秒
# /MT:16: 多线程加速 (可选,最大 128)
# /NP: 不显示进度百分比 (避免 GUI 输出问题)
# /NFL /NDL: 不记录文件名和目录名 (保持输出简洁)
command_parts.extend(['/S', '/R:1', '/W:1', '/MT:16', '/NP', '/NFL', '/NDL'])
# --- 2. 启动 Robocopy 进程 ---
status_var.set("任务正在运行,请等待...")
try:
# 使用 PIPE 替代 capture_output=True,实现版本兼容性
process = subprocess.run(
command_parts,
stdout=subprocess.PIPE, # 捕获标准输出
stderr=subprocess.PIPE, # 捕获错误输出
timeout=None
)
# 解码输出 (Robocopy 默认使用 GBK 编码)
# errors='ignore' 用于跳过无法识别的字符,避免解码错误
stdout_output = process.stdout.decode('gbk', errors='ignore')
stderr_output = process.stderr.decode('gbk', errors='ignore')
# Robocopy 返回码:0-7 都是成功的,大于 7 表示严重错误。
if 0 <= process.returncode <= 7:
result_msg = f"完成!成功提取文件至:\n{output_folder}"
status_var.set("Robocopy 任务完成。")
messagebox.showinfo("完成", result_msg)
else:
# 错误信息现在从 stderr_output 或 stdout_output 中获取
error_msg = f"Robocopy 任务失败 (返回码: {process.returncode})。\n错误信息:\n{stderr_output or stdout_output}"
status_var.set("Robocopy 任务失败。")
messagebox.showerror("错误", error_msg)
except Exception as e:
status_var.set("程序内部错误。")
messagebox.showerror("错误", f"运行 Robocopy 时出错:\n{e}")
finally:
# 任务结束后,执行清理和重置操作
progress_bar.stop() # 停止进度条滚动
progress_bar['value'] = 100 # 进度条拉满
start_button.config(state=tk.NORMAL) # 重新启用按钮
root.update_idletasks()
# -------- GUI 功能函数 ----------
def choose_source():
folder = filedialog.askdirectory(title="选择媒体文件来源文件夹")
source_var.set(folder)
def choose_target():
folder = filedialog.askdirectory(title="选择文件保存的目标文件夹")
target_var.set(folder)
def run_task():
"""启动任务前的检查和线程启动"""
src = source_var.get()
dst = target_var.get()
if not src or not dst:
messagebox.showwarning("提示", "请选择源文件夹和目标文件夹!")
return
# 路径检查和预处理
src_path = Path(src).resolve()
dst_path = Path(dst).resolve()
if not dst_path.exists():
try:
dst_path.mkdir(parents=True, exist_ok=True)
except Exception as e:
messagebox.showerror("错误", f"无法创建目标文件夹:\n{e}")
return
if src_path == dst_path:
messagebox.showwarning("警告", "源文件夹和目标文件夹不能相同!")
return
# 禁用按钮,准备运行
start_button.config(state=tk.DISABLED)
progress_bar['value'] = 0
status_var.set("开始 Robocopy 任务...")
# 使用线程启动 Robocopy 任务,防止 GUI 卡死
threading.Thread(
target=run_robocopy_task,
args=(str(src_path), str(dst_path), status_var, root),
daemon=True
).start()
# --- 初始化主窗口 ---
root = tk.Tk()
root.title("Robocopy 媒体文件提取工具")
root.geometry("480x280")
# --- GUI 变量 ---
source_var = tk.StringVar()
target_var = tk.StringVar()
status_var = tk.StringVar(value="等待选择文件夹...")
# --- 布局 ---
# 1. 源文件夹选择
tk.Label(root, text="源文件夹:").grid(row=0, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=source_var, width=40).grid(row=0, column=1)
tk.Button(root, text="选择...", command=choose_source).grid(row=0, column=2)
# 2. 目标文件夹选择
tk.Label(root, text="目标文件夹:").grid(row=1, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=target_var, width=40).grid(row=1, column=1)
tk.Button(root, text="选择...", command=choose_target).grid(row=1, column=2)
# 3. 开始按钮
start_button = tk.Button(root, text="开始提取 (Robocopy)", width=20, command=run_task)
start_button.grid(row=2, column=1, pady=15)
# 4. 状态标签
tk.Label(root, textvariable=status_var, anchor="w").grid(row=3, column=0, columnspan=3, padx=10, pady=5, sticky="w")
# 5. 进度条 (使用不确定模式显示运行状态)
progress_bar = ttk.Progressbar(root, length=400, mode='indeterminate')
progress_bar.grid(row=4, column=0, columnspan=3, padx=10, pady=5)
root.mainloop()
文件分类:分成几个子文件夹
import os
import shutil
import math
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
def split_and_copy_files(source_folder, output_folder, files_per_folder, sort_mode):
source = Path(source_folder)
target = Path(output_folder)
if not source.exists():
messagebox.showerror("错误", "源文件夹不存在!")
return
# 获取所有文件
files = [p for p in source.iterdir() if p.is_file()]
# 排序方式
if sort_mode == "time":
files.sort(key=lambda p: p.stat().st_mtime)
else:
files.sort()
total = len(files)
batches = math.ceil(total / files_per_folder)
for i in range(batches):
batch_folder = target / f"batch_{i+1}"
batch_folder.mkdir(parents=True, exist_ok=True)
for file in files[i*files_per_folder:(i+1)*files_per_folder]:
shutil.copy2(file, batch_folder / file.name)
messagebox.showinfo("完成", f"处理完成,共创建 {batches} 个子文件夹")
# -------- GUI ----------
def choose_source():
folder = filedialog.askdirectory()
source_var.set(folder)
def choose_target():
folder = filedialog.askdirectory()
target_var.set(folder)
def run_task():
src = source_var.get()
dst = target_var.get()
size = size_var.get()
sort = sort_var.get()
if not src or not dst or not size:
messagebox.showwarning("提示", "请填写完整信息!")
return
if not size.isdigit():
messagebox.showwarning("提示", "分组数量必须是数字")
return
split_and_copy_files(src, dst, int(size), sort)
root = tk.Tk()
root.title("文件自动分组工具")
root.geometry("520x220")
source_var = tk.StringVar()
target_var = tk.StringVar()
size_var = tk.StringVar()
sort_var = tk.StringVar(value="name")
tk.Label(root, text="源文件夹:").grid(row=0, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=source_var, width=40).grid(row=0, column=1)
tk.Button(root, text="选择", command=choose_source).grid(row=0, column=2)
tk.Label(root, text="目标文件夹:").grid(row=1, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=target_var, width=40).grid(row=1, column=1)
tk.Button(root, text="选择", command=choose_target).grid(row=1, column=2)
tk.Label(root, text="每个子文件夹数量:").grid(row=2, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=size_var, width=20).grid(row=2, column=1, sticky="w")
tk.Label(root, text="排序方式:").grid(row=3, column=0, sticky="e", padx=10)
sort_cb = ttk.Combobox(root, textvariable=sort_var, values=["name", "time"], width=10)
sort_cb.grid(row=3, column=1, sticky="w")
tk.Button(root, text="开始执行", width=15, command=run_task)\
.grid(row=4, column=1, pady=20)
root.mainloop()
整合文件夹内所有音频文件,设置间隔,排序,生成每首开始结束时间
传统的 Python 字符串排序 (.sort()) 会导致 1.mp3, 10.mp3, 2.mp3 的错误顺序。
我们现在使用 natsort.natsorted(),它可以识别数字并按文件系统的自然顺序进行排序:1.mp3, 2.mp3, ..., 10.mp3。
打包:将 exe 和 ffmpeg.exe 放在一起就可以
import os
import threading
import sys
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from pathlib import Path
from natsort import natsorted
import subprocess
# 核心音频处理库
try:
from pydub import AudioSegment
except ImportError:
def show_pydub_error():
messagebox.showerror("错误", "缺少 pydub 库。请先运行 'pip install pydub natsort' 进行安装,然后重启程序。")
sys.exit(1)
tk.Tk().after(100, show_pydub_error)
# 支持的音频文件扩展名
AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg', '.flac', '.m4a']
# ... (check_ffmpeg, choose_folder, choose_output 函数保持不变) ...
def check_ffmpeg():
"""检查 FFmpeg 是否在环境变量中,使用兼容 Python 3.6 的参数"""
try:
subprocess.run(
['ffmpeg', '-version'],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
def choose_folder():
"""选择源文件夹"""
folder = filedialog.askdirectory(title="选择包含音频文件的文件夹")
folder_var.set(folder)
def choose_output():
"""选择输出目标文件夹"""
folder = filedialog.askdirectory(title="选择整合文件保存的目标文件夹")
output_var.set(folder)
def format_ms_to_time(ms):
"""将毫秒转换为 HH:MM:SS.ms 格式的字符串"""
seconds, ms = divmod(ms, 1000)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}.{int(ms):03}"
def merge_audio_task(source_folder, output_folder, interval_sec, status_var, progress_bar, root):
"""
核心任务:将文件夹内的所有音频文件整合到单个文件中,并插入间隔,并生成时间日志。
"""
if not check_ffmpeg():
messagebox.showerror("依赖错误", "未检测到 FFmpeg。\n请下载 FFmpeg 并将其可执行文件路径添加到系统的环境变量 (PATH) 中。")
progress_bar.stop()
start_button.config(state=tk.NORMAL)
return
# 任务开始时
progress_bar.start()
progress_bar['mode'] = 'determinate'
src_path = Path(source_folder)
out_path = Path(output_folder)
try:
interval_ms = int(interval_sec) * 1000
except ValueError:
messagebox.showerror("错误", "间隔时间必须是有效数字。")
progress_bar.stop()
start_button.config(state=tk.NORMAL)
return
# 1. 查找和排序音频文件
audio_files_paths = []
for ext in AUDIO_EXTENSIONS:
audio_files_paths.extend(list(src_path.glob(f'*{ext}')))
# 关键修正:使用 natsorted 进行自然排序
audio_files_str = [str(p) for p in audio_files_paths]
sorted_audio_files_str = natsorted(audio_files_str)
audio_files = [Path(p) for p in sorted_audio_files_str]
if not audio_files:
status_var.set("完成:未找到符合条件的音频文件。")
messagebox.showinfo("完成", "指定文件夹未找到音频文件。")
progress_bar.stop()
progress_bar['value'] = 0
start_button.config(state=tk.NORMAL)
return
# 2. 初始化拼接、静音段和时间记录
combined_audio = AudioSegment.empty()
silence = AudioSegment.silent(duration=interval_ms, frame_rate=44100)
total_files = len(audio_files)
# *** 时间记录变量 ***
current_ms = 0 # 当前累积时长(毫秒)
timing_log = []
progress_bar['maximum'] = total_files
# 3. 循环加载、拼接和更新进度
for i, audio_file in enumerate(audio_files):
filename = audio_file.name
status_var.set(f"({i + 1}/{total_files}) 正在处理: {filename}")
try:
current_audio = AudioSegment.from_file(audio_file)
# --- 记录时间 ---
start_ms = current_ms
duration_ms = len(current_audio) # 获取当前音频时长
end_ms = start_ms + duration_ms
timing_log.append({
'filename': filename,
'start_ms': start_ms,
'end_ms': end_ms
})
# --- 拼接音频 ---
combined_audio += current_audio
current_ms = end_ms # 更新累积时长
# --- 插入间隔 ---
if i < total_files - 1:
combined_audio += silence
current_ms += interval_ms # 累积时长加上间隔时间
# 更新 GUI 进度
progress_bar['value'] = i + 1
root.update_idletasks()
except Exception as e:
print(f"处理文件失败 {filename}: {e}")
status_var.set(f"警告: 无法处理 {filename},已跳过。")
root.update_idletasks()
# 4. 导出最终文件
output_filename = out_path / "整合后的音频_Combined.mp3"
log_filename = out_path / "整合音频时间日志.txt" # 日志文件路径
status_var.set(f"正在导出最终文件和生成日志...")
try:
combined_audio.export(output_filename, format="mp3", parameters=["-acodec", "libmp3lame"])
# --- 生成时间日志文件 ---
with open(log_filename, 'w', encoding='utf-8') as f:
f.write("--- 音频整合时间日志 ---\n")
f.write(f"总时长: {format_ms_to_time(len(combined_audio))}\n")
f.write(f"间隔时间: {interval_sec} 秒\n\n")
f.write(f"{'文件名':<40} {'开始时间':<15} {'结束时间':<15}\n")
f.write("-" * 75 + "\n")
for item in timing_log:
start_time = format_ms_to_time(item['start_ms'])
end_time = format_ms_to_time(item['end_ms'])
f.write(f"{item['filename'][:38]:<40} {start_time:<15} {end_time:<15}\n")
status_var.set(f"完成!已生成音频和时间日志文件。")
messagebox.showinfo("完成", f"音频整合成功!文件和日志已保存到:\n{out_path}")
except Exception as e:
error_msg = f"导出文件或日志失败。错误信息:\n{e}"
status_var.set("导出失败。检查 FFmpeg。")
messagebox.showerror("导出错误", error_msg)
finally:
progress_bar.stop()
start_button.config(state=tk.NORMAL)
# -------- GUI 功能函数 ----------
# ... (run_task 函数保持不变) ...
def run_task():
folder = folder_var.get()
output = output_var.get()
interval = interval_var.get()
if not folder or not output:
messagebox.showwarning("提示", "请选择源文件夹和输出文件夹!")
return
try:
interval_sec = int(interval)
if interval_sec < 0:
raise ValueError
except ValueError:
messagebox.showwarning("提示", "间隔时间必须是大于或等于零的整数。")
return
output_path = Path(output)
if not output_path.exists():
try:
output_path.mkdir(parents=True, exist_ok=True)
except Exception as e:
messagebox.showerror("错误", f"无法创建输出文件夹:\n{e}")
return
start_button.config(state=tk.DISABLED)
progress_bar['value'] = 0
status_var.set("开始任务...")
threading.Thread(
target=merge_audio_task,
args=(folder, output, interval_sec, status_var, progress_bar, root),
daemon=True
).start()
# --- 初始化主窗口 ---
root = tk.Tk()
root.title("Python 音频文件整合工具")
root.geometry("500x340")
# --- GUI 变量 ---
folder_var = tk.StringVar()
output_var = tk.StringVar()
interval_var = tk.StringVar(value="10")
status_var = tk.StringVar(value="等待选择文件夹...")
# --- 布局 ---
# 1. 源文件夹选择
tk.Label(root, text="源文件夹:").grid(row=0, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=folder_var, width=40).grid(row=0, column=1)
tk.Button(root, text="选择...", command=choose_folder).grid(row=0, column=2, padx=5)
# 2. 输出文件夹选择
tk.Label(root, text="输出文件夹:").grid(row=1, column=0, sticky="e", padx=10, pady=10)
tk.Entry(root, textvariable=output_var, width=40).grid(row=1, column=1)
tk.Button(root, text="选择...", command=choose_output).grid(row=1, column=2, padx=5)
# 3. 间隔设置
tk.Label(root, text="音频间隔 (秒):").grid(row=2, column=0, sticky="e", padx=10, pady=5)
tk.Entry(root, textvariable=interval_var, width=10).grid(row=2, column=1, sticky="w")
tk.Label(root, text="(0 表示无间隔)").grid(row=2, column=1, sticky="e", padx=(0, 10))
# 4. 开始按钮
start_button = tk.Button(root, text="开始整合音频", width=20, command=run_task)
start_button.grid(row=3, column=1, pady=20)
# 5. 状态标签
tk.Label(root, textvariable=status_var, anchor="w").grid(row=4, column=0, columnspan=3, padx=10, pady=5, sticky="w")
# 6. 进度条
progress_bar = ttk.Progressbar(root, length=460, mode='determinate')
progress_bar.grid(row=5, column=0, columnspan=3, padx=10, pady=5)
# 检查依赖(在启动 GUI 前)
try:
import natsort
except ImportError:
messagebox.showerror("错误", "缺少 natsort 库。请先运行 'pip install natsort' 进行安装。")
sys.exit(1)
root.mainloop()

浙公网安备 33010602011771号