
최근들어 RPA (Robotic Process Automation)에 관심을 가지게 되었다. RPA라는 용어를 몰랐을 뿐이지 나는 나름대로 업무자동화를 이용하고 있었다. Python으로 다량의 엑셀 자료를 이용해서 자료를 만들고 추출하는 작업, 이미지를 핸들링해서 PDF로 출력을 하던 프로그램도 있었다.
최근에는 Python에 대한 업무 자동화에 대한 강좌를 할 수 있겠냐는 문의가 왔다. 그래서 검색을 하다보니 업무자동화가 RPA라는 것을 알았다.
여기의 코드는 강의를 염두에 둔 것은 아니고, 포스팅을 하기에 적합한 코드도 아닌 것 같지만 RPA에 대해서 얘기를 하기 위해서 올려 둔다. 이 코드는 잘게 잘라서 별도의 포스팅으로 올릴 예정이다.
스토리는 이렇다. 강의 준비 요청을 받고 RPA에 대한 강의를 준비 중이었다. 가장 먼저 RPA에서 해야 할 것은 누가 뭐래도 엑셀을 가지고 노는 것이다. 그래서 기본적인 엑셀을 활용하는 RPA 강의 하루 분의 자료 "Day 1"을 만들어 놓고 다음은 chatGPT, WebCrawling을 활용한 강의를 하루 코스로 만들면 어떨까 하는 생각을 하던 중이었다.
집사람이 한 지역의 건축물대장을 열람해서 PDF로 다운로드를 받아서 거기에 있는 건물주의 주소록을 만드는 작업을 하고 있었다. 몇 개 안되면 수작업이 훨씬 빠르기에 그런가보다 하고 있었다. 그런데 말을 들어보니 그 개수가 수백개가 되었다. 나중에 보니 550개 정도가 되었다. PDF를 다운로드 받는데도 해당 지역의 건물을 검색해서 호수를 입력하고 수 차례의 마우스 클릭을 해야 하나의 파일을 다운로드 받을 수 있었다. 다음은 하나하나 PDF를 열어 주소와 이름을 복사해 넣는 작업이었다.
어떤 파일인지 이메일로 공유해 달라고 했다. 분석이 되어야 해주겠다 말겠다고 얘기를 할 수 있으니 말이다. 코드를 짜기 전에 구글에 검색을 해서 다음의 포스팅을 찾았다. 내가 가진 코드를 찾아봐도 되지만 다른 분들의 코드를 활용하기도 한다.
https://kminito.tistory.com/63
[Python] 업무자동화 - PDF 파일 정리하기 (SGS 성적서)
SGS에서 레포트를 받으면 모든 파일명이 SGS의 내부 식별 번호로 되어 있습니다. 저희는 보통 한 분기에 약 200개 이상의 성적서를 받다 보니, 이것들을 시료 이름과 시험 내용에 따라 일일이 분류
kminito.tistory.com
코드를 활용해서 집사람이 준 pdf 파일을 읽어보니 text로 잘 출력이 된다. 가끔은 pdf내에 text로 보이는 것들이 이미지로 들어가 있는 경우가 있어 사전에 확인을 한 것이다. 확인이 되었으니 이제는 점수를 딸 차례다. '자료만 다운로드 받으면 내가 엑셀로 변환해 줄께'라고 했다.
다른 일을 하면서 짬짬이 파일을 다운 받느라 3일은 걸린 듯 했다.
이번 작업은 완벽하기 보다는 시간을 줄이는데 초점을 두었다. PDF 파일 내의 형식이 완전히 똑같다면 완벽하게 만들 수 있을지 모르겠으나 몇 가지 종류가 있는지도 모르는 상태라 코드도 정리되기 보다는 내가 알아보기 쉽게만 만들었다.
생각보다 PDF 파일이 동일한 형식이 아니라 약간씩 달라서 수작업으로 최종 파일을 건드려야만 했다. 엑셀로 출력을 해야 하니 csv 파일로 만들면 바로 엑셀로 읽어들여지는데 확장자를 txt로 한 것은 텍스트 파일에서 수정할 일이 생길 경우를 대비하기 위해서였다.
포스팅 전에 chatGPT에 python code를 주고 분석을 요청했더니 다음과 같이 분석을 해 줬다.
이 코드는 PDF 파일에서 주소 및 건물 정보를 추출하는 파이썬 코드입니다. pdfminer 라이브러리의 여러 모듈을 import 합니다. Province 리스트는 주소 추출에 사용됩니다. extractName 함수는 입력된 문자열에서 숫자 이전까지 문자열을 반환합니다. findProv 함수는 입력된 문자열에서 Province 리스트에 있는 지명을 찾고 그 위치를 반환합니다. firstAdd 함수는 입력된 문자열에서 괄호 안의 문자열을 반환합니다. cutFname 함수는 파일 이름에서 건물명과 호수를 추출합니다. read_pdf 함수는 pdf 파일을 읽고 텍스트를 반환합니다. path 변수는 PDF 파일들이 있는 폴더 경로를 나타냅니다. file_list_py 리스트는 폴더 안의 모든 PDF 파일 이름을 담고 있습니다. 주소 추출은 PDF 파일에서 추출한 텍스트의 특정 위치에서 발생합니다.주소 추출 후 건물 정보와 함께 txt 파일로 저장됩니다. |
from io import StringIO
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfparser import PDFParser
import os
Province = ["서울", "경기", "전라", "경상", "인천", "광주", "부산", "제주", "대전", "대구", "울산", "세종", "충청", "강원" ]
def extractName(str):
l = len(str)
k = 0
for i in range(1, l):
if str[i-1:i].isdigit():
k = i
# print(str[-(l-k):])
return str[-(l-k):]
def findProv(str):
for i in range(len(Province)):
a = str.find(Province[i])
if a > 0:
idx = i
break
index = str.find(Province[i])
return str[:index], str[index:]
def firstAdd(str):
total_len = len(str)
first = 0
second = 0
for i in range(total_len):
if str[i] == ']':
if first == 0:
first = i
else:
second = i
break
ncut = total_len - second - 1
# print(str[-ncut:])
return str[-ncut:]
def cutFname(str):
x = len(str) - 4
str = str[0:x]
l = str.find("-")
return str[-(x-l-1):], str[0:l]
def read_pdf(file_name):
output_string = StringIO()
with open(file_name, 'rb') as f:
parser = PDFParser(f)
doc = PDFDocument(parser)
rsrcmgr = PDFResourceManager()
device = TextConverter(rsrcmgr, output_string, laparams=LAParams())
interpreter = PDFPageInterpreter(rsrcmgr, device)
for page in PDFPage.create_pages(doc):
interpreter.process_page(page)
return output_string.getvalue()
path = 'C:\PROJECTs\pythonProject\pdf'
file_list = os.listdir(path)
file_list_py = [file for file in file_list if file.endswith('.pdf')]
print(file_list_py)
f = open("AddressExtraction.txt", 'w')
f.write(f"번호| 건물명| 호수| 소유주| 우편번호| 소유주 주소| 건물주소\n")
l = len(file_list_py)
for x in range(l):
txt = read_pdf(path + "\\" + file_list_py[x])
txt = txt.replace('\n', '')
txt = txt.replace(' ', '')
# print(x, ">>>", file_list_py[x], ">>>", txt) # print(txt1)
first, second = cutFname(file_list_py[x])
address_loc = txt.find(second) + len(second) + len(first)
address_loc2 = txt.find("지번도로명주소")
address = txt[address_loc:address_loc2]
if len(address) > 100:
address_loc = address.find("도로명주소") + 5
address_loc2 = address.find("화성시장")
address = address[address_loc:address_loc2]
# print(x,f"({second}-{first})", ' ', address)
if txt[0:6] == "■건축물대장":
# print(txt[0:6])
iname = txt.find("-이하여백-")
name = txt[iname-20:iname]
name = extractName(name)
# print(name)
l1 = txt.find("1/1")
if l1 > 0:
l2 = txt.find("******")
addr = txt[l2+6:l1]
aa = addr.find("소유권지분")
if aa > 0:
addr = addr[aa+5:len(addr)]
# print("1", addr)
str = f"{x + 1}| {second}| {first}호| {name}| 우편번호| {addr}| {address}\n"
print(str, end='')
f.write(str)
else:
l1 = txt.find("1/2")
l1 = txt.find("1/2")
# print("L1 ==> ", l1)
if l1 < 0:
l2 = txt.find("******")
l1 = txt.find("공용부분-이하여백-")
addr = txt[l2 + 6:l1]
str = f"{x + 1}| {second}| {first}호| {name}| 우편번호| {addr}| {address}\n"
print(str, end='')
f.write(str)
else:
l2 = txt.find("******")
addr = txt[l2 + 6:l1]
# print("2", addr)
name2, addr = findProv(addr)
if len(name2) > 15:
ll = name2.find("구분층별")
name2 = name2[:ll]
# print(name2, "@", addr)
fndB = addr.find(")")
addr1 = addr[0:fndB+1]
addr2 = addr[-(len(addr) - fndB - 1):]
# print(addr1,"@", addr2)
str = f"{x + 1}| {second}| {first}호| {name}| 우편번호| {addr1}| {address}\n"
print(str, end='')
f.write(str)
str = f"{x + 1}| {second}| {first}호| {name2}| 우편번호| {addr2}| {address}\n"
print(str, end='')
f.write(str)
elif txt[0:3] == "열람용" or txt[0:8] == "고유번호대지위치":
# print(txt[0:3])
# print(x+1, ">>", txt)
txt1 = firstAdd(txt)
loc1 = txt1.find('1/1')
if loc1 > 0:
name, addr = findProv(txt1[0:loc1].replace(',', ' '))
if len(addr) > 100:
aa = addr.find(')')
addr = addr[0:aa+1]
str = f"{x+1}| {second}| {first}호| {name}| 우편번호| {addr}| {address}\n"
print(str, end='')
f.write(str)
else:
# 1/2를 찾아
loc2 = txt1.find('1/2')
# 1/2까지 잘라서 하나 만들어 놓고
name, addr = findProv(txt1[0:loc2].replace(',', ' '))
if len(addr) > 100:
aa = addr.find(')')
addr = addr[0:aa+1]
str = f"{x+1}| {second}| {first}호| {name}| 우편번호| {addr}| {address}\n"
print(str, end='')
f.write(str)
# 앞의 1/2까지 잘라 버린다.
len2 = len(txt1) - loc2 - 3
txt2 = txt1[-len2:]
len2 = len(txt2)
loc3 = txt2.find("소유권이전")
len3 = len2 - loc3 -5
txt3 = txt2[-len3:]
loc2 = txt3.find('1/2')
# 1/2까지 잘라서 하나 만들어 놓고
name, addr = findProv(txt3[0:loc2].replace(',', ' '))
if len(addr) > 100:
aa = addr.find(')')
addr = addr[0:aa+1]
str = f"{x+1}| {second}| {first}호| {name}| 우편번호| {addr}| {address}\n"
print(str, end='')
f.write(str)
else:
print(file_list_py[x])
f.close()
'IT' 카테고리의 다른 글
AI로 이미지 생성하기 - sporky.ai (1) | 2023.03.25 |
---|---|
[Python] 문자열 추출하기 (0) | 2023.03.18 |
#12/12 아두이노 로봇 HowTo (1) | 2022.11.04 |
#11/12 아두이노 로봇 HowTo (2) | 2022.11.04 |
#10/12 아두이노 로봇 HowTo (0) | 2022.11.04 |
댓글