위키문헌:현대 한글 문헌 프로젝트/WikisourceClovaOCR.py
외관
- 파일을 위키미디어 공용에서 다운로드받아 클로바 OCR로 텍스트를 인식시키는 프로그램입니다.
- 다운로드나 변환된 파일은 프로그램 종료 후 삭제됩니다.
- 클로바 General OCR은 100건/월까지 무료입니다. 그 이상의 사용이 필요한 경우 특수:이메일보내기/Motoko (WMKR)로 문의해 주세요.
"""
pdf2image 패키지 설치 필요
(공통)
pip install pdf2image requests pillow
(Ubuntu/Debian)
sudo apt-get update
sudo apt-get install poppler-utils
(macOS)
brew install poppler
DjVu 도구 설치
(Ubuntu/Debian)
sudo apt-get install djvulibre-bin
(macOS)
brew install djvulibre
네이버 API 콘솔에서 결제 정보를 입력하고 도메인을 활성화한 뒤 API_URL, SECRET-KEY에 각각 APIGW invoke url과 secret-key 값을 할당해 주세요. (GW자동연동 추천)
https://guide.ncloud-docs.com/docs/clovaocr-domain
"""
from pdf2image import convert_from_path
from urllib.parse import urlparse
from pathlib import Path
import uuid
import time
import base64
import json
import subprocess
import requests
import re
import io
import os
HEADERS = {
'User-Agent': 'WikisourceClovaOCR/1.0 (Phython/User:Motoko C. K.)'
}
API_URL = ''
SECRET_KEY = ''
def get_file_url(file_name):
api_url = 'https://commons.wikimedia.org/w/api.php'
params = {
'action': 'query',
'titles': file_name,
'prop': 'imageinfo',
'iiprop': 'url',
'format': 'json',
}
response = requests.get(api_url, params=params, headers=HEADERS)
response.raise_for_status()
data = response.json()
pages = data.get('query', {}).get('pages', {})
page = next(iter(pages.values()))
image_url = page['imageinfo'][0]['url']
return image_url
def decect_file_from_cloava(file_path):
file_extention = file_path.rsplit('.', 1)[-1]
with open(file_path, "rb") as f:
image_data = base64.b64encode(f.read()).decode("utf-8")
api_url = API_URL
headers = {
"X-OCR-SECRET": SECRET_KEY,
"Content-Type": "application/json"
}
params = {
"version": "V2",
"requestId": str(uuid.uuid4()),
"timestamp": int(time.time() * 1000),
"lang": "ko",
"images": [
{
"format": file_extention,
"name": file_path,
"data": image_data
}
]
}
response = requests.post(api_url, headers=headers, data=json.dumps(params).encode('UTF-8'))
response.raise_for_status()
data = response.json()
line_info = data['images'][0]['fields']
text = ''
for line in line_info:
text = text + line['inferText'] + '\n'
return text
def file_creator(title, texts):
# 파일명 생성 (특수문자 제거)
safe_filename = re.sub(r'[<>:"/\\|?*]', '_', title)
filename = f"{safe_filename}.txt"
clean_text = texts[0].description
# 파일 저장
with open(filename, 'w', encoding='utf-8') as f:
f.write(clean_text)
print(f"✓ 성공: '{filename}' 파일로 저장되었습니다.")
print(f" 문자 수: {len(clean_text):,}")
return True
def download_file(url, save_path='downloaded.pdf'):
"""
URL에서 PDF 파일을 다운로드합니다.
Args:
url: PDF 파일 URL
save_path: 저장할 파일 경로
Returns:
다운로드된 파일 경로 또는 None
"""
try:
print(f"📥 파일 다운로드 중: {url}")
response = requests.get(url, headers=HEADERS, stream=True)
response.raise_for_status()
# Content-Type 확인
content_type = response.headers.get('Content-Type', '')
print(f"Content-Type: {content_type}")
# 파일 저장12
with open(save_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
file_size = os.path.getsize(save_path)
print(f"✅ 다운로드 완료: {save_path} ({file_size:,} bytes)")
return save_path
except requests.exceptions.RequestException as e:
print(f"❌ 다운로드 오류: {e}")
return None
except Exception as e:
print(f"❌ 오류 발생: {e}")
return None
def pdf_to_png(pdf_path, output_dir='output_images', dpi=300, max_dimension=2000):
"""
PDF 파일을 낱장의 PNG 이미지로 변환합니다.
Args:
pdf_path: PDF 파일 경로
output_dir: PNG 파일을 저장할 디렉토리
dpi: 이미지 해상도 (기본 200, 높을수록 고화질)
max_dimension: 최대 이미지 크기 (픽셀, 큰 파일 방지용)
Returns:
생성된 이미지 파일 경로 리스트
"""
try:
# 출력 디렉토리 생성
os.makedirs(output_dir, exist_ok=True)
print(f"\nPDF를 PNG로 변환 중...")
print(f" - 입력: {pdf_path}")
print(f" - 출력 디렉토리: {output_dir}")
print(f" - 해상도: {dpi} DPI")
# PDF를 이미지로 변환
images = convert_from_path(pdf_path, dpi=dpi)
print(f" - 총 페이지 수: {len(images)}")
saved_files = []
for i, image in enumerate(images, start=1):
width, height = image.size
# 이미지 크기 조정 (너무 큰 경우)
if width > max_dimension or height > max_dimension:
scale_factor = min(max_dimension / width, max_dimension / height)
new_width = int(width * scale_factor)
new_height = int(height * scale_factor)
image = image.resize((new_width, new_height))
print(f" 📄 페이지 {i}: {width}x{height} → {new_width}x{new_height} (크기 조정됨)")
else:
print(f" 📄 페이지 {i}: {width}x{height}")
# PNG 파일로 저장
output_path = os.path.join(output_dir, f"page_{i:03d}.png")
image.save(output_path, 'PNG')
saved_files.append(output_path)
# 파일 크기 표시
file_size = os.path.getsize(output_path)
print(f" ✅ 저장: {output_path} ({file_size:,} bytes)")
print(f"\n✨ 변환 완료! {len(saved_files)}개의 PNG 파일 생성")
return saved_files
except Exception as e:
print(f"❌ 변환 오류: {e}")
return []
def pdf_download_and_convert(url, output_dir='output_images', dpi=200):
"""
PDF를 다운로드하고 PNG로 변환하는 전체 프로세스
Args:
url: PDF 파일 URL
output_dir: PNG 파일을 저장할 디렉토리
dpi: 이미지 해상도
Returns:
생성된 이미지 파일 경로 리스트
"""
# 1. PDF 다운로드
pdf_path = download_file(url)
if not pdf_path:
print("❌ PDF 다운로드에 실패했습니다.")
return []
# 2. PNG로 변환
image_files = pdf_to_png(pdf_path, output_dir=output_dir, dpi=dpi)
# 3. 다운로드한 PDF 파일 삭제 (선택사항)
os.remove(pdf_path)
print(f"🗑️ 임시 PDF 파일 삭제: {pdf_path}\n")
return image_files
def djvu_to_png(djvu_path, output_dir="output_pages"):
"""DjVu 파일을 PNG 이미지로 변환"""
# 출력 디렉토리 생성
Path(output_dir).mkdir(exist_ok=True)
subprocess.run(['djvm', '-c', 'downloaded.djvu', 'downloaded.djvu'], check=True)
# 총 페이지 수 확인
result = subprocess.run(
['djvused', djvu_path, '-e', 'n'],
capture_output=True,
text=True
)
total_pages = int(result.stdout.strip())
print(f" - 총 페이지 수: {total_pages}")
output_files = []
# 각 페이지를 PNG로 변환
for page_num in range(1, total_pages + 1):
output_file = os.path.join(output_dir, f"page_{page_num:04d}.tiff")
subprocess.run([
'ddjvu',
'-format=tiff',
'-page={}'.format(page_num),
djvu_path,
output_file
], check=True)
print(f"✅ 변환 완료 : {page_num}/{total_pages} -> {output_file}")
output_files.append(output_file)
print(f"\nAll pages saved to {output_dir}/")
return output_files
def converted_image_detect(image_files):
file_type = type(image_files)
if file_type == list:
page_num = len(image_files)
print(f"페이지 수:{page_num}\n")
print("분석할 페이지의 범위를 입력하십시오\n")
first_page = int(input("첫 번째 페이지: "))
print("\n")
last_page = int(input("마지막 페이지: "))
for file in image_files[(first_page-1):(last_page)]:
print(f" - {file}")
print(f'이미지 파일 분석 중: {file}\n')
text = decect_file_from_cloava(file)
try:
print(text)
except:
print("텍스트 감지에 실패했습니다.\n")
else:
text = decect_file_from_cloava(image_files)
print(text)
try:
print(text)
except:
print("텍스트 감지에 실패했습니다.\n")
def main():
"""
메인 함수
"""
file_name = input("공용 파일 이름을 입력하세요\n(예시: image.jpg): ").strip()
file_name = 'File:' + file_name
temp_dir = 'output_from_url'
if file_name.endswith(('.pdf', '.djvu')):
if file_name.endswith(('.pdf')):
print("PDF 파일입니다.\n")
pdf_url = get_file_url(file_name)
image_files = pdf_download_and_convert(
url=pdf_url,
output_dir=temp_dir,
dpi=200 # 해상도 (150~300 권장)
)
if image_files:
while True:
converted_image_detect(image_files)
exit_yes_no = input("이미지 파일 분석을 계속하겠습니까?(y/n)\nenter=y: ").strip()
if exit_yes_no == 'n':
break
for file in image_files:
os.remove(file)
print(f"\n🗑️ 임시 PNG 파일 삭제\n")
else:
print("DJVU 파일입니다.\n")
djvu_url = get_file_url(file_name)
file_path = download_file(djvu_url, 'downloaded.djvu')
image_files = djvu_to_png(file_path, temp_dir)
os.remove(file_path)
print(f"🗑️ 임시 Djuv 파일 삭제: {file_path}\n")
if image_files:
while True:
converted_image_detect(image_files)
exit_yes_no = input("이미지 파일 분석을 계속하겠습니까?(y/n)\nenter=y: ").strip()
if exit_yes_no == 'n':
break
for file in image_files:
os.remove(file)
print(f"\n🗑️ 임시 TIFF 파일 삭제\n")
os.rmdir(temp_dir)
print(f"\n🗑️ 임시 {temp_dir} 폴더 삭제\n")
else:
image_url = get_file_url(file_name)
file_extention = image_url.rsplit('.', 1)[-1]
file_name = 'downloaded.' + file_extention
file_path = download_file(image_url, file_name)
print(f'url: {image_url}\n')
print(f'이미지 파일 분석 중: {image_url}\n')
converted_image_detect(file_path)
os.remove(file_path)
print(f"\n🗑️ 임시 {file_extention} 파일 삭제\n")
if __name__ == '__main__':
main()