저에게 PDF를 엑셀로 변환하는 프로그램을 개발하라는 미션이 주어졌습니다. 우선 기본 알고리즘을 설계해야 했습니다.
- 배경: 두산 에너빌리티, 한국수력원자력공사에서 수신받은 PDF 문서들을 엑셀에 정리해야 하는 작업이 있습니다. 문제는 PDF 하나당 기본적으로 70페이지가 넘는 데다가, 처리해야 하는 PDF 문서들이 많다는 점입니다. 이 작업을 사람이 일일이 하다 보면 최소 3일이 걸릴 정도로 시간이 많이 걸립니다. 이 작업을 프로그램으로 자동화하면 상당한 시간을 아낄 수 있습니다.
- 기본 알고리즘: "PDF 문서의 텍스트를 읽어들여서, 요건번호와 텍스트를 분리한 후 엑셀의 각 위치에 입력하자."
즉, 문자열을 다루는 알고리즘이기 때문에 문자열 처리에 용이한 언어를 선택해야 한다고 생각했습니다. 따라서 개발 언어는 파이썬, 개발 환경은 Visual Studio Code으로 정했습니다.
두 양식 중 두산 에너빌리티 문서가 더 간단해 보여서, 두산 문서를 먼저 해결하기로 결정했습니다. 파이썬에는 PDF를 열어서 텍스트를 추출하는 라이브러리 "fitz", Excel 파일을 생성하고 텍스트를 입력하는 라이브러리 "openpyxl"이 있습니다. 이를 활용하여, 한 페이지의 문서 내용을 "요건 번호 - 텍스트 내용"으로 분류하는 알고리즘 및 코드를 작성했습니다.
다른 인턴님이 새로운 라이브러리 "pdfminer"를 찾아주셔서, 이를 활용한 새로운 코드를 작성했습니다. 기존의 코드는 한 페이지에 대해서만 적용 가능한 코드였던 반면, 새롭게 작성한 코드는 전체 페이지에 적용 가능한 코드였습니다.
<코드1: 내 코드>
import fitz # PyMuPDF
import openpyxl
def pdf_to_excel(pdf_path, excel_path):
# PDF 파일 열기
pdf_document = fitz.open(pdf_path)
# 새 엑셀 파일 생성
workbook = openpyxl.Workbook()
sheet = workbook.active
row = 1 # 두 번째 행부터 시작
# PDF 페이지 순회
for page_num in range(len(pdf_document)):
page = pdf_document.load_page(page_num)
text = page.get_text("text")
# 문장을 줄 단위로 분리
lines = text.split('\n')
# 전처리: 머리말 지우기
del lines[:5]
print(lines)
newlines = []
digits=[]
for i, line in enumerate(lines):
if len(line) > 0 and line[0].isdigit():
digits.append(line)
else:
if lines[i-1][0].isdigit() or i == 0:
newlines.append(line)
else:
newlines[-1] += '\n' + line
print('\n\n'.join(newlines))
# 엑셀 파일에 입력
for digit, line in zip(digits, newlines):
if line.strip(): # 빈 줄이 아니면
sheet.cell(row=row, column=1).value = digit.strip()
sheet.cell(row=row, column=2).value = line.strip()
row += 1
# 엑셀 파일 저장
workbook.save(excel_path)
print(f"PDF 내용을 {excel_path} 파일에 저장했습니다.")
# 함수 실행 예시
pdf_path = r"PDF파일경로.pdf"
excel_path = r"엑셀경로.xlsx"
pdf_to_excel(pdf_path, excel_path)
<코드2: 다른 인턴분 코드>
from pdfminer.high_level import extract_text
from pdfminer.layout import LAParams
import pandas as pd
import re
pdf_file_path = r"C:\Users\SAMSUNG\OneDrive\Desktop\pdftoexcel\PDF참고자료1.pdf"
start_page = 4
end_page = 120
print("start")
print(pdf_file_path)
# PDF 파일에서 텍스트 추출
text = extract_text(pdf_file_path, laparams=LAParams(), page_numbers=range(start_page, end_page))
print(text)
# 추출한 텍스트 출력
my_list = text.split('\n')
del_list = ["DOOSAN", "Z11056-614PS-001C (Rev02)", "Document No.(Revision No.)", "Material Purchase Specification", ""]
my_list = [item for item in my_list if item not in del_list]
my_list = [item for item in my_list if item != ""]
my_list = [item for item in my_list if item[0:4] != "Page"]
str_list = []
num_list = []
tmp = ""
for i in range(len(my_list)):
cur = my_list[i]
if cur[0].isdigit() and len(cur) >= 2 and cur[1] == ".":
if tmp != "":
str_list.append(tmp)
tmp = ""
split_list= my_list[i].split(" ")
num_list.append(split_list[0])
for i in range(1,len(split_list)):
tmp+=split_list[i] + " "
else:
tmp += cur
if tmp != "":
str_list.append(tmp)
# 비정상적인 문자 제거 함수
def remove_illegal_characters(text):
# 허용되지 않는 문자 제거 (예: ASCII 범위 외 문자 및 특정 제어 문자)
return re.sub(r'[^\x20-\x7E]', '', text)
# str_list에서 비정상적인 문자 제거
str_list = [remove_illegal_characters(s) for s in str_list]
# num_list와 str_list의 길이가 동일한지 확인하고, 맞추기
if len(num_list) > len(str_list):
str_list.extend([""] * (len(num_list) - len(str_list)))
elif len(str_list) > len(num_list):
num_list.extend([""] * (len(str_list) - len(num_list)))
for i in range(len(num_list)):
print(num_list[i] + ": " + str_list[i])
# 데이터프레임 생성
df = pd.DataFrame({'No': num_list, 'Document': str_list})
# 엑셀 파일로 저장
excel_file_path = r"C:\Users\SAMSUNG\OneDrive\Desktop\Exceltest1.xlsx"
df.to_excel(excel_file_path, index=False)
print("end")