寫文件一直是一件很煩人的事,尤其是寫一些 API 文件。由於這類的文件的格式很固定,但又不得不寫,又常常要改來改去,弄起來總是很煩心。所以之前就想要透過自動化的方式,來產生word文件。而最近研究 AI, 感覺自動產生文件可以搭配的上,就先做一個技術儲備了。
Python 產生 Word 文件
Python 要產生一 word 的 docx 文件,可以透過 python-docx 套件來達成,安裝的的方式就以pip進行。
1 |
pip install python-docx |
python-docx 可以開啟一個全新的文件,或者開啟現有的文件來做為一個範本。建議就選隨便試試看,最後再套用自己的範本。
Python-docx
Python-docs 支援了加入 head, paragraph, picture 等等的功能,這樣就能構建標題、內文、圖片。表格和圖片的部份,也可以加入標號以在圖表目錄中顯示。
為了簡化 python-docx 的使用,我將其功能分離出來成幾個 add_heading, add_paragraph, add_list_bullet 在另一個檔案,這樣在使用上就可以看起來比較精簡。
下載專案
用下面命令來下載試範專案
1 |
git clone https://gitlab.com/eagleein578/python-docx.git |
產生word檔案
1 2 3 |
cd python-docx pip install python-docx python3 gen_word.py |
產生的結果
打開 demo2.docx, 可以看到如下的內容
程式碼
主體
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
from docx import Document from docx.shared import Inches from docx import Document from docx.oxml import OxmlElement from docx.shared import Pt from docx.oxml.ns import qn from gen_wordtool import add_heading, add_paragraph, add_list_bullet from gen_wordtool import add_list_number, add_caption, add_pic from gen_wordtool import add_separate, add_table, set_table_border import gen_wordtool gen_wordtool.doc = Document("demo.docx") add_heading("開頭", 1) p = add_paragraph("這是一篇測試文章, ") p.add_run("中間也可以加粗體").bold = True add_heading('第一章', 2) p=add_paragraph('下面是一個列表') add_list_bullet("蘋果") add_list_bullet("香蕉") add_list_bullet("芭樂") add_separate() add_paragraph("下面是其編號") add_list_number("Apple") add_list_number("Banana") add_list_number("Fruit") add_separate() p = add_pic("img/pic.jpg") add_caption(p, "自動圖片") add_separate() records = ( (3, '101', 'Spam'), (7, '422', 'Eggs'), (4, '631', 'Spam, spam, eggs, and spam') ) table = add_table(rows=1, cols=3) table.style = "Light Shading Accent 1" hdr_cells = table.rows[0].cells hdr_cells[0].text = 'Qty' hdr_cells[1].text = 'Id' hdr_cells[2].text = 'Desc' for qty, id, desc in records: row_cells = table.add_row().cells row_cells[0].text = str(qty) row_cells[1].text = id row_cells[2].text = desc set_table_border(table) add_caption(table, "自動表格") gen_wordtool.doc.add_page_break() gen_wordtool.doc.save('demo2.docx') |
主體是利用 gen_wordtool.py 所提供的 function 進行文件的建構,原則上沒什麼特別,只是比較精簡一點。
L1~L5: import docx 的功能
L6~9: import 自己撰寫在 gen_wordtool.py 的功能
L11: 以 demo.docx 為範本來進行修改
L13~15: 新增標題與內文,內文有粗體
L17~22: 新增 List 列表
L24~27: 新增數字列表
L31~32: 新增圖片以及其 caption
L42~L55: 新增表格,設定其樣式為 “Light Shading Accent 1”, 並加邊框。在官網的文件裡,有提到可用的 Style 。
L56: 為表格加入標號
L57~59: 加入分頁號及存檔
gen_wordtool.py
gen_wordtool.py 是將 python-docx 的 API 再組和一下成為語法較簡易的用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
from docx import Document from docx.shared import Inches from docx import Document from docx.oxml import OxmlElement from docx.shared import Pt from docx.oxml.ns import qn import docx doc="" def add_separate(): doc.add_paragraph('') doc.add_paragraph('') def add_heading(desc, lvl): doc.add_heading(desc, level=lvl) def add_paragraph(desc): p = doc.add_paragraph(desc) return p def add_list_bullet(desc): p = doc.add_paragraph(desc, style='List Bullet') return p def add_list_number(desc): p = doc.add_paragraph(desc, style='List Number') return p def add_caption(obj, desc): """ Based on: https://github.com/python-openxml/python-docx/issues/359 """ if type(obj) == docx.table.Table: target = '表格' else: target = '圖' # caption type paragraph = doc.add_paragraph(f'{target} ', style='Caption') # numbering field run = paragraph.add_run() fldChar = docx.oxml.OxmlElement('w:fldChar') fldChar.set(docx.oxml.ns.qn('w:fldCharType'), 'begin') run._r.append(fldChar) instrText = docx.oxml.OxmlElement('w:instrText') instrText.text = f' STYLEREF 1 \s ' run._r.append(instrText) fldChar = docx.oxml.OxmlElement('w:fldChar') fldChar.set(docx.oxml.ns.qn('w:fldCharType'), 'end') run._r.append(fldChar) # add dash between chapter and seq paragraph.add_run(f'-') # numbering field run = paragraph.add_run() fldChar = docx.oxml.OxmlElement('w:fldChar') fldChar.set(docx.oxml.ns.qn('w:fldCharType'), 'begin') run._r.append(fldChar) instrText = docx.oxml.OxmlElement('w:instrText') instrText.text = f'SEQ {target} \\* ARABIC \s 1' run._r.append(instrText) fldChar = docx.oxml.OxmlElement('w:fldChar') fldChar.set(docx.oxml.ns.qn('w:fldCharType'), 'end') run._r.append(fldChar) # caption text paragraph.add_run(f' {desc}') def add_pic(file): p = doc.add_picture(file, width=Inches(1.25)) return p def add_table(rows, cols): tbl = doc.add_table(rows=rows, cols=cols) return tbl def set_cell_border(cell, **kwargs): """ Set cell's border Usage: set_cell_border( cell, top={"sz": 12, "val": "single", "color": "#FF0000"}, bottom={"sz": 12, "color": "#00FF00", "space": "0"} ) """ tc = cell._tc tcPr = tc.get_or_add_tcPr() # Border codes borders = { "top": "top", "end": "right", "bottom": "bottom", "start": "left" } for key, value in kwargs.items(): border_name = borders[key] border_elm = OxmlElement('w:' + border_name) # Check for each attribute in the border element for k, v in value.items(): if k == "sz": sz = OxmlElement('w:sz') sz.set(qn('w:val'), str(v)) border_elm.append(sz) elif k == "val": val = OxmlElement('w:val') val.set(qn('w:val'), v) border_elm.append(val) elif k == "color": color = OxmlElement('w:color') color.set(qn('w:val'), v) border_elm.append(color) elif k == "space": space = OxmlElement('w:space') space.set(qn('w:val'), v) border_elm.append(space) tcPr.append(border_elm) def set_table_border(table): for row in table.rows: for cell in row.cells: set_cell_border( cell, top={"sz": 24, "val": "single", "color": "#FF0000"}, # 12pt, single black border bottom={"sz": 24, "val": "single", "color": "#000000"}, start={"sz": 24, "val": "single", "color": "#000000"}, end={"sz": 24, "val": "single", "color": "#000000"} ) |
其中有幾個比較複雜的 function.
- add_caption(): 將圖或表加入標號,使得其可以在圖表目錄中顯示。其中有用到功能變數 STYLEREF, SEQ, ARABIC,這些其實較難瞭解。最快的方法,還是從已經建立的文件去觀察其功能變數為何。在 Word 中按下 ALT+F9 就可以切換
- set_table_border(): 幫表格加邊框。
原始表格標記
切換功能變數後所顯示的標記
結語
Word 雖然算不上是好的格式,但非常通用。好在其也遵循了一定的標準,可以讓我們用python進行一定的修改。為將來的自動化產生,奠定了一定的基礎。