โ† ๊ฐ€์ด๋“œ ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ
๊ธฐ์ˆ  ๋ฐ ๊ฐœ๋ฐœ

๐Ÿ› ๏ธ ํŒŒ์ด์ฌ pdfplumber๋ฅผ ํ™œ์šฉํ•œ ๋น„์ •ํ˜• PDF ํ‘œ ์ถ”์ถœ ๋ชจํ—˜๊ธฐ

์ž‘์„ฑ์ž: ๋ฆฌ์น˜๋กœ๋“œ ๊ฐœ๋ฐœ์ž L์”จ์ตœ์ข… ์—…๋ฐ์ดํŠธ: 2026๋…„ 6์›”

์•ˆ๋…•ํ•˜์„ธ์š”, ๋ฆฌ์น˜๋กœ๋“œ ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„๋ฅผ ์ „๋‹ดํ•œ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž 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 ์–‘์‚ฐํ˜• ๋ธ”๋กœ๊ทธ์™€ ์ฐจ๋ณ„ํ™”์‹œํ‚ค๋Š” ๊ฐ€์น˜ ์žˆ๋Š” ์ž์‚ฐ์ด๋ผ ๋ฏฟ์Šต๋‹ˆ๋‹ค.