๐ ํ๋ก์ ํธ ๊ฐ์
- (ํ์ต ๋ชฉํ) ์ด๋ฒ ํ๋ก์ ํธ์ ์ฃผ์ ๋ชฉํ๋ ์น ํฌ๋กค๋ง๊ณผ PDF ๋ฐ์ดํฐ ์ถ์ถ์ ํตํด ๊ธ์ต๊ฐ๋ ์์์ ์ ๊ณตํ๋ ๊ฒ์ฌ๊ฒฐ๊ณผ ์ ์ฌ ์ ๋ณด๋ฅผ ์๋์ผ๋ก ๋ค์ด๋ก๋ํ๊ณ , OCR์ ํ์ฉํ์ฌ ํ ์คํธ๋ฅผ ์ถ์ถํ๋ ๊ธฐ์ ํ์ต
- (๋ถ์ ๊ณผ์ ) ๊ธ์ต๊ฐ๋ ์ ์น์ฌ์ดํธ์์ "์ ์ฉ์ ๋ณด"์ ๊ดํ ์ ์ฌ ์ ๋ณด๋ฅผ ๊ฒ์ํ์ฌ, ๊ด๋ จ๋ PDF ํ์ผ์ ๋ค์ด๋ก๋ํ ํ, ํด๋น ํ์ผ์์ ํ์ํ ์ ๋ณด๋ฅผ ์ถ์ถํ์ฌ ์์ ํ์ผ๋ก ์ ์ฅ [ ์น ํฌ๋กค๋ง → PDF ์ฒ๋ฆฌ → OCR ํ์ฉ → ์์ ์ ์ฅ]
๐ป ํ์ต ๋ด์ฉ
1. ์ค์น ๋ผ์ด๋ธ๋ฌ๋ฆฌ
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ | ๊ธฐ๋ฅ(์์ธ ์ค๋ช ) | ์ค์น ์ฝ๋ | ์ค์น ํ์ธ ์ฝ๋ |
| requests | HTTP ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ์ฒ๋ฆฌํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ. ์ฃผ๋ก ์น์์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ก๋ ๋ฐ์ ๋ ์ฌ์ฉ | pip install requests | import requests ๋ฅผ ํตํด ํ์ธ |
| pdfplumber | PDF์์ ํ ์คํธ, ํ ๋ฑ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ. ํ ์คํธ๋ฅผ ์ฝ๊ฒ ํ์ฑํ ์ ์๋๋ก ๋์์ค | pip install pdfplumber | import pdfplumber ๋ก ํ์ธ |
| pytesseract | OCR(Optical Character Recognition) ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ์ด๋ฏธ์ง๋ฅผ ํ ์คํธ๋ก ๋ณํ. PDF์์ ํ ์คํธ๋ฅผ ๋ชป ์ฝ์ ๋ ์ฌ์ฉ | pip install pytesseract | import pytesseract๋ก ํ์ธ |
| pdf2image | PDF ํ์ผ์ ์ด๋ฏธ์ง๋ก ๋ณํํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ. OCR์ ์ํํ๊ธฐ ์ํ ์ด๋ฏธ์ง ๋ณํ์ ์ฌ์ฉ | pip install pdf2image | from pdf2image import convert_from_path๋ก ํ์ธ |
| pillow | ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, pdf2image์ ๋ณํ๋ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฌ์ฉ | pip install pillow | from PIL import Image๋ก ํ์ธ |
| openpyxl | Excel ํ์ผ์ ์ฝ๊ณ ์ฐ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ. ๋ฐ์ดํฐ ์ถ์ถ ํ Excel๋ก ์ ์ฅํ๋ ๋ฐ ์ฌ์ฉ | pip install openpyxl | import openpyxl๋ก ํ์ธ |
| beautifulsoup4 | HTML, XML ๋ฌธ์๋ฅผ ํ์ฑํ๊ณ ์น ์คํฌ๋ํ์ ํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ. PDF ๋งํฌ ์ถ์ถ ๋ฐ ํฌ๋กค๋ง์ ์ฌ์ฉ | pip install beautifulsoup4 | from bs4 import BeautifulSoup์ผ๋ก ํ์ธ |
2. ํฌ๋กฌ ๋๋ผ์ด๋ฒ ์ค์น ๋ฐ ์ค์
(1) Homebrew ์ค์น
#bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Homebrew ์ค์น ํ์ธ
#bash
brew --version
Homebrew๋ฅผ PATH์ ์ถ๊ฐ(ํฐ๋ฏธ๋์์ ์ธ์์ ํ์ง ๋ชปํ๋ ์ํฉ)
#bash
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
(2) ChromeDriver ์ค์น
#bash
brew install chromedriver
ChromeDriver ์ค์น ์์น ํ์ธ
#bash
which chromedriver
macOS์์ ChromeDriver ์คํ ํ์ฉํ๊ธฐ
1. ์์คํ ์ค์ (์์คํ ํ๊ฒฝ์ค์ ) ์ด๊ธฐ
Apple ๋ฉ๋ด → "์์คํ ์ค์ " ๋๋ "์์คํ ํ๊ฒฝ์ค์ " ํด๋ฆญ
2. ๋ณด์ ๋ฐ ๊ฐ์ธ์ ๋ณด ๋ณดํธ ๋ฉ๋ด๋ก ์ด๋
"๊ฐ์ธ์ ๋ณด ๋ณดํธ ๋ฐ ๋ณด์" (๋๋ "๋ณด์ ๋ฐ ๊ฐ์ธ ์ ๋ณด ๋ณดํธ") ์ ํ
3. ํ๋จ์ 'chromedriver ์ฐจ๋จ๋จ' ๋ฉ์์ง๊ฐ ์๋์ง ํ์ธ
"chromedriver"๋ Apple์์ ์ธ์ฆํ์ง ์์๊ธฐ ๋๋ฌธ์ ์คํํ ์ ์์ต๋๋ค. ๋ฉ์์ง ์์ ์๋ "์คํ ํ์ฉ" ๋๋ "์ด๊ธฐ" ๋ฒํผ ํด๋ฆญ
3. ์ฝ๋ ๋ฆฌ๋ทฐ
| ์ค๋ฅ ๋ด์ฉ | ์์ธ ๋ถ์ | ํด๊ฒฐ ๋ฐฉ๋ฒ |
| ๊ฒ์์ฐฝ์์ "์ ์ฉ์ ๋ณด"๋ฅผ ๊ฒ์ํ ์ ์์ |
๊ฒ์์ฐฝ <input>์ด <span class="keyword"> ์์ ํฌํจ๋์ด ์๊ธฐ ๋๋ฌธ์, ๊ฒ์์ฐฝ์ ๋ฐ๋ก ์กฐ์ํ๋ ๊ฒ์ด ์๋๋ผ, ์์ ์์(span.keyword)๋ฅผ ๋จผ์ ํด๋ฆญํด์ผ ํจ |
<span class="keyword"> ์์๋ฅผ ๋จผ์ ํด๋ฆญํด์ ๊ฒ์์ฐฝ์ ํ์ฑํ EC.element_to_be_clickable((By.CLASS_NAME, "keyword")) keyword_span.click() ์คํ ๊ฒ์์ฐฝ์ด ํ์ฑํ๋์์ผ๋ฏ๋ก input#query๋ฅผ ์ฐพ๊ณ send_keys() ์คํ EC.element_to_be_clickable((By.ID, "query")) search_box.send_keys("์ ์ฉ์ ๋ณด") |
| Selenium์ด ์ํ๋ ๊ฒ์์ฐฝ(id="query")์ด ์๋๋ผ ๋ค๋ฅธ ๊ฒ์์ฐฝ (id="searchWrd")์ ์ ํํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ |
<form id="searchFrm"> ์์ ์๋ <input id="query" name="query">๋ฅผ ์ฐพ์์ผ ํจ. id="query"๋ฅผ ๊ฐ์ง <input>์ ์ ํํ searchFrm ๋ด๋ถ์์ ์ฐพ์์ผ ํจ |
searchFrm ๋ด๋ถ์์ query๋ฅผ ์ฐพ๋๋ก ์ฝ๋ ๋ณ๊ฒฝ search_box = search_form.find_element(By.ID, "query") ์ด๋ ๊ฒ ํ๋ฉด ์๋ชป๋ ๊ฒ์์ฐฝ(searchWrd)์ด ์๋๋ผ searchFrm ๋ด๋ถ์ query ๊ฒ์์ฐฝ๋ง ์ฐพ์ ์ ์์. ๊ฒ์์ฐฝ์ ํด๋ฆญํ ํ ๊ฒ์ ์คํ ์ผ๋ถ ์ฌ์ดํธ์์๋ click()์ ๋จผ์ ํด์ผ ๊ฒ์์ฐฝ์ด ํ์ฑํ๋จ. search_box.click() ๊ฒ์์ด ์ ๋ ฅ ํ ENTER ํค ์คํ search_box.send_keys("์ ์ฉ์ ๋ณด") search_box.send_keys(Keys.RETURN) |
| pdf ๋ด์ ํ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ์ง ๋ชปํ๋ ํ์ | pdfplumber.extract_text() ๋ฐฉ์์ผ๋ก๋ ํ ์์ ๋ด์ฉ์ด๋ ์์์ ์ธ์ํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ๊ฐ ์ ๋๋ก ์ถ์ถ๋์ง ์์ | pdfplumber.extract_table()์ ์ฌ์ฉํ์ฌ ํ(table) ๋ฐ์ดํฐ ์ถ์ถ pdfplumber.extract_text()๋ก ์ผ๋ฐ ํ ์คํธ ์์ญ(๊ธ์ตํ์ฌ๋ช , ์ ์ฌ์กฐ์น์ผ, ์ ์ฌ๋์์ฌ์ค) ์ถ์ถ ์ ๊ท์(re)์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ์ ๋ฆฌ ์ถ์ถํ ๋ฐ์ดํฐ๋ฅผ ์์ ํ์ผ๋ก ์ ์ฅ |
| pdf ๋ฐ์ดํฐ๊ฐ ์ถ์ถ๋์ง ์๋ ๋ฌธ์ |
pdfplumber.extract_text()๋ฅผ ๋จผ์ ์คํํ๊ณ , ๋ด์ฉ์ด ์๋ค๋ฉด OCR(๋ฌธ์์ธ์) ์ฌ์ฉ ํ ์ด๋ธ์ extract_table()๋ก ์ถ์ถํ ํ, ์ขํ๋ฅผ ์ง์ ์ง์ ํด ๊ฐ์ ํ ์คํธ๋ฅผ ์ ๊ท์์ผ๋ก ์ ์ ํ์ฌ ํ์ํ ์ ๋ณด๋ง ์ถ์ถ |
pytesseract ๋ฐ pdf2image ์ค์น |
| pytesseract๊ฐ ํ๊ตญ์ด ์ธ์ด ํ์ผ์ ์ฐพ์ง ๋ชปํ๋ ๋ฌธ์ |
Tesseract์ ํ๊ตญ์ด(kor.traineddata) ๋ฐ์ดํฐ๊ฐ ์ค์น๋์ง ์์ Tesseract๊ฐ tessdata ๊ฒฝ๋ก๋ฅผ ์ฐพ์ง ๋ชปํจ TESSDATA_PREFIX ํ๊ฒฝ ๋ณ์๊ฐ ์ค์ ๋์ง ์์ |
Tesseract ํ๊ตญ์ด ๋ฐ์ดํฐ ํ์ผ ์ค์น wget -P /opt/homebrew/share/tessdata/ https://github.com/tesseract-ocr/tessdata_best/raw/main/kor.traineddata |
| "์ ์ฉ์ ๋ณด"๋ฅผ ๊ฒ์ํ ํ pdf ๋งํฌ๋ฅผ ์ถ์ถํ์ง ๋ชปํ๋ ์ค๋ฅ |
PDF ๋งํฌ๋ ํ์ด์ง์์ ๋์ ์ผ๋ก ๋ก๋ฉ๋๊ณ ์์ ๊ฐ๋ฅ์ฑ ์ฆ, ํ์ด์ง์ HTML์ PDF ๋งํฌ๊ฐ ์ง์ ์ ์ผ๋ก ๋ณด์ด์ง ์์ ์ ์์ |
๋์ ์ฝํ
์ธ ์ฒ๋ฆฌ: ์น ํ์ด์ง์ ์์๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋์ ์ผ๋ก ๋ก๋ฉ๋๋ฏ๋ก ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ์๋ก์ด ๋ฐฉ๋ฒ์ ์๋ PDF ๋ค์ด๋ก๋ ๋งํฌ ์ถ์ถ ๋ฐ ํด๋ฆญ: ๋งํฌ๋ฅผ ์ง์ ํด๋ฆญํ์ฌ PDF ๋ค์ด๋ก๋ ์๋ ํค๋๋ฆฌ์ค ๋ธ๋ผ์ฐ์ : ์น ํ์ด์ง๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํ๊ณ ๋ค์ด๋ก๋ ๋ํ์์ ์์ด ํ์ผ์ ๋ค์ด๋ก๋ํ ์ ์๋๋ก ์ค์ |
| 1ํ์ด์ง์ ๋ค์ด๋ก๋๋ง ์ฑ๊ณตํ๊ณ 2ํ์ด์ง๋ถํฐ๋ ํฌ๋กค๋ง์ด ์คํจํจ |
javascript:fnSearch(2)์ ๊ฐ์ JavaScript ํจ์๊ฐ ํธ์ถ๋๋ ๋ฐฉ์์ผ๋ก ํ์ด์ง๊ฐ ๋ก๋๋๋ ๊ตฌ์กฐ๋ผ๋ฉด, ํด๋น JavaScript ํจ์๋ฅผ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ์ํด์๋ click() ๋์ execute_script()๋ฅผ ์ฌ์ฉํ์ฌ JavaScript๋ฅผ ์คํํ ํ์ | JavaScript ์คํ: ํ์ด์ง ๋ฒํธ๋ฅผ ํด๋ฆญํ ๋, driver.execute_script()๋ฅผ ์ฌ์ฉํ์ฌ javascript:fnSearch(page_num)๋ฅผ ์คํํ๋๋ก ๋ณ๊ฒฝ ํ์ด์ง ๋ฒํธ ํด๋ฆญ: ๊ฐ ํ์ด์ง ๋ฒํธ๋ฅผ ํด๋ฆญํ ๋ XPath์์ href="javascript:fnSearch(page_num)" ๊ฐ์ ์ฌ์ฉํ์ฌ ๋ฒํผ ์ฐพ๊ธฐ |
| pdf์์ "์ ์ฌ๋์์ฌ์ค"์ ์ ๋๋ก ์ฝ์ด์ค์ง ๋ชปํ๋ ์ค๋ฅ |
ํ์ฌ pdfplumber๋ฅผ ์ฌ์ฉํด์ ํ
์คํธ๋ฅผ ์ถ์ถํ๊ณ ์์ง๋ง, "์ ์ฌ๋์์ฌ์ค" ์ดํ์ ํ
์คํธ๊ฐ ์ ๋๋ก ๋์ค๋์ง ํ์ธํ๊ณ , ๊ทธ ๋ถ๋ถ์ ์ข ๋ ๊ตฌ์ฒด์ ์ผ๋ก ์ถ์ถํ๋ ๋ฐฉ๋ฒ์ ์ ์ฉํด์ผ ํจ ํ์ฌ find ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ณ ์๋๋ฐ, ํ ์คํธ๊ฐ ์ฌ๋ฌ ์ค๋ก ๋๋์ด ์์ ์ ์๊ธฐ ๋๋ฌธ์ ์ข ๋ ์ ๊ตํ ๋ฐฉ๋ฒ์ผ๋ก "์ ์ฌ๋์์ฌ์ค"๊ณผ ๊ทธ ์ดํ์ ๋ชจ๋ ํ ์คํธ๋ฅผ ์ถ์ถํ๋๋ก ์์ ํ ํ์ |
PDF์์ "์ ์ฌ๋์์ฌ์ค" ์๋์ ํ ์คํธ๋ฅผ ์ ๋๋ก ์ถ์ถํ ์ ์๋๋ก violation_details ๋ถ๋ถ์ ์์ ํ์ฌ "์ ์ฌ๋์์ฌ์ค" ์ดํ ๋ชจ๋ ํ ์คํธ๋ฅผ ํฌํจํ๋๋ก ํจ |
4. ์ต์ข ์ฝ๋
import time
import os
import requests
import pdfplumber
import pandas as pd
import re
import pytesseract
from pdf2image import convert_from_path
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from bs4 import BeautifulSoup
# ๐ Chrome WebDriver ์ค์ (ํค๋๋ฆฌ์ค ๋ชจ๋ ์ ๊ฑฐ)
chrome_driver_path = "/opt/homebrew/bin/chromedriver" # ํฌ๋กฌ ๋๋ผ์ด๋ฒ ๊ฒฝ๋ก
download_dir = "/Users/allzero/Downloads" # ๋ค์ด๋ก๋ ํด๋ ๊ฒฝ๋ก ์ค์
# Chrome ์ต์
์ค์
options = Options()
# options.add_argument("--headless") # UI ์์ด ์คํ์ ํ์ง ์์, ๋ธ๋ผ์ฐ์ ํ์๋จ
options.add_argument("--disable-gpu")
options.add_argument(f"--window-size=1920x1080")
options.add_experimental_option("prefs", {
"download.default_directory": download_dir, # ๋ค์ด๋ก๋ ํด๋ ์ค์
"download.prompt_for_download": False, # ๋ค์ด๋ก๋ ๋ํ์์ ๋นํ์ฑํ
"download.directory_upgrade": True, # ๋๋ ํ ๋ฆฌ ์
๊ทธ๋ ์ด๋
"savefile.default_directory": download_dir, # ์ ์ฅ ๋๋ ํ ๋ฆฌ ์ค์
"plugins.always_open_pdf_externally": True # PDF ํ์ผ ์ธ๋ถ์์ ์ด๊ธฐ
})
# ๐ ๋ธ๋ผ์ฐ์ ์คํ
service = Service(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=options)
# ๐ ๊ธ์ต๊ฐ๋
์ ๊ฒ์ฌ๊ฒฐ๊ณผ์ ์ฌ ๊ฒ์ํ ์ ์
URL = "https://www.fss.or.kr/fss/job/openInfo/searchList.do"
driver.get(URL)
# ๐ ๊ฒ์์ฐฝ ์ฐพ๊ธฐ ๋ฐ "์ ์ฉ์ ๋ณด" ์
๋ ฅ
search_box = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "query")))
search_box.send_keys("์ ์ฉ์ ๋ณด")
search_box.send_keys(Keys.RETURN)
# ๐ "์ ์ฉ์ ๋ณด" ๊ฒ์ ํ 3์ด ๋๊ธฐ
print("โ
'์ ์ฉ์ ๋ณด' ๊ฒ์ ์๋ฃ!")
time.sleep(3) # ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ก๋ฉ๋ ๋๊น์ง 3์ด ๋๊ธฐ
# ๐ 1ํ์ด์ง ๋ก๋ฉ ํ ๋ช
ํํ๊ฒ ๋ก๋ฉ๋ ์์ ํ์ธ
try:
# 1ํ์ด์ง๊ฐ ์์ ํ ๋ก๋๋์๋์ง ํ์ธ
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//ul[@class='list-desc']"))) # ๋ฆฌ์คํธ๊ฐ ๋ก๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆผ
print("โ
1ํ์ด์ง ๋ก๋ฉ ์๋ฃ!")
except TimeoutException:
print("โ ๏ธ 1ํ์ด์ง ๋ก๋ฉ ์คํจ!")
# ๐ 1ํ์ด์ง PDF ๋งํฌ ์ถ์ถ ๋ฐ ๋ค์ด๋ก๋
pdf_links = []
print(f"๐ 1ํ์ด์ง ํฌ๋กค๋ง ์ค...")
soup = BeautifulSoup(driver.page_source, "html.parser")
for a_tag in soup.find_all("a", href=True):
href = a_tag["href"]
if ".pdf" in href:
if href.startswith("/"):
href = "https://www.fss.or.kr" + href
pdf_links.append(href)
# ๐ ๊ฐ PDF ๋งํฌ๋ฅผ ๋ค์ด๋ก๋
for index, pdf_url in enumerate(pdf_links):
print(f"๐ฅ {index+1}/{len(pdf_links)} - ๋ค์ด๋ก๋ ์ค: {pdf_url}")
# PDF ๋ค์ด๋ก๋ ๋งํฌ ํด๋ฆญํ์ฌ ๋ค์ด๋ก๋ ์๋
driver.get(pdf_url) # PDF ๋ค์ด๋ก๋ ๋งํฌ๋ก ์ด๋
time.sleep(2) # ๋ค์ด๋ก๋ ์ฐฝ ์ด๋ฆด ๋๊น์ง ์ ์ ๋๊ธฐ
print("โ
1ํ์ด์ง PDF ๋งํฌ ๋ค์ด๋ก๋ ์๋ฃ! '๋ค์ด๋ก๋ ํด๋'์ ์ ์ฅ๋์์ต๋๋ค.")
# ๐ 2ํ์ด์ง๋ถํฐ 8ํ์ด์ง๊น์ง ํฌ๋กค๋ง (์ฌ๊ธฐ์๋ ํ์ด์ง ํฌ๋กค๋ง๊ณผ PDF ๋ค์ด๋ก๋)
for page_num in range(2, 9):
print(f"๐ {page_num} ํ์ด์ง ํฌ๋กค๋ง ์ค...")
# ํ์ด์ง ๋ฒํผ ํด๋ฆญ (ํ์ด์ง ๋ฒํธ ํด๋ฆญ)
try:
page_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.XPATH, f"//a[@data-pageindex='{page_num}']"))
)
page_button.click()
time.sleep(3) # ํ์ด์ง๊ฐ ๋ก๋ฉ๋ ๋๊น์ง ๋๊ธฐ
# ํ์ด์ง ๋ก๋ฉ ํ PDF ๋งํฌ ์์ง
soup = BeautifulSoup(driver.page_source, "html.parser")
for a_tag in soup.find_all("a", href=True):
href = a_tag["href"]
if ".pdf" in href:
if href.startswith("/"):
href = "https://www.fss.or.kr" + href
pdf_links.append(href)
except TimeoutException:
print(f"โ ๏ธ {page_num} ํ์ด์ง ๋ก๋ฉ ์คํจ!")
# ๐ ๊ฐ PDF ๋งํฌ๋ฅผ ๋ค์ด๋ก๋
for index, pdf_url in enumerate(pdf_links):
print(f"๐ฅ {index+1}/{len(pdf_links)} - ๋ค์ด๋ก๋ ์ค: {pdf_url}")
# PDF ๋ค์ด๋ก๋ ๋งํฌ ํด๋ฆญํ์ฌ ๋ค์ด๋ก๋ ์๋
driver.get(pdf_url) # PDF ๋ค์ด๋ก๋ ๋งํฌ๋ก ์ด๋
time.sleep(2) # ๋ค์ด๋ก๋ ์ฐฝ ์ด๋ฆด ๋๊น์ง ์ ์ ๋๊ธฐ
print("โ
PDF ๋งํฌ ๋ค์ด๋ก๋ ์๋ฃ! ํ์ผ์ด '๋ค์ด๋ก๋ ํด๋'์ ์ ์ฅ๋์์ต๋๋ค.")
# ๐ OCR์ ํ์ฉํ PDF ๋ฐ์ดํฐ ์ถ์ถ ํจ์
def extract_info(pdf_path):
with pdfplumber.open(pdf_path) as pdf:
text = "\n".join([page.extract_text() for page in pdf.pages if page.extract_text()])
# ๐น 1. ํ
์คํธ๊ฐ ์ถ์ถ๋์ง ์์ ๊ฒฝ์ฐ OCR ์ฌ์ฉ
if not text.strip():
print("โ ๏ธ ์ผ๋ฐ ํ
์คํธ ์ถ์ถ ์คํจ! OCR ์ฌ์ฉ")
images = convert_from_path(pdf_path)
ocr_text = ""
for image in images:
ocr_text += pytesseract.image_to_string(image, lang="kor") + "\n"
text = ocr_text.strip()
# ๐น 2. ๊ธ์ตํ์ฌ๋ช
์ถ์ถ
company_pattern = r"๊ธ์ตํ์ฌ๋ช
\s*:\s*(.*)"
financial_company = re.search(company_pattern, text)
financial_company = financial_company.group(1).strip() if financial_company else "N/A"
# ๐น 3. ์ ์ฌ์กฐ์น์ผ ์ถ์ถ
date_pattern = r"์ ์ฌ์กฐ์น์ผ\s*:\s*([\d]{4}\.\s*[\d]{1,2}\.\s*[\d]{1,2})"
action_date = re.search(date_pattern, text)
action_date = action_date.group(1).strip() if action_date else "N/A"
# ๐น 4. ์ ์ฌ์กฐ์น๋ด์ฉ(ํ) ์ถ์ถ
action_content = []
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
tables = page.extract_table()
if tables:
for row in tables:
# None ๊ฐ์ ๋น ๋ฌธ์์ด๋ก ๋์ฒดํ์ฌ ์ฒ๋ฆฌ
row = [str(cell) if cell is not None else "" for cell in row]
action_content.append(" | ".join(row))
action_content = "\n".join(action_content) if action_content else "N/A"
# ๐น 5. ์ ์ฌ๋์์ฌ์ค ์ถ์ถ: "์ ์ฌ๋์์ฌ์ค" ์๋์ ๋ชจ๋ ํ
์คํธ๋ฅผ ์ถ์ถ
violation_start = text.find("์ ์ฌ๋์์ฌ์ค")
if violation_start != -1:
violation_details = text[violation_start:] # "์ ์ฌ๋์์ฌ์ค" ๋ถํฐ ๋๊น์ง
else:
violation_details = "N/A"
return [financial_company, action_date, action_content, violation_details]
# ๐ ๋ฐ์ดํฐ ์ถ์ถ ๋ฐ ์์
์ ์ฅ
data_list = []
for index, pdf_url in enumerate(pdf_links):
print(f"๐ฅ {index+1}/{len(pdf_links)} - ๋ค์ด๋ก๋ ์ค: {pdf_url}")
# PDF ๋ค์ด๋ก๋
pdf_response = requests.get(pdf_url)
pdf_path = f"temp_{index}.pdf"
with open(pdf_path, "wb") as f:
f.write(pdf_response.content)
# ๋ฐ์ดํฐ ์ถ์ถ
extracted_data = extract_info(pdf_path)
data_list.append(extracted_data)
# ์์ ํ์ผ ์ญ์
os.remove(pdf_path)
print("โ
PDF ๋ฐ์ดํฐ ์ถ์ถ ์๋ฃ! ์์
์ ์ฅ ์์...")
# ๐ ํ ํํ๋ก ๋ณํ ํ ์์
์ ์ฅ
df = pd.DataFrame(data_list, columns=["๊ธ์ตํ์ฌ๋ช
", "์ ์ฌ์กฐ์น์ผ", "์ ์ฌ์กฐ์น๋ด์ฉ", "์ ์ฌ๋์์ฌ์ค"])
df.to_excel("๊ธ์ต๊ฐ๋
์_์ ์ฉ์ ๋ณด_์ ์ฌ๊ฒฐ๊ณผ.xlsx", index=False, engine='openpyxl')
print("โ
์์
์ ์ฅ ์๋ฃ! '๊ธ์ต๊ฐ๋
์_์ ์ฉ์ ๋ณด_์ ์ฌ๊ฒฐ๊ณผ.xlsx' ํ์ผ์ ํ์ธํ์ธ์.")
# ๐ ๋ธ๋ผ์ฐ์ ์ข
๋ฃ
driver.quit()
๐ ๊ฐ์ ํ ์ & ์ถ๊ฐ๋ก ๋ถ์ํด๋ณผ ์ ์๋ ์ฌํญ
- PDF์์ ํ
์คํธ ์ถ์ถ ์ฑ๋ฅ ํฅ์ :
- pdfplumber์ pytesseract์ ์กฐํฉ์ ์๋นํ ์ ์ฉํ์ง๋ง, ๋ณต์กํ PDF ํ์ผ์์๋ ์ฌ์ ํ ํ ์คํธ ์ถ์ถ์ด ์ด๋ ค์ด ๊ฒฝ์ฐ๊ฐ ์์ ์ ์์. PDF ๊ตฌ์กฐ ๋ถ์์ ํตํด ๋ ๋์ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผ
- ์น ํฌ๋กค๋ง ์ต์ ํ :
- ์น ํ์ด์ง์ ๋์ ์์๊ฐ ๋ง์ ๊ฒฝ์ฐ, selenium์ ํตํด ๋ก๋ฉ ๋๊ธฐ ๋ฐ ํ์ด์ง ๋ด ์์์ ๋ก๋ฉ ์ํ๋ฅผ ์ ํํ ์ฒดํฌํ๋ ๊ฒ์ด ์ค์
-> headless ๋ชจ๋ ๋์ ์ผ๋ฐ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ฌ์ฉํ์ฌ ํฌ๋กค๋ง์ ํจ์จ์ฑ์ ๋์ผ ์ ์์
- ์น ํ์ด์ง์ ๋์ ์์๊ฐ ๋ง์ ๊ฒฝ์ฐ, selenium์ ํตํด ๋ก๋ฉ ๋๊ธฐ ๋ฐ ํ์ด์ง ๋ด ์์์ ๋ก๋ฉ ์ํ๋ฅผ ์ ํํ ์ฒดํฌํ๋ ๊ฒ์ด ์ค์
- PDF ๋ด์ฉ์ ๊ตฌ์กฐ์ ๋ถ์:
- ์ถ์ถ๋ ํ ํํ ๋ฐ์ดํฐ๊ฐ ์ ๋๋ก ์ ๋ฆฌ๋์ง ์์ ๊ฒฝ์ฐ, ์ถ๊ฐ์ ์ธ ๋ฐ์ดํฐ ํด๋ฆฐ์ง์ด ํ์
๐ ์ถ์ถ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ค ์ ํํ ํ์์ผ๋ก ๋ณํํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ถ๊ฐ ๋ถ์
- ์ถ์ถ๋ ํ ํํ ๋ฐ์ดํฐ๊ฐ ์ ๋๋ก ์ ๋ฆฌ๋์ง ์์ ๊ฒฝ์ฐ, ์ถ๊ฐ์ ์ธ ๋ฐ์ดํฐ ํด๋ฆฐ์ง์ด ํ์
- ๊ณ ๊ธ ๋ฐ์ดํฐ ๋ถ์ ๋ฐ ์๊ฐํ:
- ์์ ๋ก ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํน์ ๊ธ์ตํ์ฌ์ ๋ํ ์ ์ฌ ์ด๋ ฅ์ ๋ถ์ํ๊ฑฐ๋, ์ ์ฌ ์กฐ์น๊ฐ ๋ง์ด ๋ฐ์ํ ์์ ์ ๋ํ ์๊ณ์ด ๋ถ์