这篇博文里,我将介绍利用Python编程语言自动化一些繁琐机械的工作的一个实际例子。适合有一点编程基础的财务人员参考。
最近工作上有一个需求:有一个业务上的Excel表格,里面有几千行人员数据,并且有所属企业等其他信息列。任务是给表格里每一个企业发送一封信,里面列出下属人员的相关信息并通知整改。
首先我想到Word上有一个功能叫做”邮件“,里面可以做一个邮件模板,然后挖空一些地方(比如抬头、称呼、落款)。Word会根据你提供的数据源表格,自动地填充这些地方,批量生成大量信件。
但是!这个功能很弱,没法合并同一个公司的员工信息。虽然也不是不能通过“域”语法实现,但感觉很麻烦,这种数据处理的事情还是搬出Python吧。
任务可以拆分成三块:
①读取Excel表格里的人员数据
②把每一行人员数据追加到该公司的信函中
③渲染信函,生成美观可读文件(用于电邮寄送,或者打印)。
总体思路是:先读取excel文件,按格式要求做出markdown格式的信函,然后用pandoc转换成html。
这里介绍一下pandoc,pandoc是一个在不同标记文档格式之间进行转换的命令行工具,支持html/pdf/docx/latex/markdown等。我们可以在Python中调用pandoc,来实现格式转换。
直接上代码了,首先用xlrd库读取表格。
1 2 3 4 5 6 import xlrd excel =r'list.xlsx' output_path=os.path.join(sys.path[0 ],"output" ) workbook = xlrd.open_workbook(excel)print (workbook.sheet_names()) sheet1 = workbook.sheet_by_index(0 )
然后抽取每一条数据,生成markdown格式的信函。
1 2 3 4 5 6 7 8 9 10 11 for i in range (1 ,sheet1.nrows-1 ): try : md_company=sheet1.cell(i,2 ).value.strip() md_file='%s.md' %(md_company) md_fullfile=os.path.join(output_path,md_file) print ("准备写入文件:" ,md_fullfile) if os.path.exists(md_fullfile)==False : ... except Exception as e: print (e) continue
由于markdown支持html标签,我们可以逐行这样准备要写入markdown的数据。记得用换行符。
1 2 3 4 contenthead="<br/><br/><center><h2>告知函</h2></center>\n\n" contenthead=contenthead+"尊敬的用户,……\n" contenthead=contenthead+"\n\n姓名 | 个人账号 | 证件号 | 账户状态 | 错误原因\n" contenthead=contenthead+":-: | :-: | :-: | :-: | :-:\n"
然后写入文件。这里用到codecs来指定编码为utf8,避免转换期间的编码错误。
1 2 3 4 5 6 7 8 9 10 if os.path.exists(md_fullfile)==False ... file = codecs.open (md_fullfile, 'a' ,'utf-8' ) file.write(contenthead) writeline(sheet1,i,file) file.close() else : file=codecs.open (md_fullfile,'a' ,'utf-8' ) writeline(sheet1,i,file) file.close()
这里的writeline()函数,就是逐行往信函markdown文件里写员工信息。记得数据要拼接成markdown可识别的表格行格式,用 dash符分隔,像这样。
(我一直觉得自己写代码一点都不objective,像写过程化语句)
1 2 3 4 5 def writeline (sheet,i,file ) file .write (sheet.cell(i, 4 ).value+" | " )#姓名E 列 file .write (sheet.cell(i, 3 ).value+" | " )#账号D 列 file .write (sheet.cell(i, 5 ).value+" | " )#证件号F 列 file .write (sheet.cell(i, 9 ).value+" | " )#账户状态J 列
最后一步,就是在所有数据都追加完毕后,给所有的信函markdown文件追加落款,并顺便转换成html格式。这里用os.walk()来遍历。很简单,网上的代码段一大把。这里可以用div的style标记来做落款的右对齐,并且通过datetime模块取生成时间作为落款时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 def write_prefix (): for root,dirs,files in os.walk(output_path): for fullfiles in files: fullfile = os.path.join(root, fullfiles).lower() extension = os.path.splitext(fullfile)[1 ][1 :] if extension in ["md" ,"txt" ]: file=codecs.open (fullfile,'a' ,'utf-8' ) file.write(" 特此告知!\n\n\n" ) file.write("<div style=\"text-align: right\">XX市XX主管部门 </div>\n" ) file.write("<div style=\"text-align: right\">%s年%s月%s日</div>\n" %\ (datetime.datetime.now().year,datetime.datetime.now().month,datetime.datetime.now().day)) file.close() run_pandoc(fullfile,fullfile.replace(".md" ,".html" ))
上面代码段的最后一行调用了run_pandoc(),就是调用pandoc命令来转换markdown到html。为什么我不转换成docx?因为这玩意要依赖latex库,而且样式支持奇差。假如是用于打印或网络传输的信函,html足够了。
pandoc可以在转换时附带一个css,让转换出来的信件样式更美观。css可以网上挑一个漂亮的。记得加上–self-contained函数,把css内联到html里。
1 2 3 4 def run_pandoc (source_file,object_file ): print ("正在渲染" ,object_file) if ".htm" in object_file: os.system("pandoc --css style.css -s %s -o %s --self-contained --metadata pagetitle=\" \"" %(source_file,object_file))
这样就好了。大概十秒钟左右就能生成五百份信函。性能也说得过去,没必要上异步文件处理库(aiofile之类)。
最后欢呼一下:科技解放生产力。
对这方面知识感兴趣但对编程知之甚少的网友,可以参考一下这本书:《Python编程快速上手:让繁琐工作自动化》