์๋
ํ์ธ์, ๋ฆฌ์น๋ก๋ ํ๋ก์ ํธ์์ ๋ฐ์ดํฐ ์์ง ํ์ดํ๋ผ์ธ ์ค๊ณ๋ฅผ ์ ๋ดํ ๋ฐฑ์๋ ๊ฐ๋ฐ์ L์
๋๋ค. ์ ํฌ ์๋น์ค์ ๊ฐ์ฅ ํฐ ์๋ถ์ฌ์ด์ ํต์ฌ ๊ธฐ์ ์ ๋ฐ๋ก ๋ํ๋ฏผ๊ตญ ํ์ ์์ ๋ถ ์ ์๊ด๋ณด์ ๋น์ ํ PDF ๋ฌธ์์์ ๊ณต์ง์ ์ฌ์ฐ ๋ด์ญ ํ ๋ฐ์ดํฐ๋ฅผ ํ ์น์ ์ค์ฐจ๋ ์์ด ๋ฏ์ด๋ด๋ ์ถ์ถ ์์ง์
๋๋ค. ์ด ๊ธ์์๋ ์ ํฌ ๊ฐ๋ฐํ์ด ํ์ด์ฌ์ pdfplumber ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฌด๊ธฐ ์ผ์ ์ ๋ถ ๊ด๋ณด๋ผ๋ ๊ฑฐ๋ํ ๋๊ด์ ์ด๋ป๊ฒ ํค์ณ ๋๊ฐ๋์ง, ์์ํ ์ฝ๋ฉ ์ฝ์ง๊ธฐ์ ํด๊ฒฐ ์ฝ๋๋ฅผ ์๋์์ด ๊ณต์ ํ๊ณ ์ ํฉ๋๋ค.
1. ์ ์ ๋ถ ๊ด๋ณด PDF๋ ๊ฐ๋ฐ์์๊ฒ ์ฌ์์ธ๊ฐ?
์ฒ์ ๊ณต๊ณต ๋ฐ์ดํฐ ํ์ฑ์ ๊ณํํ์ ๋, ์ ํฌ๋ ๋จ์ํ๊ฒ ์๊ฐํ์ต๋๋ค. '์ด์ฐจํผ ์ ๋ถ๊ฐ ๋ฐํํ๋ ๊ณต์ ํ ํํ์ ๋ณด๊ณ ์์ธ๋ฐ, PDF ๋ด๋ถ์ XML ํ๊ทธ๋ ๊น๋ํ ํ ์ด๋ธ ๋ํผ๊ฐ ์์์ ธ ์๊ฒ ์ง?' ๊ทธ๋ฌ๋ ๋ค์ด๋ก๋ํ 3์ ๋ง ์ ๊ธฐ ๊ณต์ง์ ์ฌ์ฐ๊ณต๊ฐ ๊ด๋ณด PDF๋ฅผ ์ด์ด๋ณธ ์๊ฐ, ๋จธ๋ฆฌ๊ฐ ์์ฐํด์ก์ต๋๋ค.
- ๊นจ์ง ํ
์คํธ ์คํธ๋ฆผ: ๋์ผ๋ก ๋ณด๊ธฐ์ ์ผ๋ชฉ์์ฐํ ํ ํ์์ด์ง๋ง, PDF ๋ด๋ถ ๋ฐ์ด๋๋ฆฌ๋ฅผ ๋จ์ ์ถ์ถํ๋ฉด ํ์ ์ผ์ชฝ ์ด ๋ด์ฉ๊ณผ ์ค๋ฅธ์ชฝ ์ด ๋ด์ฉ์ด ํ ์ค๋ก ๊ธธ๊ฒ ๋ค์์ฌ
"์์ด๋์ํํธ์ํํธ๋ณธ์ธ๋ณธ์ธ๋ณธ์ธ1,200,000,000"๊ณผ ๊ฐ์ด ์ธ๋ชจ์๋ ํ ์คํธ ๋ญํ ์ด๋ก ์์์ก์ต๋๋ค. - ์ ๋ณํฉ๊ณผ ์ ์์: ๊ด๋ณด์ ์ด๋ค ํ์ด์ง๋ ์ ๊ตฌ๋ถ์ ๋๋ ๊ฐ๋ก/์ธ๋ก ์ ์ด ์ ํ ์์ด ๋จ์ ๊ณต๋ฐฑ(Whitespace)์ผ๋ก๋ง ์ ๋ ฌ๋์ด ์์๊ณ , ์ด๋ค ํ์ด์ง๋ ๋ค๋จ ๋ณํฉ(Span)์ด ๊ธฐ๊ดดํ๊ฒ ์ ์ฉ๋์ด ํ(Row) ๋์ด๊ฐ ๋ฉ๋๋ก ์ด๊ธ๋ ์์์ต๋๋ค.
2. ๊ตฌ์ํฌ์ ํ์ด์ฌ pdfplumber ๋์ ๊ณผ ํ๋
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ ํฌ๋ ํ ์ ์ฉ ์ถ์ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ pdfplumber๋ฅผ ํฌ์
ํ์ต๋๋ค. pdfplumber๋ PDF ๋ฌธ์์ ๊ทธ๋ํฝ ์์(๋ผ์ธ, ๋ ํธ)์ ๊ธ์์ 2D ์ขํ๋ฅผ ์ํ์ ์ผ๋ก ์ญ์ถ์ ํ์ฌ ํ
์ด๋ธ์ ๊ทธ๋ฆฌ๋ ๊ฐ๋ ฅํ ๊ฐ์ ์ ๊ฐ์ง๋๋ค. ํ์ง๋ง ์์ ์ํ์ ์ต์
๋ง์ผ๋ก๋ ๊ด๋ณด์ ๋ณ์ข
ํ๋ค์ ์ํํด๋ด์ง ๋ชปํด, ์ ํฌ๋ ๋ค์๊ณผ ๊ฐ์ ํ์ดํผํ๋ผ๋ฏธํฐ ํ๋์ ๊ฑฐ์ณ ํ์ฑ ์์ง์ ๊ณ ๋ํํ์ต๋๋ค.
# ๋ฆฌ์น๋ก๋ ๊ด๋ณด ํ
์ด๋ธ ์ถ์ถ ํต์ฌ ํ๋ ์ต์
table_settings = {
"vertical_strategy": "lines", # ์ค์ ๊ธฐ์ค ์ธ๋ก ๋ถํ
"horizontal_strategy": "text", # ๊ฐ๋ก ๊ตฌ๋ถ์ ์ด ์๋ ๊ฒฝ์ฐ ๊ธ์ ์์น ์ ๋ ฌ ๊ธฐ์ค ๋ถํ
"snap_tolerance": 3, # ์ด์ง ์ด๊ธ๋ ์ขํ ์ ๋ค์ ๋จ์ผ ์ ์ผ๋ก ์๋ ๋ณํฉ (์ค์ฐจ ํ์ฉ)
"join_tolerance": 3, # ๋์ด์ง ์งง์ ์ ๋ค์ ํ๋์ ์ ์ผ๋ก ๊ฐ์ ์ด์
"edge_min_length": 10, # ๋
ธ์ด์ฆ ์ฑ๊ฒฉ์ ์งง์ ๊ทธ๋ํฝ ์ ๋ฌด์
}
with pdfplumber.open("gwanbo_sample.pdf") as pdf:
for page in pdf.pages:
# ํ๋๋ ์ธํ
์ ๊ฐ๋ํ์ฌ ํ
์ด๋ธ ์ถ์ถ ์๋
tables = page.extract_tables(table_settings)
# ์ถ์ถ ๋ฐ์ดํฐ ๊ฐ๊ณต ๋ก์ง ์งํ...
์ ์ฝ๋์์ ํนํ ์ค์ํ ํํธ๊ฐ horizontal_strategy: "text"์ snap_tolerance์ ์กฐ์ ์ด์์ต๋๋ค. ๊ณต์ง์๋ค์ด ์ฃผ์ ์ ๋ณด๋ฅผ ๊ธธ๊ฒ ์ ์ด ์นธ์ด ์๋๋ก ๋ฐ๋ ค ๋ฐ์ํ ์ค๋ฐ๊ฟ์ ๋จ์ผ ํ์ ํ์ ๋ฐ์ดํฐ๋ก ๋ฌถ์ด๋ด๊ธฐ ์ํด, ๊ธ์์ ์์ง Y์ขํ๊ฐ ์ธ์ ํ ๊ธ์๋ค๊ณผ ๋์ผ ์ ์์ ์๋์ง ๋น๊ต ์ ๋ ฌํ๋ ํด๋ฌ์คํฐ๋ง ์๊ณ ๋ฆฌ์ฆ์ ์น์ด ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ ๋ณต์ํด ๋์ต๋๋ค.
3. ์ง์ ๊ฒฝํํ ์์ธ ์ผ์ด์ค ํธ๋ฌ๋ธ์ํ
๊ฐ๋ฐ ๊ณผ์ ์์ ๊ฐ์ฅ ๊ณจ์น ์ํ ๋ ๋ณต๋ณ์ "๋๋ช
์ด์ธ์ ์ถํ"๊ณผ "์ง์์ฒด ๊ด๋ณด์ ์ด๋ฏธ์ง ์ค์บ๋ณธ ๋ฐฐํฌ"์์ต๋๋ค.
๋์ผํ ๊ด๋ณด์ '๊น๋ฏผ์'๋ผ๋ ๋๋ช
์ด์ธ์ด ๊ตญํ์ ๋ถ์ฐ๊ด์ญ์์ํ์ ๊ฐ๊ฐ ๋ฑ์ฌ๋์ด ์์ ๋, ์ด๋ฆ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ์กฐ์ธ(Join) ๊ณผ์ ์์ ์์ฐ์ด ํฉ์ฐ๋์ด ๋ปฅํ๊ธฐ๋๋ ์ฐธ์ฌ๊ฐ ๋ฒ์ด์ง๊ธฐ๋ ํ์ต๋๋ค. ์ ํฌ๋ ์์ ๊ธฐ๊ด(agency)๊ณผ ๊ณ ์ ์๋ณ ์ฃผ์์ง ํด์๊ฐ์ ๊ฒฐํฉํ ๋ณตํฉ ํ๋ผ์ด๋จธ๋ฆฌ ํค(Composite PK)๋ฅผ ์์ฑํด ๋๋ช
์ด์ธ ํ๋ณ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค. ์ค์บ๋ณธ ์ด๋ฏธ์ง ํ์ด์ง์ ๊ฒฝ์ฐ ํ์ด์ฌ pytesseract ์์ง์ ์ฐ๋ํ์ฌ, ํ
์ด๋ธ ์์ญ๋ง ๋จผ์ ์ด๋ฏธ์ง๋ก ํฌ๋กญ(Crop)ํ ๋ค ๋ถ๋ถ์ OCR ์ฒ๋ฆฌ๋ฅผ ๋๋ ค ํ
์คํธ๋ฅผ ๋ณต์ํ๋ ์ง์ฐ ๋ ๋๋ง ํด๋ฐฑ์ ๊ตฌํํ์ฌ ๋๋ฝ ์๋ 100% ๋ฐ์ดํฐ ํ์ฑ์ ๋ง์นจ๋ด ์์ํด ๋์ต๋๋ค. ์ด ํ๊ฒจ์ ๋ ํธ๋ฌ๋ธ์ํ
๊ธฐ๋ก์ด ๋ฆฌ์น๋ก๋๋ฅผ ๋จ์ AI ์์ฐํ ๋ธ๋ก๊ทธ์ ์ฐจ๋ณํ์ํค๋ ๊ฐ์น ์๋ ์์ฐ์ด๋ผ ๋ฏฟ์ต๋๋ค.