본문 바로가기

프로젝트/Project1

Toy Project 1 (6) - HWP XML PARSING하기 2

전의 코드를 확장하여 모든 xml 파일을 돌면서 본문의 표 안에 있는 데이터를 추출한다. 이때 표는 column 방향으로 2칸 혹은 3칸으로 split된 표만 포함한다. 3칸으로 된 표 중에 필요한 데이터는 많이 없기 때문에 직접 몇 개의 xml 파일을 뽑았다. 코드는 아래와 같다.

 

import xml.etree.ElementTree as ET
import os
import csv

mStr0 = [] # 개정 전 약관 텍스트
mStr1 = [] # 개정 후 약관 텍스트

mStr2 = [] # 첫번째 칸 문자열
mStr3 = [] # 두번째 칸 문자열
mStr4 = [] # 세번째 칸 문자열

mFileList1 = []
mFileList2 = []
mFileList3 = [
    '/Users/jason/Test/code/test1/term210.xml',
    '/Users/jason/Test/code/test1/term55.xml',
    '/Users/jason/Test/code/test1/term198.xml',
    '/Users/jason/Test/code/test1/term79.xml',
    '/Users/jason/Test/code/test1/term259.xml',
    '/Users/jason/Test/code/test1/term23.xml',
    '/Users/jason/Test/code/test1/term36.xml',
    '/Users/jason/Test/code/test1/term110.xml',
    '/Users/jason/Test/code/test1/term26.xml',
    '/Users/jason/Test/code/test1/term300.xml',
    '/Users/jason/Test/code/test1/term319.xml',
    '/Users/jason/Test/code/test1/term297.xml',
    '/Users/jason/Test/code/test1/term282.xml',
    '/Users/jason/Test/code/test1/term186.xml',
    '/Users/jason/Test/code/test1/term190.xml',
    '/Users/jason/Test/code/test1/term64.xml',
    '/Users/jason/Test/code/test1/term154.xml',
    '/Users/jason/Test/code/test1/term169.xml',
    '/Users/jason/Test/code/test1/term98.xml'
]

# 개정 전 약관과 개정 후 약관을 분류하여 mStr0과 mStr1에 저장하는 함수
def appendString(order, z, mFile):
    mString = ''
    for r in z.iter('Text'):
        mString = mString + r.text
    
    if order == 0:
        mStr0.append(mString)
    if order == 1:
        mStr1.append(mString)
        mFileList1.append(mFile)
        
    if order == 2:
        mStr2.append(mString)
    if order == 3:
        mStr3.append(mString)
    if order == 4:
        mStr4.append(mString)
        mFileList2.append(mFile)
    
# 해당 파일의 데이터를 추출하여 string list에 append하는 함수
def makeDataList(mFile):
    tree = ET.parse(mFile)
    root = tree.getroot()

    lineseg = [lineseg[0] for lineseg in root[2][0][6]]
    tableBody = []
    
    #2칸으로 나뉜 모든 테이블 노드 tableBody list에 저장하기
    for x in lineseg:
        if x.find('TableControl') != None: #모든 table에 접근
            if x.find('TableControl').find('TableBody').attrib['cols'] == '2': #2개의 그룹으로 나뉜 table
                tableBody.append(x.find('TableControl').find('TableBody'))
    
    for x in tableBody:
        for y in x.findall('TableRow'):
            for z in y.findall('TableCell'):
                if z.attrib['col'] == '0' and z.attrib['colspan'] == '1':
                    if z.attrib['rowspan'] == '1':
                        appendString(0, z, mFile)
                    else: #padding
                        for i in range(int(z.attrib['rowspan'])):
                            appendString(0, z, mFile)

                if z.attrib['col'] == '1' and z.attrib['colspan'] == '1':
                    if z.attrib['rowspan'] == '1':
                        appendString(1, z, mFile)
                    else: #padding
                        for i in range(int(z.attrib['rowspan'])):
                            appendString(1, z, mFile)

    
    


def makeDataList3(mFile):
    tree = ET.parse(mFile)
    root = tree.getroot()

    lineseg = [lineseg[0] for lineseg in root[2][0][6]]
    tableBody = []
    
    # 3칸으로 나뉜 모든 테이블 노드 tableBody list에 저장하기
    for x in lineseg:
        if x.find('TableControl') != None: #모든 table에 접근
            if x.find('TableControl').find('TableBody').attrib['cols'] == '3': #3개의 그룹으로 나뉜 table
                tableBody.append(x.find('TableControl').find('TableBody'))
    
    for x in tableBody:
        for y in x.findall('TableRow'):
            for z in y.findall('TableCell'):
                if z.attrib['col'] == '0' and z.attrib['colspan'] == '1':
                    if z.attrib['rowspan'] == '1':
                        appendString(2, z, mFile)
                    else: #padding
                        for i in range(int(z.attrib['rowspan'])):
                            appendString(2, z, mFile)

                if z.attrib['col'] == '1' and z.attrib['colspan'] == '1':
                    if z.attrib['rowspan'] == '1':
                        appendString(3, z, mFile)
                    else: #padding
                        for i in range(int(z.attrib['rowspan'])):
                            appendString(3, z, mFile)
                
                if z.attrib['col'] == '2' and z.attrib['colspan'] == '1':
                    if z.attrib['rowspan'] == '1':
                        appendString(4, z, mFile)
                    else: #padding
                        for i in range(int(z.attrib['rowspan'])):
                            appendString(4, z, mFile)
    
    

    
    
    
    
    
    
#main    
path = '/Users/jason/Test/code/test1'
res = []
for root, dirs, files in os.walk(path):
    rootpath = os.path.join(os.path.abspath(path), root)
    for file in files:
        filepath = os.path.join(rootpath, file)
        res.append(filepath)

for mFile in res:
    try:
        makeDataList(mFile)
    except:
        continue

for mFile in mFileList3:
    makeDataList3(mFile)
        
for v in [mStr0, mStr1, mStr2, mStr3, mStr4]:
    print(len(v))
    
#2
with open('test.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['번호', '수정 전 약관', '수정 후 약관'])
    for i in range(len(mStr0)):
        writer.writerow([i, mStr0[i], mStr1[i]])

#3
with open('test3.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['번호', '약관1', '약관2', '약관3'])
    for i in range(len(mStr2)):
        writer.writerow([i, mStr2[i], mStr3[i], mStr4[i]])

 

 

2칸으로 된 표와 3칸으로 된 표를 tableBody에 저장하여 table의 attribute 중 col, row, colspan, rowspan을 이용하여 필요한 데이터들을 mStr 리스트에 appending한다. 그리고 저장이 완료되면 csv 파일로 변환하여 저장한다. 결과는 아래와 같다.

 

 

 

데이터 숫자 출력.

 

2 splited data

 

3 splited data

 

이제 여기서 pandas를 사용하여 필요한 데이터는 남기고 필요 없는 데이터는 삭제하는 과정을 진행할 것이다. 3 splited data도 2 splited로 변경해야 한다.

 

 

import numpy as np
import pandas as pd

df1 = pd.read_csv('/Users/jason/Test/code/test.csv').fillna('')
df3 = pd.read_csv('/Users/jason/Test/code/test3.csv').fillna('')

df1 = df1.drop_duplicates(['수정 전 약관', '수정 후 약관'], keep = 'first')
mIndex = df1[ (df1['수정 전 약관'].str.len() < 30) & (df1['수정 후 약관'].str.len() < 30) ].index
df1.drop(mIndex)
df1.to_csv('/Users/jason/Test/code/testlen1.csv', sep=',', encoding='utf-8')

df3 = df3.drop_duplicates(['약관1', '약관2', '약관3'], keep = 'first')
mIndex = df3[ (df3['약관1'].str.len() < 30) & (df3['약관2'].str.len() < 30) & (df3['약관3'].str.len() < 30) ].index
df3.drop(mIndex)
df3.to_csv('/Users/jason/Test/code/testlen3.csv', sep=',', encoding='utf-8')

df1 = pd.read_csv('/Users/jason/Test/code/testlen1.csv').fillna('')
df3 = pd.read_csv('/Users/jason/Test/code/testlen3.csv').fillna('')
mDF = pd.concat([df1, df3])
mDF.to_csv('/Users/jason/Test/code/mTermData.csv', sep=',', encoding='utf-8')

mDF["Term1"] = mDF["Term1"].str.replace('ㅇ','',regex=True)
mDF["Term2"] = mDF["Term2"].str.replace('ㅇ','',regex=True)
mDF["Term1"] = mDF["Term1"].str.replace(r'[^ㄱ-ㅎㅏ-ㅣ가-힣]', repl=r'', regex=True)
mDF["Term2"] = mDF["Term2"].str.replace(r'[^ㄱ-ㅎㅏ-ㅣ가-힣]', repl=r'', regex=True)
mDF.to_csv('/Users/jason/Test/code/mTermData2.csv', sep=',', encoding='utf-8')

 

데이터를 직접 정리하여 병합했다.

 

 

이제 띄어쓰기 검사기를 사용하고 토큰화를 진행할 것이다.