本文覆盖 7 大高频场景、11 个可直接运行的代码片段,并提供一套完整的「每日自动日报」工作流。无需复杂架构,只需 Python 标准库 + 几个轻量第三方包,即可把重复劳动交给脚本。


一、为什么需要个人自动化?

作为开发者,我们每天都在经历这样的场景:

  • 下载文件夹堆积如山,找文件像考古;
  • 每周一要手动汇总 Excel 报表,复制粘贴到眼花;
  • 想监控某个商品价格,却不得不每天打开网页查看;
  • 定时发送日报/周报,内容固定但操作重复。

这些任务并不复杂,却极度消耗注意力。自动化的核心目的不是「偷懒」,而是把认知资源留给真正需要创造性思考的工作。

Python 凭借其丰富的生态和简洁的语法,是个人自动化领域性价比最高的工具。本文将从「文件系统 → 办公文档 → 网络请求 → 系统调度」四个层级,逐步搭建你的自动化能力。


二、环境准备:核心依赖一览

在开始之前,建议创建一个独立的虚拟环境:

python -m venv automation-env
source automation-env/bin/activate  # Windows: automation-env\Scripts\activate

pip install openpyxl python-docx requests beautifulsoup4 \
            selenium schedule pyautogui Pillow PyPDF2 \
            lxml pandas

💡 提示:如果你只关注某几个场景,可以按需安装,不必一次性全部引入。


三、文件系统自动化:从「混乱」到「秩序」

3.1 智能分类下载文件夹

下载文件夹是最常见的「数字垃圾场」。以下脚本按文件后缀自动归类,且具备幂等性——多次运行不会重复移动已整理的文件。

from pathlib import Path
import shutil

DOWNLOADS = Path.home() / "Downloads"

CATEGORIES = {
    "Images": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"],
    "Documents": [".pdf", ".docx", ".txt", ".xlsx", ".pptx", ".md"],
    "Archives": [".zip", ".rar", ".7z", ".tar.gz", ".tgz"],
    "Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv"],
    "Music": [".mp3", ".wav", ".flac", ".aac"],
    "Executables": [".exe", ".msi", ".dmg", ".pkg"],
}

def organize_downloads():
    """按文件类型自动分类下载文件夹内容"""
    moved_count = 0
    
    for file_path in DOWNLOADS.iterdir():
        if not file_path.is_file():
            continue
            
        # 跳过已分类文件夹内的文件
        if file_path.parent.name in CATEGORIES:
            continue
            
        for folder_name, extensions in CATEGORIES.items():
            if file_path.suffix.lower() in extensions:
                target_dir = DOWNLOADS / folder_name
                target_dir.mkdir(exist_ok=True)
                
                # 处理重名冲突
                destination = target_dir / file_path.name
                counter = 1
                while destination.exists():
                    stem = file_path.stem
                    suffix = file_path.suffix
                    destination = target_dir / f"{stem}_{counter}{suffix}"
                    counter += 1
                
                shutil.move(str(file_path), str(destination))
                moved_count += 1
                print(f"✅ 已移动: {file_path.name} -> {folder_name}/")
                break
    
    print(f"\n整理完成,共处理 {moved_count} 个文件。")

if __name__ == "__main__":
    organize_downloads()

设计亮点:

  • 使用 pathlib 而非 os.path,代码更具可读性;
  • 自动处理重名文件(追加 _1, _2 后缀);
  • 跳过已分类目录,避免误操作。

3.2 批量重命名与格式转换

import os
from pathlib import Path

def batch_rename(directory: str, pattern: str, replacement: str, dry_run: bool = True):
    """
    批量重命名文件
    :param dry_run: 为 True 时只预览,不实际执行
    """
    target_dir = Path(directory)
    renamed = 0
    
    for file_path in target_dir.iterdir():
        if file_path.is_file() and pattern in file_path.name:
            new_name = file_path.name.replace(pattern, replacement)
            new_path = target_dir / new_name
            
            if dry_run:
                print(f"[预览] {file_path.name} -> {new_name}")
            else:
                file_path.rename(new_path)
                renamed += 1
                print(f"[已重命名] {file_path.name} -> {new_name}")
    
    if not dry_run:
        print(f"\n共重命名 {renamed} 个文件")

# 使用示例:先预览,确认无误后执行
# batch_rename("/path/to/files", "2025_", "2026_", dry_run=True)
# batch_rename("/path/to/files", "2025_", "2026_", dry_run=False)

⚠️ 避坑指南:批量重命名前务必开启 dry_run 模式预览。一旦误操作,恢复成本极高。


四、办公文档自动化:解放复制粘贴

4.1 零代码模板生成 Excel 报表

openpyxl 是处理 .xlsx 文件的最佳选择。以下示例展示如何生成带样式、公式和自动列宽的报表。

import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter

def create_formatted_report(data: list[list], output_path: str, title: str = "月度报表"):
    wb = openpyxl.Workbook()
    ws = wb.active
    ws.title = title
    
    # 定义样式
    header_font = Font(bold=True, color="FFFFFF", size=12)
    header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
    header_align = Alignment(horizontal="center", vertical="center")
    
    thin_border = Border(
        left=Side(style='thin'),
        right=Side(style='thin'),
        top=Side(style='thin'),
        bottom=Side(style='thin')
    )
    
    # 写入表头
    headers = ["日期", "项目", "收入", "支出", "净利润", "备注"]
    ws.append(headers)
    
    # 表头样式
    for col_num, header in enumerate(headers, 1):
        cell = ws.cell(row=1, column=col_num)
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = header_align
        cell.border = thin_border
    
    # 写入数据
    for row_data in data:
        ws.append(row_data)
    
    # 添加公式行(净利润列自动计算)
    last_row = len(data) + 1
    for row in range(2, last_row + 1):
        income_cell = ws.cell(row=row, column=3)
        expense_cell = ws.cell(row=row, column=4)
        profit_cell = ws.cell(row=row, column=5)
        profit_cell.value = f"={income_cell.coordinate}-{expense_cell.coordinate}"
        profit_cell.number_format = '#,##0.00'
    
    # 自动调整列宽
    for column in ws.columns:
        max_length = 0
        column_letter = get_column_letter(column[0].column)
        for cell in column:
            try:
                if cell.value:
                    max_length = max(max_length, len(str(cell.value)))
            except:
                pass
        adjusted_width = min(max_length + 2, 50)
        ws.column_dimensions[column_letter].width = adjusted_width
    
    # 冻结首行
    ws.freeze_panes = "A2"
    
    wb.save(output_path)
    print(f"📊 报表已生成: {output_path}")

# 示例数据
sample_data = [
    ["2026-06-01", "项目A", 15000, 5000, "", "正常推进"],
    ["2026-06-02", "项目B", 28000, 12000, "", "客户追加需求"],
    ["2026-06-03", "内部工具", 0, 3000, "", "基础设施投入"],
]

# create_formatted_report(sample_data, "monthly_report.xlsx")

4.2 基于模板的 Word 文档生成

对于合同、邀请函等格式固定、内容变化的文档,使用模板替换是最优雅的方案。

from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH

def generate_from_template(template_path: str, output_path: str, replacements: dict):
    """
    基于模板生成 Word 文档
    :param replacements: 占位符 -> 实际值的映射,如 {"{{COMPANY}}": "某某科技"}
    """
    doc = Document(template_path)
    
    # 替换段落中的占位符
    for paragraph in doc.paragraphs:
        for placeholder, value in replacements.items():
            if placeholder in paragraph.text:
                # 保留原有样式的情况下替换文本
                paragraph.text = paragraph.text.replace(placeholder, value)
                # 重新应用字体样式(因为 text 替换会重置 runs)
                for run in paragraph.runs:
                    run.font.name = '微软雅黑'
                    run.font.size = Pt(10.5)
    
    # 替换表格中的占位符
    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for placeholder, value in replacements.items():
                    if placeholder in cell.text:
                        cell.text = cell.text.replace(placeholder, value)
    
    doc.save(output_path)
    print(f"📄 文档已生成: {output_path}")

# 使用示例
# replacements = {
#     "{{COMPANY}}": "未来科技有限公司",
#     "{{DATE}}": "2026年6月8日",
#     "{{AMOUNT}}": "¥50,000.00",
#     "{{SIGNATURE}}": "___________"
# }
# generate_from_template("contract_template.docx", "contract_20260608.docx", replacements)

📌 模板制作建议:在 Word 中预先设计好排版,用 {{VARIABLE}} 作为占位符,直观且不易与正常文本混淆。


五、网络自动化:爬虫、监控与通知

5.1 轻量级网页数据抓取

不需要浏览器驱动的场景,用 requests + BeautifulSoup 效率更高。

import requests
from bs4 import BeautifulSoup
import time

class PriceMonitor:
    def __init__(self, target_price: float, webhook_url: str = None):
        self.target_price = target_price
        self.webhook_url = webhook_url
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        }
        self.session = requests.Session()
    
    def check_price(self, url: str, selector: str) -> dict:
        """检查商品价格并返回状态"""
        try:
            response = self.session.get(url, headers=self.headers, timeout=10)
            response.raise_for_status()
            
            soup = BeautifulSoup(response.text, "lxml")
            price_element = soup.select_one(selector)
            
            if not price_element:
                return {"status": "error", "message": "未找到价格元素"}
            
            # 清洗价格文本
            price_text = price_element.text.strip()
            price = float(''.join(filter(lambda x: x.isdigit() or x == '.', price_text)))
            
            result = {
                "status": "success",
                "current_price": price,
                "target_met": price <= self.target_price,
                "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
            }
            
            if result["target_met"]:
                print(f"🚨 价格达标!当前: ¥{price}, 目标: ¥{self.target_price}")
                # 可在此接入邮件/钉钉/企业微信通知
            else:
                print(f"ℹ️ 当前价格: ¥{price}, 继续监控...")
            
            return result
            
        except requests.RequestException as e:
            return {"status": "error", "message": str(e)}

# 使用示例
# monitor = PriceMonitor(target_price=2999.0)
# result = monitor.check_price("https://example.com/product", ".price-current")

5.2 浏览器自动化:处理复杂交互

当网页需要登录、JavaScript 渲染或复杂交互时,SeleniumPlaywright 是更好的选择。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

def auto_login_and_scrape(url: str, username: str, password: str, chrome_driver_path: str = None):
    """
    自动登录并抓取需要认证后才能查看的数据
    """
    options = Options()
    # 生产环境建议开启无头模式
    # options.add_argument("--headless")
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1920,1080")
    options.add_argument("--no-sandbox")
    
    # 防止被检测为自动化工具
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option('useAutomationExtension', False)
    
    if chrome_driver_path:
        service = Service(chrome_driver_path)
        driver = webdriver.Chrome(service=service, options=options)
    else:
        driver = webdriver.Chrome(options=options)
    
    try:
        driver.get(url)
        
        # 显式等待:等待元素可交互
        wait = WebDriverWait(driver, 10)
        
        username_input = wait.until(EC.presence_of_element_located((By.ID, "username")))
        password_input = driver.find_element(By.ID, "password")
        login_button = driver.find_element(By.ID, "login-btn")
        
        username_input.send_keys(username)
        password_input.send_keys(password)
        login_button.click()
        
        # 等待登录后的某个标志性元素出现
        dashboard = wait.until(EC.presence_of_element_located((By.CLASS_NAME, "dashboard-data")))
        
        # 抓取数据
        data = {
            "dashboard_text": dashboard.text,
            "cookies": {c['name']: c['value'] for c in driver.get_cookies()}
        }
        
        print(f"✅ 登录成功,获取数据: {data['dashboard_text'][:100]}...")
        return data
        
    except Exception as e:
        print(f"❌ 操作失败: {e}")
        # 失败时截图留存
        driver.save_screenshot("error_screenshot.png")
        raise
    finally:
        driver.quit()

🔐 安全提醒:切勿在代码中硬编码密码。建议使用环境变量或密钥管理服务:

import os
password = os.environ.get("PORTAL_PASSWORD")

六、系统级自动化:让脚本自己跑起来

6.1 定时任务调度(跨平台)

schedule 库非常适合个人本地的定时任务,语法直观,无需学习 Crontab 语法。

import schedule
import time
import signal
import sys

class AutomationScheduler:
    def __init__(self):
        self.jobs = []
        self.running = True
        
        # 优雅退出
        signal.signal(signal.SIGINT, self._signal_handler)
        signal.signal(signal.SIGTERM, self._signal_handler)
    
    def _signal_handler(self, signum, frame):
        print("\n🛑 收到终止信号,正在安全退出...")
        self.running = False
    
    def add_daily_job(self, job_func, time_str: str, *args, **kwargs):
        """添加每日定时任务"""
        job = schedule.every().day.at(time_str).do(job_func, *args, **kwargs)
        self.jobs.append(job)
        print(f"⏰ 已添加每日任务: {job_func.__name__} @ {time_str}")
        return job
    
    def add_interval_job(self, job_func, minutes: int, *args, **kwargs):
        """添加间隔任务"""
        job = schedule.every(minutes).minutes.do(job_func, *args, **kwargs)
        self.jobs.append(job)
        print(f"⏰ 已添加间隔任务: {job_func.__name__}{minutes} 分钟")
        return job
    
    def run(self):
        """启动调度循环"""
        print("🚀 自动化调度器已启动,按 Ctrl+C 停止")
        while self.running:
            schedule.run_pending()
            time.sleep(1)  # 每秒检查一次,避免 CPU 空转

# 示例任务函数
def daily_backup():
    print(f"[{time.strftime('%H:%M:%S')}] 执行数据备份...")

def health_check():
    print(f"[{time.strftime('%H:%M:%S')}] 执行健康检查...")

# 启动示例
# scheduler = AutomationScheduler()
# scheduler.add_daily_job(daily_backup, "02:00")
# scheduler.add_interval_job(health_check, 30)
# scheduler.run()

6.2 跨平台部署为系统服务

Linux (systemd)
创建 ~/.config/systemd/user/automation.service

[Unit]
Description=Personal Python Automation
After=network.target

[Service]
Type=simple
ExecStart=/home/yourname/automation-env/bin/python /home/yourname/automation/main.py
Restart=always
RestartSec=10

[Install]
WantedBy=default.target

启用命令:

systemctl --user enable automation.service
systemctl --user start automation.service

Windows (任务计划程序)
使用 PowerShell 快速创建:

$action = New-ScheduledTaskAction -Execute "python.exe" -Argument "C:\scripts\main.py"
$trigger = New-ScheduledTaskTrigger -Daily -At 9am
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "PythonAutomation" -Description "Daily automation tasks"

macOS (launchd)
创建 ~/Library/LaunchAgents/com.user.automation.plist,使用 launchctl load 加载。


七、完整实战:每日自动日报系统

将上述能力组合,构建一个完整的自动化工作流:

import os
from datetime import datetime, timedelta
from pathlib import Path

class DailyReportBot:
    def __init__(self):
        self.report_dir = Path.home() / "Automation" / "Reports"
        self.report_dir.mkdir(parents=True, exist_ok=True)
        
        # 从环境变量读取配置
        self.email_sender = os.environ.get("EMAIL_USER")
        self.email_password = os.environ.get("EMAIL_PASS")
        self.manager_email = os.environ.get("MANAGER_EMAIL")
    
    def collect_data(self) -> list:
        """模拟数据收集,实际可对接数据库/API"""
        yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
        return [
            [yesterday, "核心系统", 32000, 15000, "", "稳定运行"],
            [yesterday, "客户端App", 18000, 8000, "", "发布v2.1"],
            [yesterday, "数据平台", 0, 5000, "", "集群扩容"],
        ]
    
    def generate_report(self) -> str:
        """生成日报文件"""
        today = datetime.now().strftime("%Y%m%d")
        filename = self.report_dir / f"daily_report_{today}.xlsx"
        
        data = self.collect_data()
        create_formatted_report(data, str(filename), title=f"日报 {today}")
        
        return str(filename)
    
    def send_notification(self, attachment: str):
        """发送邮件通知(此处复用前文 send_email 函数)"""
        if not all([self.email_sender, self.email_password, self.manager_email]):
            print("⚠️ 邮件配置不完整,跳过发送")
            return
        
        subject = f"工作日报 {datetime.now().strftime('%Y-%m-%d')}"
        body = """
        领导,您好:
        
        附件为今日工作日报,请查收。
        
        此邮件由自动化系统发送。
        """
        
        # send_email(subject, body, self.manager_email, attachment)  # 复用前文函数
        print(f"📧 模拟发送邮件至 {self.manager_email}")
    
    def cleanup_old_reports(self, keep_days: int = 30):
        """清理过期报告"""
        cutoff = datetime.now() - timedelta(days=keep_days)
        deleted = 0
        
        for file in self.report_dir.glob("daily_report_*.xlsx"):
            file_time = datetime.fromtimestamp(file.stat().st_mtime)
            if file_time < cutoff:
                file.unlink()
                deleted += 1
        
        print(f"🧹 清理完成,删除 {deleted} 个过期文件")

    def run(self):
        """执行完整流程"""
        print(f"🤖 日报机器人启动 @ {datetime.now()}")
        
        try:
            # 1. 整理环境
            self.cleanup_old_reports()
            
            # 2. 生成报表
            report_path = self.generate_report()
            
            # 3. 发送通知
            self.send_notification(report_path)
            
            print("✅ 日报流程全部完成")
            
        except Exception as e:
            print(f"❌ 流程异常: {e}")
            # 此处可接入错误告警(如钉钉机器人)

# 定时调度入口
def daily_job():
    bot = DailyReportBot()
    bot.run()

# 生产环境使用
# scheduler = AutomationScheduler()
# scheduler.add_daily_job(daily_job, "18:00")
# scheduler.run()

八、踩坑与最佳实践

问题 解决方案
脚本在后台运行一段时间后崩溃 添加 try/except 捕获异常,配合 logging 记录错误,而非直接 print
定时任务时间漂移 使用 schedulerun_pending() 配合短间隔 sleep,或改用系统级 Cron/systemd
Selenium 被反爬检测 开启 --headless,使用 undetected-chromedriver 库,或模拟真实鼠标轨迹
邮件发送被服务商拦截 使用应用专用密码(App Password),开启 SMTP 服务,避免高频发送
路径硬编码导致跨平台失败 统一使用 pathlib.Path,避免字符串拼接路径
敏感信息泄露 绝对不要提交密码到 Git,使用 .env 文件 + python-dotenv

九、进阶方向

当你掌握了基础自动化后,可以探索以下方向:

  1. GUI 自动化:使用 PyAutoGUILackey 处理无法通过 API 操作的老旧系统;
  2. RPA 增强:结合 Playwright 进行无头浏览器自动化,比 Selenium 更稳定;
  3. 消息推送:集成企业微信/钉钉/Slack Webhook,实现即时通知;
  4. 云部署:将脚本部署到云服务器或树莓派,实现 7×24 小时运行;
  5. 低代码平台:使用 PrefectAirflow 管理复杂的工作流依赖。

十、结语

自动化不是一蹴而就的,而是持续迭代的过程。建议从「最让你烦恼的一个重复任务」入手,先写一个 20 行的小脚本,再逐步扩展。

记住两个原则:

  1. 先跑起来,再优化:不要追求一次性完美,先解决有无问题;
  2. 可观测性优先:任何自动化脚本都必须有日志和异常通知,否则你只会知道它「什么时候坏了」,而不知道「为什么坏」。

希望本文能成为你个人自动化之旅的起点。如果你实现了有趣的自动化场景,欢迎在评论区分享。


参考链接:

本文代码在 Python 3.11+ 环境测试通过,最后更新于 2026 年 6 月。

更多推荐