自动化脚本实战指南:从任务拆解到Shell、Python与pytest
1. 自动化脚本的本质先别急着写代码先搞清楚要自动化什么自动化与脚本这个话题这几年被炒得越来越热。自动化测试、自动化办公、爬虫脚本、RPA机器人好像谁不沾点自动化就跟不上时代。但你真去搜一圈会发现绝大多数教程都在教具体工具——这个框架怎么装、那个命令怎么敲——很少有人先把自动化到底在解决什么问题讲透。我自己最早接触这块就是因为一个小需求每周五下班前要手动处理几十份日志文件做归档、清理超期文件、生成周报摘要。第一次手动跑完将近一个小时第二次我开始想这种破事能不能让电脑自己干于是第一次认真写了段Shell脚本。写完那一刻确实觉得写代码好厉害但后面做多了才明白真正值钱的不是那几行代码而是**我居然把自己脑子里那套判断逻辑完整拆了出来**。很多人一上来就问学Python还是学Shellpytest怎么用playwright怎么装其实都问错了顺序。自动化脚本真正要过的第一关是把任务拆到机器能理解的程度。机器特别笨你脑子里大概处理一下差不多就行的想法它完全接不住。你必须把流程拆成什么样的输入 → 做怎样的处理 → 输出什么样的结果 → 出错了怎么办。打个比方写脚本就像给一个新来的实习生写一份极其详细的工作说明书。你自己干活可以靠经验临场判断但实习生只能按你写好的步骤执行。这份说明书写得越清晰执行就越稳定写漏一步结果就可能全错。那么什么样的任务适合自动化我总结了三层筛选标准频率高每天、每周要重复做的事。这是自动化的第一价值来源。规则明确现在就说得清楚什么情况怎么处理。规则模糊的任务脚本会变成一场灾难。出错有代价手动操作容易漏、容易错机器跑反而更可靠。比如批量改名、大批量数据校验。反过来有三种情况我劝你别急着自动化一次性的活脚本写完活都干完了需求三天两头变的活改脚本的时间比重做还长完全没有稳定操作路径的活每次处理方式都不一样脚本根本没法固化。真正成熟的自动化负责人心里都有一笔账自动化不是把时间省成零而是把必须人在场的时间压缩成偶尔瞟一眼的时间。脚本跑的时候你可以去开会、去写文档、甚至去睡觉这就是自动化的魅力所在。所以从这篇文章开始我建议你把思路从我要学某个工具切换成我要解决某个具体的、重复的、讲得清楚规则的问题。工具只是手段问题才是起点。2. 工具选型Shell、Python、RPA还是测试框架各管哪一段方向对了之后第二个容易让人纠结的问题就是到底学哪个工具网上的声音太杂有人说Shell是Linux基本功必须学有人说Python天下第一还有人说现在都用RPA不用写代码了。其实这些说法都对但它们各自解决的问题根本不在一个层面。我自己这几年的经验是工具选型不按哪个流行来而是按任务发生的环境层级来分操作的是系统本身、文件、进程 → 选Shell或者PowerShell。操作的是数据、接口、需要复杂逻辑判断 → 选Python。操作的是多个软件界面、跨系统搬数据 → 选RPA工具。验证软件功能是否正确、回归测试 → 选pytest这类自动化测试框架。我用张表格把常见的几个选项捋一下帮你对照着自己手头的任务来选择。工具/方向最擅长的场景上手门槛典型代表不建议的场景Shell脚本文件批处理、定时任务、系统运维低命令基础即可bash、PowerShell复杂数据处理、需要第三方库的活Python脚本数据处理、接口调用、脚本编排中需要一点编程基础requests、pandas大量依赖系统底层命令的活UI自动化框架浏览器操作、APP操作回归测试中高需要理解选择器和等待机制playwright、appium、maestro重数据逻辑的活纯靠UI点太脆RPA工具跨软件桌面自动化、模拟人工操作低可视化编排为主影刀、UIBot高并发、大批量任务效率和稳定不如代码这里举一个我实际遇到的例子你可以直观感受到选型差异。有一次我接了个需求每天早晨把系统里的报表导出转成PDF发给业务群。听了之后我的第一反应是这活至少有三层导出报表系统如果是Web端点鼠标能导出那用playwright录一遍点击流程挺合适如果系统有接口直接调接口拿数据更快。转PDF这是文件格式转换属于数据处理用Python一行库的调用就搞定。发到群这是跨应用操作Python可能需要研究各种协议或者用RPA模拟人工操作。最后我把三件事拆开用了三个工具playwright跑导出、Python做转换、RPA负责发送。每个工具用在自己最顺手的环节整体才稳定。一个工具包打天下的念头趁早放弃。另外针对自动化测试多说一句。很多初学者把写脚本调接口和自动化测试框架混为一谈。实际上如果你只是调一次接口看看返回那用requests写个几十行的脚本就够了但如果你希望长期维护一套用例跑完自动出报告、断言能提示哪条挂了那就需要pytest这种框架来帮你管理用例、组织数据、生成报告。第4章我会用实际例子拆解这个差异。3. Shell脚本实战从手动敲命令到一条命令搞定的完整路径Shell脚本是自动化领域最容易被低估的一环。很多玩Python的人看不起Shell觉得它不算编程语言。但实际上凡是跟文件、进程、系统状态打交道的自动化Shell的效率几乎无可替代。它不需要装任何依赖几乎每一台服务器、每一台Mac、每一套Linux发行版都自带而且执行逻辑非常直白。3.1 一个真实案例日志归档与清理脚本我们从一个我真实做过的任务开始。场景是这样的服务器上某个应用每天产生大量日志文件放在/data/app/logs/目录下按日期命名。我需要做两件事保留最近7天的完整日志把超过7天的日志压缩归档。删除30天以前的归档文件防止磁盘被写满。手动做的话无非就是ls看看文件tar打压缩包rm删旧文件。但每天都手动来一遍纯属浪费时间。写成Shell脚本内容大概是这样的#!/bin/bash # 日志归档与清理脚本 LOG_DIR/data/app/logs ARCHIVE_DIR/data/app/logs/archive KEEP_DAYS7 DELETE_DAYS30 # 创建归档目录如果不存在 mkdir -p $ARCHIVE_DIR # 查找超过7天且未被压缩的日志文件逐个打包 find $LOG_DIR -type f -name *.log -mtime $KEEP_DAYS ! -name *.tar.gz | while read -r file; do filename$(basename $file) tar -czf $ARCHIVE_DIR/${filename}.tar.gz -C $LOG_DIR $filename if [ $? -eq 0 ]; then rm -f $file echo $(date %Y-%m-%d %H:%M:%S) 归档并删除: $filename else echo $(date %Y-%m-%d %H:%M:%S) 归档失败: $filename 2 fi done # 删除30天以前的归档文件 find $ARCHIVE_DIR -type f -name *.tar.gz -mtime $DELETE_DAYS -delete echo 清理完成这段脚本看起来不复杂但里面藏了几个新手最容易踩的坑。第一个坑是路径带空格。如果目录路径里有空格比如/data/My App/logs不带引号的写法直接裂开。所以$LOG_DIR这种引号一定不能省。第二个坑是**while read逐行读取**比for file in $(find ...)更安全——后者遇到文件名带空格时会把一个文件名拆成好几个。第三个坑是判断上一条命令是否成功这里用$?检查tar的返回值只有压缩成功才删原文件避免文件没打成包却被删了这种惨剧。3.2 定时执行crontab让脚本彻底隐身脚本写好只是第一步让它按时自动跑才是自动化的精髓。服务器上最常用的就是crontab。执行crontab -e编辑当前用户的任务表加一行30 2 * * * /usr/local/bin/archive_logs.sh /var/log/archive_logs.log 21这一行代表每天凌晨2点30分执行这个脚本并且把标准输出和错误输出都追加到日志文件里。我觉得加日志这个习惯特别重要。裸跑脚本不记日志等于让一个员工闯了祸还不留记录。脚本执行过程中的echo输出、报错信息全落到日志文件里第二天出任何问题都能查。说到crontab的时间规则简单记就行五个星号分别代表分 时 日 月 周。30 2 * * *就是每天2:300 9 * * 1-5就是工作日早上9点*/10 * * * *就是每10分钟跑一次。真记不住就用在线生成器没必要硬背。3.3 for循环Shell自动化最常用的结构热词里有个shell脚本for循环这确实是Shell里出现频率最高的结构。它的本质就是把一批东西挨个处理一遍。我列举几种最常见的写法# 遍历当前目录下所有txt文件 for file in *.txt; do echo 处理文件: $file done # 遍历数字范围1到10 for i in {1..10}; do echo 第 $i 轮 done # 遍历命令的输出结果 for ip in $(cat ip_list.txt); do ping -c 1 $ip /dev/null 21 echo $ip 通了 || echo $ip 不通 done这里有个重要的教训for里遍历文件时如果目录是空的*.txt这个通配符会原样当成一个文件名传给循环导致它处理一个不存在的字面意义上的*.txt。应对方法是在循环开头加一句if [ ! -f $file ]; then continue; fi。这种边界问题只有真正被坑过才会记得住。Shell脚本练到能熟练处理文件查找 for循环 条件判断 定时执行就已经能覆盖日常一大半的系统自动化需求了。下一步如果发现某个自动化里开始出现复杂的字符串处理、JSON解析、正则替换那就说明该请Python出场了。4. Python pytest从能跑的脚本到能维护的测试项目如果说Shell是自动化的第一级台阶那Python就是那只啥都能干的瑞士军刀。而pytest作为Python生态里最主流的自动化测试框架值得单独开一章来讲——不是因为它是唯一选择而是因为它把写脚本这件事往做工程推了一大步。4.1 为什么用框架而不是写一堆能跑的脚本可能有人会问我直接用Python写个脚本循环调用接口、打印结果不也能自动化测试吗能但那是能跑和能维护的区别。裸脚本最大的问题是所有逻辑全部混在一起接口地址写死在代码里断言失败后脚本继续跑最后在终端输出里翻半天找哪行是错误。当你有5个接口、10个用例时还能忍当你有50个接口、300个用例时这种搞法就是灾难。pytest替我解决了几个核心痛点用例组织一个文件放一类测试一个函数就是一个用例不用自己写复杂的调度逻辑。断言清晰用assert写判断失败时自动展示期望值和实际值一眼定位问题。夹具机制fixture把前置准备和环境清理抽出来复用不用每个用例重复写。报告生成配合pytest-html插件或者Allure跑完直接出可视化报告同事看得懂领导看得懂。选择执行用-k按用例名筛选、用-m按标记分组我可以在1000个用例里只跑冒烟测试。4.2 一个最小可用的接口自动化测试项目举个最典型的场景测试一个用户查询接口。接口地址是https://api.example.com/user/{id}返回JSON格式是{code:0,data:{name:张三,age:18}}。正常逻辑是id存在时code为0且data里有数据id不存在时code为1001且message给出提示id非法时返回400。用pytest实现项目结构可以这样安排test_project/ ├── requirements.txt ├── conftest.py # 放共享fixture ├── test_user_api.py # 用户接口测试用例 └── config.py # 放环境配置config.py里放基础信息BASE_URL https://api.example.com TIMEOUT 10conftest.py里定义一个会话级的session发请求时可以复用连接省得每个用例都重新握手import pytest import requests pytest.fixture(scopesession) def session(): s requests.Session() yield s s.close()test_user_api.py里写具体的用例import pytest def test_get_user_success(session, base_url): 正常查询返回用户信息 resp session.get(f{base_url}/user/1001, timeout10) assert resp.status_code 200 body resp.json() assert body[code] 0 assert body[data][name] ! def test_get_user_not_found(session, base_url): 查询不存在的用户返回业务错误码 resp session.get(f{base_url}/user/999999, timeout10) body resp.json() assert resp.status_code 200 assert body[code] 1001 pytest.mark.parametrize(user_id, [abc, -1, 0, 1.5]) def test_get_user_invalid_id(session, base_url, user_id): 非法id参数校验层应拦截 resp session.get(f{base_url}/user/{user_id}, timeout10) assert resp.status_code 400注意最后一个用例用了pytest.mark.parametrize这是pytest非常好用的一个功能——同一个用例逻辑喂四组不同的参数就变成四个用例。这在接口自动化里应用极广尤其是输入边界值、异常值、类型错误值这些场景用参数化写起来又干净又全面。4.3 跑通之后如何让测试结果真正被人依赖跑通用例只是第一步。我见过很多团队做了自动化测试但没人看结果最后沦为死项目。要让测试结果真正产生价值至少要接上两件事持续集成在代码流水线里加一步提交代码后自动跑pytest。跑挂了就拦住发布用例才开始有牙齿。报告可视化pytest --htmlreport.html就能生成一份不错的HTML报告。在公司环境里可以再搭个简单的服务或者用已有的CI平台展示报告页面。这里分享一个我踩过的坑别上来就追求100%覆盖率和全量用例稳定运行。UI自动化和接口自动化里总有几个用例因为环境波动、第三方依赖不稳定而时不时挂一下。你要做的是先挑出核心链路、最稳定的20条用例让它们保证100%通过且每次都跑然后再慢慢往里加。让团队成员对自动化结果建立它挂就是真有bug的信任比用例数量重要得多。做成十次挂五次的测试最后大家只会选择不看结果项目就废了。5. UI自动化playwright覆盖浏览器RPA接管跨软件桌面流程聊到UI自动化很多人的第一反应是Selenium。但近两年playwright这个后起之秀在浏览器自动化领域的体验比Selenium好太多。安装简单、API设计合理、自带等待机制、还能录脚本回放非常适合快速上手。与此同时RPA工具比如影刀则在跨软件桌面自动化这个方向上不可替代。这一章我把这两条路线放在一起讲帮你分清你该用哪条。5.1 playwright用录制回放打开UI自动化的大门playwright最大的优点之一是它自带一个录制器。启动命令playwright codegen https://example.com它会打开一个浏览器窗口你在里面正常操作页面代码面板会同步实时生成对应的Python代码。操作完之后把代码复制下来改吧改吧第一个UI自动化脚本就成了。比如说我要实现打开登录页、输入账号密码、点击登录、检查是否跳转到首页这个过程。用playwright录制得到代码后再加上断言大概长这样from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://example.com/login) page.fill(#username, test_user) page.fill(#password, 123456) page.click(button[typesubmit]) # 等待跳转并断言 page.wait_for_url(**/home) assert page.title() 首页 browser.close()这里重点说一下等待机制。Selenium时代最烦的就是元素还没加载好就点了这类问题你需要手写各种time.sleep和显式等待。而playwright的很多操作自带自动等待比如click会等元素先出现、可点击、稳定后再执行。这极大提升了脚本的稳定性但注意也不是万能保险涉及复杂异步加载的页面还是建议有意识地用expect来等待关键状态出现。5.2 UI自动化的稳定性问题设置正确看待期望做UI自动化最难的不是写脚本是维护稳定性。选择器随前端改版就失效、页面弹窗突然遮挡按钮、网络慢导致超时……这些问题没经历过的人不会懂。我从失败中总结出几条实践建议给元素加稳定的定位锚点优先用>python --version如果这个也报无法识别说明Python都没装好直接去官网重新装。如果python --version正常进入下一步。第二步找到Python的安装路径。在PowerShell里执行where.exe python它会输出Python可执行文件的完整路径比如C:\Users\你的用户名\AppData\Local\Programs\Python\Python311\python.exe。第三步推断pip所在目录。pip通常就在Python安装目录下的Scripts子目录里。所以在上面的例子中pip应该在这个位置C:\Users\你的用户名\AppData\Local\Programs\Python\Python311\Scripts\pip.exe试着直接运行这个完整路径看看能不能出版本号。能出的话问题就锁定在环境变量PATH里没有加Scripts目录。第四步把Scripts目录加进PATH。在Windows里打开系统设置→关于→高级系统设置→环境变量编辑Path变量把Scripts目录路径追加进去。加完之后务必新开一个PowerShell窗口再试——老窗口不会自动加载新改的环境变量。这几步走下来问题基本解决。之所以这么说是因为这种能运行但找不到命令的错误九成都是PATH的问题。再来看另一个很常见的问题Windows脚本文件双击后一闪而过。你写了一个.bat或.ps1脚本双击执行窗口闪了一下就没了里面内容看到没看到。其实脚本大概率是执行了然后因为控制台窗口的默认行为是执行完毕自动关闭有没有报错你根本来不及看。解决方式有两个。调试时在PowerShell里直接运行脚本而不是双击.\myscript.ps1这样错误信息就会留在当前窗口里。或者在.bat脚本的最后加一行pause窗口会停在请按任意键继续...。这个pause在调试期几乎相当于脚本调试的安全气囊我自己的脚本在开发阶段十有八九都会加它来观察输出。从更大一点的视角看环境配置类的坑几乎都有一个共同特征报错信息里藏着答案但大多数人不读原文。pip报错说是无法识别不是找不到模块这两个问题方向完全不同。我建议新手养成一个习惯把整段报错原文复制下来去搜索引擎里搜而不是只描述我的Python好像出问题了。原样搜索报错是排查效率最高的动作没有之一。7. 几个从踩坑中总结的脚本开发习惯现在分享给你文章写到这自动化与脚本从理念到选型、从Shell到Python、从浏览器到RPA、从环境到排错基本说全了。最后这部分不按理论来纯粹分享几个我打磨了两三年才养成的习惯每一个都是拿加班和线上事故换的。第一个习惯先手动跑通路径再写自动化脚本。不管是Shell、pytest还是playwright第一步永远别是打开编辑器写代码。先去命令行手动执行一遍流程把每一步涉及的命令、参数、输出、可能的异常都记录下来。脚本是手动流程的固化不是凭空想象出来的。我见过太多人上来就写代码结果写出来的脚本每一步都在猜跑起来全是错。手动路径跑通了写脚本只是翻译而已难度骤降。第二个习惯脚本必须有日志并且日志里要有时间戳。我前面说过很多次这里再强调一次。没有日志的自动化脚本出问题的时候你就是盲人摸象。最简单的做法脚本全局给所有关键动作加上echo $(date %Y-%m-%d %H:%M:%S) 做了什么或者Python里用logging模块统一管理。你永远不知道明天会不会需要查这个脚本为什么半夜三点跑挂了。第三个习惯留好最后一道保险。执行删除类、覆盖类、清理类操作的脚本要特别小心。我的做法通常是脚本默认进入演练模式dry-run只打印将要做什么但不真做确认无误后用--execute参数才真正执行。比如Shell里用find ... -delete之前先跑一遍不带-delete的版本看看列出来的是不是真想删的文件。花了十分钟做验证可能帮你省下恢复数据好几天的时间。第四个习惯拥抱少量多次的迭代节奏不要贪心。刚学自动化的人容易犯一个毛病憋大招想一次性把整个流程全自动化。我的建议正好相反——先把流程里最小的一步自动化跑通哪怕只是自动给文件改名跑通了再串下一步。每增加一小截都验证一下没破坏前面已经通的部分。自动化项目死在憋大招上的比死在技术难度上的多得多。既然技术的路已经铺好剩下的就是选一个你手头最烦的重复性任务照着这篇的思路去拆去固化去让它自己跑起来。第一次成功的那个深夜你会觉着以前熬过的那些夜挺值的。