본문 바로가기
업무자동화/파이썬-아래아한글 자동화 기초

한/글 없이 hwpx 파일의 표를 pd.DataFrame으로 추출하는 방법

by 회사원코딩 2022. 2. 2.
반응형

안녕하세요?

일상의 코딩, 일코입니다.

 

hwpx 포맷이 만들어진지 벌써 10년도 훌쩍 넘었습니다.

한컴오피스에서는, 데이터 추출이 번거로운 기존의 hwp 포맷 대신 

machine-readable한 hwpx 포맷을 사용해줄 것을 정부 및 기관에 요청한 바 있었는데요.

 

실제로 hwpx 포맷이 어떻게 쓰이는지 한 번 간단히 알아보고,

한/글 프로그램 없이도

한/글 문서의 표를 판다스 데이터프레임으로 추출하는

간단한 예제를

여러분께 소개하려고 합니다.

 

우선 추출하고자 하는 한/글 문서는 아래와 같습니다.

df.hwpx 문서

예제로 사용할 hwpx문서(표) 안에 들어 있는 데이터는

seaborn으로 데이터 시각화 연습하실 때

한 번쯤은 사용해보셨을 법한,

seaborn_tips_dataset입니다.

 

데이터 출처 : seaborn_tips_dataset | Kaggle

 

seaborn_tips_dataset

 

www.kaggle.com

예제를 간편하게 따라해 보고 싶으신 분들은

아래의 한/글 문서를 다운로드한 후 따라와주시기 바랍니다.

df.hwpx
0.10MB


 

우선 hwpx가 왜 machine-readable 한 건지

한 번 알아보겠습니다.

 

df.hwpx 문서를 다운로드하셨으면

하나 복사하신 후에

확장자인 hwpx를 zip으로 바꾼 후

압축을 해제해 보시기 바랍니다.

hwpx 파일의 정체는 ZIP과 XML !?

압축을 풀면 대략 아래와 같은 구조입니다.

이미지를 제외하면, 모두 메모장으로 읽을 수 있는 포맷입니다.

 

우리가 추출할 텍스트 데이터는

Contents/section0.xml 파일 안에 들어 있습니다.

71번 라인에 첫 번째 셀의 값인 "total_bill"이 보이네요. (리포맷 상태)

 

저도 XML 문서를 파싱하는 게 익숙하지는 않아서

표의 데이터를 추출해서 df로 변환하는 예제만 간단히 보여드리겠습니다.

 


 

① 임포트할 모듈은 딱 다섯 개입니다. os, shutil, xml, zipfile, pandas

 

② hwpx 파일 경로를 파이썬에 입력합니다.

 

③ hwpx 파일의 압축을 해제합니다.

 

④ 생성된 폴더 안의 Contents/Section0.xml 파일을 xml.etree.ElementTree로 열어줍니다.

 

⑤ 문서 안의 표 태그를 가져옵니다. (현재 문서에는 총 1개입니다.)

 

⑥ 표의 열 갯수와 행 갯수 정보를 가져옵니다. (pd.reshape시 활용)

 

⑦ 표의 데이터를 1차원 리스트로 쭉 빼옵니다.

 

⑧ 데이터프레임으로 변환, reshape 후 dtype 설정

 

⑨ (마지막으로) 압축해제된 폴더 삭제

끝.

 

한 번 플롯은 그려보고 마칠까요?

바이올린플롯을 그려보았습니다.

"금요일만 되면 남자들이 크게 쏘는" 씁쓸한 경향을 보이는 플롯이네요.

직접 한 번 해 보시기 바랍니다...

 

위에서 작성한 코드입니다. 참고하시기 바랍니다.

#1
import os
import shutil
import xml.etree.ElementTree as ET
import zipfile

import pandas as pd


#2
hwpx_file = r"C:\Users\smj02\Desktop\df.hwpx"
os.chdir(os.path.dirname(hwpx_file))
path = os.path.join(os.getcwd(), "hwpx")


#3
with zipfile.ZipFile(hwpx_file, 'r') as zf:
    zf.extractall(path=path)


#4
tree = ET.parse(os.path.join(os.getcwd(), "hwpx", "Contents", "section0.xml"))
root = tree.getroot()


#5
tbl_list = []
for child in root.iter():
    if child.tag.endswith("}tbl"):
        tbl_list.append(child)


#6
tbl = tbl_list[0]
tbl_cols = int(tbl.attrib["colCnt"])
tbl_rows = int(tbl.attrib["rowCnt"])


#7
data = []
for i in tbl.iter():
    if i.tag.endswith("}t"):
        data.append(i.text)


#8
df = pd.DataFrame(data)
df = pd.DataFrame(data=df.iloc[tbl_cols:, :].values.reshape(-1, tbl_cols), columns=df.iloc[:tbl_cols, 0].values)
df = df.astype({"total_bill": "float", "tip": "float", "size": "int"})


#9
shutil.rmtree(path)

# 끝.



### 플로팅
df.describe()

import seaborn as sns
import matplotlib.pyplot as plt

plt.style.use('ggplot')
sns.catplot(x="day", y="total_bill", hue="sex", kind="violin", data=df)
반응형

댓글3

  • surin4757 2022.07.27 12:38 신고

    안녕하십니까 평소에 회사원코딩님 게시글 잘보고있는 직장인 입니다!
    다른게 아니라 궁금한게 있어서 좀 여쭤보고 싶어서 댓글 남깁니다ㅠㅠ
    #5번에서 endswith(“}tbl”)되어있는데 한컴 hwpx내 selection0 파일에서는 “}tbl”로 끝나는 텍스트가 없던데 저 구문이 어떤 의미인지, append 하려는데 어떤 형식으로 되어있는지 알수있을까요??ㅠㅠㅠㅠ
    답글

    • 회사원코딩 2022.07.31 00:12 신고

      회신이 늦었습니다ㅜ
      한/글에서 표를 나타내는 XML 태그의 속성 중 하나였던 것으로 기억합니다. 가물가물하네요ㅜ 혹시 작업하시려는 문서에 표가 있는지요?^^; 하도 오래된 작업이라 저도 다시 뒤적거려봐야 할 것 같아요..

  • surin4757 2022.08.02 23:23 신고

    태그를 다시 봐보니 있네요..Xml을 잘 몰라서..ㅎㅎ 블로그글 너무 도움됩니다! 감사합니다!
    답글