반응형

Python MS WORD  docx 문서 작성하기

 

동일 유형의 업무를 반복하다보면, 완전히 형식이 굳어진 문서들로 내용들이 비슷비슷해질 때가 있습니다. 예를 들어 공문을 작성한다면 거의 공문 제목에 따라 내용은 거의 비슷하고 수신자와 관련된 몇가지 단어만 바꿔주면 될 때가 있습니다.

제 경우, 매일 여러 개의 보고서를 작성하게 되는데 일부 업무의 경우 보고서의 형태가 거의 유사합니다. 이전에는 제대로 마음먹고 온갖 내용을 담아놓은 문서를 서식 보고서로 만들어 놓고, 매번 새 보고서 작성시마다 필요한 부분을 수정, 불필요한 내용을 삭제, 다른 이름으로 저장하는 방식으로 보고서를 작성하였습니다.

그런데 ctrl+c, ctrl+v와 서류를 보며 그대로 한두줄 타이핑을 하는 단순한 작업인데도, 간간히 오타가 나는 바람에 정정 문서를 다시 작성하는 일이 생기곤 했습니다. 도저히 안되겠다 싶어서 이제는 문서를 자동으로 생성시켜주는 프로그램을 만들어 업무시간을 급격하게 줄이고, 오타 실수에서 벗어날 수 있게 되었습니다.

파이썬에는 MS워드 docx파일을 다루는 패키지가 있어서 이를 잘 이용하면 반복되는 업무의 부담을 드라마틱하게 줄여 줄 수 있습니다.

 

패키지 설치

저는 anaconda를 사용하고 있기 때문에 anaconda prompt에서 다음과 같이 입력합니다.

pip install python-docx

주의하셔야 할 것은 docx라는 패키지로 오해할 수 있는데, python 3.X인 경우 python-docx로 설치하여야 합니다.

 

문서 생성하기

from docx import Document

document = Document()
document.save("테스트.docx")

우선 docx에서 Document를 가져옵니다.

Document() 객체를 생성시키고 save() 하는 것으로도 아래와 같은 빈 문서가 생성됩니다.

 

제목 넣기

document.add_heading('이 문서의 타이틀! lvl = 0', level = 0) 
document.add_heading('이 문서의 타이틀! lvl = 1', level = 1) 
document.add_heading('이 문서의 타이틀! lvl = 2', level = 2) 
document.add_heading('이 문서의 타이틀! lvl = 3', level = 3) 
document.add_heading('이 문서의 타이틀! lvl = 4', level = 4) 
document.add_heading('이 문서의 타이틀! lvl = 5', level = 5)

Document() 객체의 add_heading()함수로 문서의 제목을 넣을 수 있습니다. 위의 예제에서 level = 0~5는 워드 스타일에서 확인할 수 있는데, 0~5는 문서 제목 및 제목1~5를 나타내고, 그 결과는 아래 그림과 같습니다.

add_heading으로 제목을 넣었지만, 바로 이어서 보일 문단(paragraph)와 동일합니다. 단지 문단의 스타일이 제목 스타일로 들어간 것입니다.

 

문단 넣기

from docx.shared import RGBColor, Pt

paragraph = document.add_paragraph("문단을 추가합시다. 이게 1번째 문단입니다. \n 줄바꿈이 되는지 해봅니다.")
paragraph = document.add_paragraph("문단을 추가합시다. 이게 2번째 문단입니다. 일부러 변수명을 똑같이 해봤습니다.")

insert_paragraph = paragraph.insert_paragraph_before("3번째로 생성한 문단을 삽입합니다. 2번째 문단 앞에 넣습니다.")

paragraph = document.add_paragraph("문단을 추가합시다. 이게 4번째 문단입니다.\n")
paragraph.add_run(" 4번째 문단에 글을 덧붙여 봅니다.")

paragraph = document.add_paragraph() # 5번째 빈 문단 생성
paragraph.add_run("5번째 문단. 이 문장이 Bold가 될까 해봅니다.\n").bold = True
paragraph.add_run(" 5번째 문단. 이 문장의 글자색 바꿔봅니다. 귀찮아서 안 쓸 것 같습니다.\n").font.color.rgb = RGBColor(255, 0, 0)
run = paragraph.add_run(" 5번째 문단. 이 문장은 Bold 적용, 글씨색 변경, 이텔릭, 글씨체, 글씨크기 변경을 다 적용해봅니다.\n")
run.bold = True
run.font.color.rgb = RGBColor(0, 0, 255)
run.italic = True
run.font.name = "궁서체"
run.font.size = Pt(20)
paragraph.add_run(" 정말 귀찮습니다. 그냥 미리 서식 파일 만들어 놓고 씁시다 (아직 5번째 문단임)"
  • 문단 추가는 Document() 객체의 add_paragraph() 함수를 이용합니다.
  • 문단 내 문자열에서 "\n" 줄바꿈은 워드 작성시 Shift+Enter와 같습니다.
  • 문단 사이에 새로운 문단을 삽입하고 싶다면, Paragraph 객체의 insert_paragraph_before() 함수를 이용합니다.
  • 이미 존재하는 문단에 글을 이어서 쓰고 싶을 때는 Paragraph 객체의 add_run()함수를 이용합니다.
  • add_run()은 Run이란 객체를 반환하는데, Run 객체에는 글씨 스타일을 수정할 수 있는 bold, italic, font 등이 있습니다.

위의 예시의 결과는 아래 그림과 같습니다.

(문제는 제 개인 컴에는 python 3.7.6, python-doc 0.8.10, office2010 조합을 쓰고 있는데, 글씨체 변경이 글씨는 안 바뀌고 띄어쓰기 부분만 글씨체가 변경됩니다. 그냥 서식파일 하나 만들어 놓고 그 파일을 열어서 문서를 만드는 게 낫습니다.)

 

다단계 목록 생성

p = document.add_paragraph('번호 없는 리스트 1단계 1번째', style='ListBullet') 
p = document.add_paragraph('번호 없는 리스트 1단계 2번째', style='ListBullet') 
p = document.add_paragraph('번호 없는 리스트 2단계 1번째', style='ListBullet2') 
p = document.add_paragraph('번호 없는 리스트 2단계 2번째', style='ListBullet2') 
p = document.add_paragraph('번호 없는 리스트 3단계 1번째', style='ListBullet3') 

p = document.add_paragraph('번호 있는 리스트 1단계 1번째', style='ListNumber') 
p = document.add_paragraph('번호 있는 리스트 2단계 1번째', style='ListNumber2') 
p = document.add_paragraph('번호 있는 리스트 3단계 1번째', style='ListNumber3') 
p = document.add_paragraph('번호 있는 리스트 3단계 2번째', style='ListNumber3') 
p = document.add_paragraph('번호 있는 리스트 1단계 2번째', style='ListNumber') 
p = document.add_paragraph('번호 있는 리스트 1단계 3번째', style='ListNumber')

다단계 목록도 결국에는 add_paragraph()로 문단을 추가하면서 style 옵션으로 'ListBullet', 'ListNumber' 를 지정합니다. 스타일에 숫자가 붙는 것은 코드 설명보다 아래 결과가 더 이해가 빠르실 겁니다.

 

표 만들기

table = document.add_table(rows = 2, cols = 3, style = "Table Grid") 

cell = table.cell(0,1) # 0번째 행, 1번째 열의 셀을 지정
cell.text = "0,1"        # 해당 셀에 "0,1"값 대입

table.rows[1].cells[0].text = "1,0" # 1번째 행, 0번째 열에 "1,0"대입

table.columns[2].cells[0].text = "0,2" # 0번째 행, 2번째 열에 "0,2"대입

row = table.add_row()   # 표에 행 추가
row.cells[1].text = "2,1" # 추가된 행의 1번째 열에 "2,1"대입
  • 표를 만드는 것은 Document 객체의 add_table(row=행_갯수, cols=열_갯수)을 이용하면 됩니다.
  • 뒤에 style = "Table Grid"를 붙인 것은 아주 단순하게 표 테두리가 얇은 실선으로 된 스타일입니다.
  • 표 스타일 목록은 https://readthedocs.org/projects/python-docx/downloads/pdf/latest/ 에서 "Table styles in default temple"에서 찾아보실 수 있습니다.
  • 표 셀 안에 값을 넣는 방법의 간단한 예시를 설명하면 다음과 같습니다.
    • Table 객체의 cell(행,열)을 지정하여 cell(행,열).text 값에 문자열을 대입합니다. 여기서 주목할 것은 행과 열의 번호가 0부터 시작입니다.
    • 표 객체의 rows[행].cell[열].text 에 값을 대입할 수 있습니다.
    • 비슷하게 표 객체의 columns[열].cells[행].text 에 값을 대입할 수 있습니다.
  • 행을 추가하는 방법으로 표 객체의 add_row() 함수를 이용하시면 됩니다.

위의 예시 코드의 결과는 다음과 같습니다.

 

그림 넣기

from docx.shared import Inches

document.add_picture('image-filename.png', width=Inches(1.0))

Document() 객체의 add_picture를 쓰면 되긴 한데, 이미지 크기 때문에 Inches를 가져와 씁니다.

matplotlib 등이랑 같이 쓰면 데이터 그래프도 그리고 문서에도 자동으로 넣을 수 있을 듯 한데, 제가 작성하는 보고서에는 그림이 들어가지 않아서 이건 이용해보지 않았습니다.

 

표, 문단 객체

# 문서 내 모든 표의 모든 셀을 출력
for table in document.tables: 
    for row in table.rows: 
        for cell in row.cells: 
            print(cell.text) 
# 문서 내 모든 문단 출력
for p in document.paragraphs: 
    print(p.text)

Document() 객체 내에 있는 표와 문단의 객체는 각각 tables와 paragraphs에 리스트로 존재합니다.

이를 잘 이용하면 기존 작성된 문서에서 특정 내용일 있는지 검색을 하거나, 특정 문구 등을 수정하는 등 다양하게 활용될 수 있습니다.

 

 

간단한 응용 예시

하나하나 코딩하여 문서를 작성하는 것도 좋긴 하지만, 상당히 고달픈 방법일 뿐더러 docx패키지의 기본 스타일이 참 보기 안좋습니다. 가장 좋은 방법은 제대로 된 문서파일을 기본 서식으로 하나 만들어 놓고, 수정할 부분만 찾아서 수정하고 다른 이름으로 저장하는 것을 권합니다.

예를 들어 위와 같은 test_form.docx 라는 문서를 만들어 봤습니다.

 

"의뢰인", "시료명"과 {C001}에 날짜를, 표에는 검사 결과를 입력하고, 다른 이름으로 저장하겠습니다.

from docx import Document 

document = Document("test_form.docx") 


customer = "용왕" # 의뢰인 
sample = "토끼간" # 시료명 
issue_date = "2020.XX.XX." # 날짜 {C001} 

result = [["신선도","90.0 이상",21.1,"썩음"], 
          ["약효","50 이상",1.,"독"], 
          ["냄새","10 이하",99.9,"우웩"]] 


for p in document.paragraphs: 
    if "의뢰인 : " in p.text: # 문단에 "의뢰인 : "이 있으면 customer를 문단 뒤에 붙임
        p.add_run(customer) 
    elif "시료명 : " in p.text: # 문단에 "시료명 : "이 있으면 sample를 문단 뒤에 붙임 
        p.add_run(sample) 
    elif "{C001}" in p.text: # 문단에 "{C001}"이 있으면 날짜로 문자열 변경
        p.text = p.text.replace("{C001}",issue_date) 


table = document.tables[0]  # 표가 1개만 있으니 0번째 표를 대입
n_add_rows = len(result) + 1 - len(table.rows) # 더 만들어야 할 행 개수
for i in range(n_add_rows):  # 행 추가
    table.add_row() 
for i in range(len(result)):  # result 값을 표에 입력
    for j in range(len(result[i])): 
        table.cell(i+1,j).text = str(result[i][j]) if j == 2 else result[i][j]
        # 표에 들어가는 타입은 문자열, 따라서 정수 및 실수는 문자열로 변환

document.save("output.docx") # 다른 이름으로 저장

수정해야할 문단 번호를 알면 바로 그 문단을 수정하는 코드로 만들 수 있지만, 문단 위치가 바뀔 것 같으면 특정 문구가 들어가는 문단을 수정하는 식으로 코드를 만들면 됩니다.

코드로 문단 하나하나 스타일 지정하느니, docx파일 하나 제대로 서식 만들어 놓으면 편할 겁니다.

 

위의 코드를 실행하여 다음과 같은 문서를 만들었습니다.

직인 같은 것이야 미리 이미지로 넣어 놓으면 되는데, 제 컴퓨터에는 어떠한 직인 파일도 없....

 

잘 응용하시면 고통스럽게 반복되는 문서 작업에서 벗어나실 수 있습니다.

반응형

+ Recent posts