每天重复做同样的事情——整理下载文件夹、备份重要文件、批量重命名图片、清理桌面……这些琐碎的任务单个看起来不值一提,但加在一起,每周可能浪费你好几个小时。
我之前也是这样。后来开始用Python把这些重复操作自动化,效果立竿见影。今天分享10个我实际在用的自动化脚本,每个都是解决真实痛点的,不是那种”看起来很酷但从来不会用”的demo。
下载文件夹永远是重灾区。这个脚本按文件类型自动归类:
import shutil
import os
from pathlib import Path
FILE_CATEGORIES = {
"文档": [".pdf", ".docx", ".doc", ".txt", ".xlsx", ".csv", ".pptx"],
"图片": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp"],
"视频": [".mp4", ".mov", ".avi", ".mkv", ".webm"],
"音频": [".mp3", ".wav", ".flac", ".aac", ".ogg"],
"压缩包": [".zip", ".rar", ".7z", ".tar", ".gz"],
"代码": [".py", ".js", ".ts", ".html", ".css", ".json", ".yaml"],
"安装包": [".dmg", ".exe", ".msi", ".deb", ".rpm", ".pkg"],
}
def organize_downloads(folder_path):
"""按文件类型整理文件夹"""
folder = Path(folder_path)
organized_count = 0
for file in folder.iterdir():
if not file.is_file():
continue
# 找到文件所属分类
target_category = None
for category, extensions in FILE_CATEGORIES.items():
if file.suffix.lower() in extensions:
target_category = category
break
if not target_category:
target_category = "其他"
# 创建分类目录并移动文件
target_dir = folder / target_category
target_dir.mkdir(exist_ok=True)
target_path = target_dir / file.name
# 避免文件名冲突
if target_path.exists():
stem = file.stem
counter = 1
while target_path.exists():
target_path = target_dir / f"{stem}_{counter}{file.suffix}"
counter += 1
shutil.move(str(file), str(target_path))
organized_count += 1
print(f"整理完成!共移动 {organized_count} 个文件")
# 使用
organize_downloads(os.path.expanduser("~/Downloads"))
重要数据定期备份,这个脚本支持增量备份(只备份变化的文件):
import shutil
import hashlib
import json
from pathlib import Path
from datetime import datetime
BACKUP_MANIFEST = ".backup_manifest.json"
def get_file_hash(filepath):
"""计算文件MD5哈希"""
hasher = hashlib.md5()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
hasher.update(chunk)
return hasher.hexdigest()
def backup_folder(source, backup_root):
"""增量备份文件夹"""
source_path = Path(source)
backup_root_path = Path(backup_root)
manifest_path = backup_root_path / BACKUP_MANIFEST
# 读取上次的备份记录
manifest = {}
if manifest_path.exists():
manifest = json.loads(manifest_path.read_text())
# 创建带时间戳的备份目录
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = backup_root_path / timestamp
backup_path.mkdir(parents=True, exist_ok=True)
new_manifest = {}
backed_up = 0
skipped = 0
for file in source_path.rglob("*"):
if not file.is_file():
continue
rel_path = str(file.relative_to(source_path))
file_hash = get_file_hash(file)
# 检查文件是否变化
if rel_path in manifest and manifest[rel_path] == file_hash:
skipped += 1
new_manifest[rel_path] = file_hash
continue
# 复制变化的文件
target = backup_path / rel_path
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(file, target)
new_manifest[rel_path] = file_hash
backed_up += 1
# 更新备份记录
manifest_path.write_text(json.dumps(new_manifest, indent=2))
print(f"备份完成!新增/更新 {backed_up} 个文件,跳过 {skipped} 个未变化文件")
print(f"备份位置: {backup_path}")
# 使用
backup_folder(
source=os.path.expanduser("~/Documents/重要项目"),
backup_root=os.path.expanduser("~/Backup")
)
从相机或手机导出的图片名字都是IMG_0001这种,这个脚本按拍摄日期重命名:
from pathlib import Path
from datetime import datetime
from PIL import Image
import os
def rename_photos_by_date(folder_path):
"""按拍摄日期重命名照片"""
folder = Path(folder_path)
renamed = 0
for file in sorted(folder.iterdir()):
if not file.is_file():
continue
if file.suffix.lower() not in [".jpg", ".jpeg", ".png", ".heic"]:
continue
try:
img = Image.open(file)
exif_data = img._getexif()
if exif_data and 36867 in exif_data:
# 36867 是EXIF中的拍摄日期字段
date_str = exif_data[36867]
date = datetime.strptime(date_str, "%Y:%m:%d %H:%M:%S")
else:
# 没有EXIF信息,使用文件修改时间
date = datetime.fromtimestamp(file.stat().st_mtime)
new_name = date.strftime("%Y%m%d_%H%M%S") + file.suffix
new_path = file.parent / new_name
# 避免重名
counter = 1
while new_path.exists():
new_name = date.strftime("%Y%m%d_%H%M%S") + f"_{counter}" + file.suffix
new_path = file.parent / new_name
counter += 1
file.rename(new_path)
renamed += 1
except Exception as e:
print(f"处理失败: {file.name} - {e}")
print(f"重命名完成!共处理 {renamed} 张照片")
# 使用
rename_photos_by_date(os.path.expanduser("~/Pictures/待整理"))
超过30天未使用的桌面文件自动归档:
import shutil
import os
import time
from pathlib import Path
def clean_desktop(desktop_path, archive_path, days_threshold=30):
"""清理桌面:将超过指定天数的文件移到归档目录"""
desktop = Path(desktop_path)
archive = Path(archive_path)
archive.mkdir(parents=True, exist_ok=True)
now = time.time()
threshold_seconds = days_threshold * 86400
moved = 0
for file in desktop.iterdir():
if not file.is_file():
continue
last_access = file.stat().st_atime
if (now - last_access) > threshold_seconds:
# 按原始日期归档
file_date = time.strftime(
"%Y-%m", time.localtime(file.stat().st_mtime)
)
target_dir = archive / file_date
target_dir.mkdir(exist_ok=True)
target = target_dir / file.name
if target.exists():
target = target_dir / f"{file.stem}_{int(now)}{file.suffix}"
shutil.move(str(file), str(target))
moved += 1
print(f"归档: {file.name} -> {target_dir.name}/")
print(f"\n清理完成!归档 {moved} 个文件")
# 使用
clean_desktop(
desktop_path=os.path.expanduser("~/Desktop"),
archive_path=os.path.expanduser("~/DesktopArchive"),
days_threshold=30
)
批量截取网页的长截图,适合做竞品分析或存档:
import asyncio
from playwright.async_api import async_playwright
from pathlib import Path
async def batch_screenshot(urls_file, output_dir):
"""批量截取网页截图"""
output = Path(output_dir)
output.mkdir(parents=True, exist_ok=True)
urls = Path(urls_file).read_text().strip().split("\n")
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page(viewport={"width": 1440, "height": 900})
for url in urls:
url = url.strip()
if not url:
continue
try:
await page.goto(url, wait_until="networkidle")
# 等待页面完全加载
await page.wait_for_timeout(2000)
filename = url.replace("https://", "").replace("http://", "").replace("/", "_")[:50]
filepath = output / f"{filename}.png"
await page.screenshot(path=str(filepath), full_page=True)
print(f"截图完成: {filename}")
except Exception as e:
print(f"截图失败: {url} - {e}")
await browser.close()
print(f"\n全部完成!截图保存在: {output_dir}")
# 使用
asyncio.run(batch_screenshot("urls.txt", "./screenshots"))
自动清洗CSV文件中的常见问题:空值、重复行、格式不统一:
import pandas as pd
from pathlib import Path
def clean_csv(input_path, output_path=None):
"""清洗CSV文件"""
input_file = Path(input_path)
if output_path is None:
output_path = input_file.parent / f"cleaned_{input_file.name}"
df = pd.read_csv(input_file)
original_rows = len(df)
print(f"原始数据: {original_rows} 行, {len(df.columns)} 列")
# 删除完全重复的行
df = df.drop_duplicates()
print(f"去重后: {len(df)} 行 (删除 {original_rows - len(df)} 行)")
# 清理列名(去除空格和特殊字符)
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
# 填充或删除空值
for col in df.columns:
null_count = df[col].isnull().sum()
if null_count > 0:
if df[col].dtype in ["int64", "float64"]:
df[col] = df[col].fillna(df[col].median())
else:
df[col] = df[col].fillna("未知")
print(f" 列 '{col}': 填充 {null_count} 个空值")
# 去除字符串列的前后空格
for col in df.select_dtypes(include=["object"]).columns:
df[col] = df[col].str.strip()
# 保存清洗后的数据
df.to_csv(output_path, index=False, encoding="utf-8-sig")
print(f"\n清洗完成!保存到: {output_path}")
# 使用
clean_csv("raw_data.csv")
在长时间工作时定时提醒休息:
import time
import subprocess
import platform
REMINDERS = [
"站起来活动一下!",
"喝杯水吧,你已经很久没喝水了",
"看看远处,放松一下眼睛",
"深呼吸三次,缓解肩颈紧张",
]
def send_notification(title, message):
"""发送系统通知"""
system = platform.system()
try:
if system == "Darwin": # macOS
subprocess.run([
"osascript", "-e",
f'display notification "{message}" with title "{title}"'
])
elif system == "Linux":
subprocess.run([
"notify-send", title, message
])
elif system == "Windows":
subprocess.run([
"powershell", "-Command",
f'[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms"); '
f'[System.Windows.Forms.MessageBox]::Show("{message}", "{title}")'
])
except Exception as e:
print(f"[通知] {title}: {message}")
def pomodoro_timer(work_minutes=25, break_minutes=5):
"""番茄钟定时器"""
cycle = 1
while True:
print(f"\n--- 第 {cycle} 个工作周期 ({work_minutes}分钟) ---")
time.sleep(work_minutes * 60)
reminder = REMINDERS[(cycle - 1) % len(REMINDERS)]
send_notification("休息时间到!", reminder)
print(f"--- 休息时间 ({break_minutes}分钟) ---")
time.sleep(break_minutes * 60)
send_notification("休息结束", "开始下一个工作周期吧!")
cycle += 1
# 使用
pomodoro_timer(work_minutes=25, break_minutes=5)
把Markdown文件批量转换为HTML或PDF:
import markdown
from pathlib import Path
from jinja2 import Template
HTML_TEMPLATE = Template("""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<style>
body { font-family: -apple-system, sans-serif; max-width: 800px; margin: 0 auto; padding: 2rem; line-height: 1.8; }
code { background: #f4f4f4; padding: 0.2rem 0.4rem; border-radius: 3px; }
pre { background: #f4f4f4; padding: 1rem; border-radius: 5px; overflow-x: auto; }
blockquote { border-left: 4px solid #ddd; margin: 0; padding-left: 1rem; color: #666; }
</style>
</head>
<body>
<h1 id="从零开始卖代码我的第一个数字产品上架记">从零开始卖代码:我的第一个数字产品上架记</h1>
<h2 id="一个大胆的想法">一个大胆的想法</h2>
<p>作为一个写了多年代码的程序员,我的硬盘里躺着无数个自己写的工具脚本、模板和自动化工具。有些是工作中为了提高效率随手写的,有些是学习新技术时的练手项目。它们安静地躺在硬盘角落里,除了我自己,没有人知道它们的存在。</p>
<p>直到有一天,我看到一个帖子:有人把自己写的数据处理脚本打包卖了几百美元。</p>
<p>我愣住了。这些我每天都在用的东西,居然可以卖钱?</p>
<p>于是我做了一个决定:把我最常用的数据处理模板整理出来,打包成一个产品,上架到代码交易平台上。</p>
<p>这就是我第一次”卖代码”的经历。</p>
<h2 id="第一步选品什么代码值得卖">第一步:选品——什么代码值得卖?</h2>
<p>不是所有代码都能卖。我花了两天时间研究各个代码交易平台上的热销产品,发现了一个规律:<strong>能解决具体问题的工具比通用框架更受欢迎。</strong></p>
<p>我最终选择了一个方向:Python数据处理模板包。原因很简单:</p>
<ol>
<li><strong>需求广泛。</strong> 几乎所有和数据打交道的行业都需要数据处理。</li>
<li><strong>复用性高。</strong> 数据清洗、格式转换、VLOOKUP匹配这些操作是高频需求。</li>
<li><strong>门槛适中。</strong> 不需要太复杂的架构,但需要足够的实用性。</li>
</ol>
<p>我筛选了自己常用的10个数据处理模板,涵盖了从基础的数据加载保存到高级的VLOOKUP匹配和数据透视表功能。</p>
<h2 id="第二步产品化从能用到好卖">第二步:产品化——从”能用”到”好卖”</h2>
<p>把一堆脚本变成一个”产品”,远比我想象中复杂。</p>
<p><strong>代码规范化。</strong> 我原本的脚本都是自己用的,变量命名随心所欲,注释几乎没有。为了让别人能看懂、能上手,我花了三天时间重写所有代码:统一命名规范、添加完整的文档字符串、编写使用示例。</p>
<p><strong>文档编写。</strong> 这是最耗时的部分。我写了详细的README,包含安装步骤、每个模板的功能说明、使用示例和常见问题解答。光是文档就写了将近3000字。</p>
<p><strong>示例数据。</strong> 为了让用户能快速上手,我为每个模板准备了示例数据文件。用户解压后可以直接运行看到效果,不需要自己准备数据。</p>
<p><strong>打包测试。</strong> 我在三个不同的操作系统上测试了整个模板包,确保解压即用,不需要额外的配置。</p>
<p>从”一堆能用的脚本”到”一个完整的产品”,我花了整整一周。</p>
<h2 id="第三步选平台代码去哪里卖">第三步:选平台——代码去哪里卖?</h2>
<p>代码交易平台有很多,我调研了十几个,最终选了几个来尝试:</p>
<p><strong>平台A(免审核,佣金5%)。</strong> 最简单,上传即上线,佣金也最低。适合快速验证产品是否有市场。</p>
<p><strong>平台B(需审核1-3天,佣金30%)。</strong> 审核流程严格,需要提供预览图、截图、详细描述。但流量较大,曝光机会更多。</p>
<p><strong>平台C(需审核,佣金30%)。</strong> 要求最严格,需要完善个人资料才能上传产品。预览图尺寸也和其他平台不同。</p>
<p>每个平台的规则都不一样:图片尺寸不同、定价规则不同、文件格式要求不同。光是准备各平台所需的素材,就花了我两天。</p>
<h2 id="第四步定价卖多少钱">第四步:定价——卖多少钱?</h2>
<p>定价是最纠结的环节。</p>
<p>我研究了同类产品的价格区间,发现数据处理模板的价格从$5到$50不等。太便宜了显得不专业,太贵了没人买。</p>
<p>我最终定价在$20左右。这个价格在同类产品中属于中等偏低,作为一个新卖家,低价策略有助于快速积累第一批用户和评价。</p>
<h2 id="第五步上架意想不到的坑">第五步:上架——意想不到的坑</h2>
<p>上架过程中踩了不少坑:</p>
<p><strong>图片尺寸问题。</strong> 每个平台要求的预览图尺寸完全不同。平台A要1000px以上,平台B要590x300,平台C要1600x800。我不得不为每个平台单独制作预览图。</p>
<p><strong>文件大小单位。</strong> 平台B的文件大小单位是MB,我填了16(以为是KB),结果提交不了。16KB应该填0.02MB。</p>
<p><strong>分类选择。</strong> 平台B的分类选项里没有”纯Python”,最接近的是”Django”。我只好选了Django,虽然我的产品和Django毫无关系。</p>
<p><strong>个人资料。</strong> 平台C要求先完善个人资料(头像、背景图、个人介绍)才能上传产品。我又花了半天准备这些材料。</p>
<h2 id="第六步等待与焦虑">第六步:等待与焦虑</h2>
<p>产品提交后,就是漫长的等待。</p>
<p>平台A即时上线,没有任何审核。平台B和平台C需要1-3天的审核时间。</p>
<p>等待审核的那几天,我反复检查产品页面:描述有没有错别字?截图清不清楚?价格合不合理?那种感觉就像高考完等成绩一样忐忑。</p>
<p>两天后,平台B审核通过了。看到”Published”状态的那一刻,我激动得差点从椅子上跳起来。</p>
<h2 id="经验总结">经验总结</h2>
<p>这次”卖代码”的经历教会了我很多:</p>
<p><strong>1. 产品化思维和写代码是两回事。</strong> 写代码追求功能实现,产品化追求用户体验。一个”能用的脚本”和一个”好卖的产品”之间,差的是文档、示例、测试和包装。</p>
<p><strong>2. 选对平台很重要。</strong> 不同平台的用户群体、佣金比例、审核规则差异很大。不要把鸡蛋放在一个篮子里,但也不能什么平台都上——选择3-4个最适合的就好。</p>
<p><strong>3. 素材准备比写代码更耗时。</strong> 预览图、截图、产品描述、使用文档——这些”非代码”工作占了整个产品化过程60%以上的时间。</p>
<p><strong>4. 定价策略需要灵活。</strong> 新卖家可以用低价积累评价,等有了足够的用户反馈再调整价格。</p>
<p><strong>5. 一次性买断是最省心的模式。</strong> 不需要维护服务器,不需要提供持续更新,用户下载后自己负责。这对独立开发者来说是最友好的商业模式。</p>
<h2 id="写在最后">写在最后</h2>
<p>我的第一个数字产品上架了,虽然还没有迎来第一笔订单,但完成从”写代码”到”卖代码”的跨越,本身就是一种成长。</p>
<p>如果你也有大量自己写的工具和脚本,不妨考虑把它们产品化。你不需要辞职创业,不需要融资,只需要花一周时间把代码整理好,上传到合适的平台。</p>
<p>谁知道呢,也许你的下一个被动收入来源,就藏在你硬盘的某个角落里。</p>
<hr />
<p><em>这是一个独立开发者的真实经历。你有过卖数字产品的经验吗?欢迎分享你的故事。</em></p>
</body>
</html>
""")
def convert_markdown_folder(input_dir, output_dir):
"""批量转换Markdown文件为HTML"""
input_path = Path(input_dir)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
converted = 0
for md_file in input_path.glob("**/*.md"):
html_content = markdown.markdown(
md_file.read_text(encoding="utf-8"),
extensions=["fenced_code", "tables", "toc"]
)
html = HTML_TEMPLATE.render(
title=md_file.stem,
content=html_content
)
rel_path = md_file.relative_to(input_path)
output_file = output_path / rel_path.with_suffix(".html")
output_file.parent.mkdir(parents=True, exist_ok=True)
output_file.write_text(html, encoding="utf-8")
converted += 1
print(f"转换: {md_file.name} -> {output_file.name}")
print(f"\n转换完成!共 {converted} 个文件")
# 使用
convert_markdown_folder("./markdown_notes", "./html_output")
持续监控CPU和内存使用情况,超过阈值时告警:
import psutil
import time
from datetime import datetime
def monitor_system(cpu_threshold=80, mem_threshold=85, interval=60):
"""监控系统资源使用"""
print(f"开始监控 (CPU阈值: {cpu_threshold}%, 内存阈值: {mem_threshold}%)")
print("按 Ctrl+C 停止\n")
while True:
cpu = psutil.cpu_percent(interval=1)
mem = psutil.virtual_memory().percent
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
status = "正常"
if cpu > cpu_threshold:
status = f"CPU告警 ({cpu:.1f}%)"
elif mem > mem_threshold:
status = f"内存告警 ({mem:.1f}%)"
print(f"[{timestamp}] CPU: {cpu:5.1f}% | 内存: {mem:5.1f}% | {status}")
if "告警" in status:
# 记录告警日志
with open("system_alerts.log", "a") as f:
f.write(f"{timestamp} - {status}\n")
time.sleep(interval - 1)
# 使用
monitor_system()
把上面的脚本组合起来,定时自动执行:
import schedule
import time
import subprocess
import os
from datetime import datetime
def job_backup():
"""每天凌晨2点执行备份"""
print(f"[{datetime.now()}] 开始自动备份...")
# 调用备份脚本
subprocess.run(["python", "backup_script.py"])
def job_clean_desktop():
"""每周一早上9点清理桌面"""
print(f"[{datetime.now()}] 开始清理桌面...")
subprocess.run(["python", "clean_desktop.py"])
def job_organize_downloads():
"""每天下午6点整理下载文件夹"""
print(f"[{datetime.now()}] 开始整理下载文件夹...")
subprocess.run(["python", "organize_downloads.py"])
def job_system_check():
"""每小时检查系统资源"""
subprocess.run(["python", "monitor_system.py"])
# 设置调度
schedule.every().day.at("02:00").do(job_backup)
schedule.every().monday.at("09:00").do(job_clean_desktop)
schedule.every().day.at("18:00").do(job_organize_downloads)
schedule.every().hour.do(job_system_check)
print("自动化调度器已启动...")
while True:
schedule.run_pending()
time.sleep(60)
这10个脚本解决的都是”小问题”,但正是这些小问题在不知不觉中消耗着你的时间和精力。
如果你对Python自动化感兴趣,但不想从零开始写这些脚本,我之前整理了一套更完整的Creator Pro Bundle,里面包含了这些脚本的优化版本,还附带了一个可视化的调度面板和详细的使用文档。不一定要买,但可以看看思路——有时候别人的解决方案能给你启发。
自动化不是偷懒,是把你从重复劳动中解放出来,去做更有创造性的事情。哪怕每天只省下30分钟,一年下来就是180多个小时——足够你学一门新技能或者完成一个副业项目了。