PDF에서 문자 정보를 추출하려면 PDFMiner 선택

PDF에서 문자 정보를 추출하기 위해



환경 구축



Dockerfile
FROM python:3.6
ENV LC_ALL C.UTF-8
ENV LANG C.UTF-8  
RUN apt-get -y update && \
    apt-get install -y --fix-missing \
    build-essential \
    software-properties-common \
    poppler-utils && \
    apt-get clean && \
    rm -rf /tmp/* /var/tmp/* && \
    mkdir /api
WORKDIR /api
COPY requirements.txt /api/requirements.txt
RUN pip3 install --upgrade pip && \
    pip3 install --upgrade -r requirements.txt
EXPOSE 8888
ENTRYPOINT jupyter notebook --ip=0.0.0.0 --allow-root --no-browser

requirements.txt
pandas==0.24.2
pillow==7.0.0
opencv-python==3.4.2.16
pdfminer==20191125
jupyter==1.0.0
$ docker build -t pdfminer -f ./Dockerfile .
$ docker run -it -v `pwd`:/api -p 8888:8888 --name pdfminer pdfminer bash

PDF에서 문자 정보 추출



컨테이너 생성에 성공하면 자동으로 Jupiter가 시작되므로 파이썬 파일을 만듭니다.
아래 설정은 최소한의 문자 정보를 추출하여 텍스트 파일에 저장하는 코드입니다.
이번에는 금융청의 PDF를 test.pdf로 했습니다.
htps //w w. f. . jp / 네 ws / 30 / wp / 슈퍼 r ゔ ぃ ry_ 앗 p pdf

test.py
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTContainer, LTTextBox, LTTextLine, LTChar
from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager
from pdfminer.pdfpage import PDFPage

def pdfminer_config(line_overlap, word_margin, char_margin,line_margin, detect_vertical):
    laparams = LAParams(line_overlap=line_overlap,
                        word_margin=word_margin,
                        char_margin=char_margin,
                        line_margin=line_margin,
                        detect_vertical=detect_vertical)
    resource_manager = PDFResourceManager()
    device = PDFPageAggregator(resource_manager, laparams=laparams)
    interpreter = PDFPageInterpreter(resource_manager, device)
    return (interpreter, device)

def find_textboxes(layout_obj):
    if isinstance(layout_obj, LTTextBox):
        return [layout_obj]
    if isinstance(layout_obj, LTContainer):
        boxes = []
        for child in layout_obj:
            boxes.extend(find_textboxes(child))
        return boxes
    return []

def find_textlines(layout_obj):
    if isinstance(layout_obj, LTTextLine):
        return [layout_obj]
    if isinstance(layout_obj, LTTextBox):
        lines = []
        for child in layout_obj:
            lines.extend(find_textlines(child))
        return lines
    return []

def find_characters(layout_obj):
    if isinstance(layout_obj, LTChar):
        return [layout_obj]
    if isinstance(layout_obj, LTTextLine):
        characters = []
        for child in layout_obj:
            characters.extend(find_characters(child))
        return characters
    return []

def write_text(text_file, text):
    text_file.write(text)

text_file = open('output.txt', 'w')
with open("./test.pdf", 'rb') as f:
    interpreter, device = pdfminer_config(line_overlap=0.5, word_margin=0.1, char_margin=2, line_margin=0.5, detect_vertical=True)
    for page in PDFPage.get_pages(f):
        interpreter.process_page(page)  # ページを処理する。
        layout = device.get_result()  # LTPageオブジェクトを取得。
        boxes = find_textboxes(layout)
        for box in boxes:
            write_text(text_file, box.get_text().strip())

text_file.close()

laparams에 의한 조정



텍스트가 생각대로 검색할 수 없는 경우 laparams 매개 변수를 조정합니다. char_margin, word_margin, line_margin을 변경하면 그룹화되는 문자가 변경됩니다.
detect_vertivcal은 일본어처럼 세로 쓰기 문장이 있으면 True로 설정합니다.

test.py
interpreter, device = pdfminer_config(line_overlap=0.5, word_margin=0.1, char_margin=2.0, line_margin=0.5, detect_vertical=False)



boxes 내용



위의 코드에서 얻은 boxes에는 다양한 정보가 담겨 있습니다.
  • 문자 정보
  • 문자의 위치 정보 (단위는 pt이므로 opencv 등으로 가공하는 경우는 pt에서 pixel로의 단위 변환이 필요)
  • print(boxes[0])
    # >> <LTTextBoxHorizontal(0) 92.160,755.000,524.296,766.952 'かし、従来は金融庁の国際部門は国際規制の導入負担ができるだけ小さくなるよう交\n'>
    print(boxes[0].get_text())
    # >> かし、従来は金融庁の国際部門は国際規制の導入負担ができるだけ小さくなるよう交
    print(boxes[0].bbox)
    # >> (92.15997480600001, 754.9998879965001, 524.2961793060001, 766.9523361965001)
    # >> タプルの中は(x0, y0, x1, y1)の順になっており、示す位置は画像のようになっています。
    



    lines의 내용



    일부 박스에서는 LTTextLine이 목록으로 나열됩니다. 따라서 이전 코드에서 사용하지 않은 find_textline을 사용하여 LTTextLine을 얻으십시오.

    test.py
    lines = find_textlines(boxes[0])
    print(lines[0])
    # >><LTTextLineHorizontal 92.160,755.000,524.296,766.952 'かし、従来は金融庁の国際部門は国際規制の導入負担ができるだけ小さくなるよう交\n'>
    print(lines[0].get_text())
    # >> かし、従来は金融庁の国際部門は国際規制の導入負担ができるだけ小さくなるよう交
    print(lines[0].bbox)
    # >> (92.15997480600001, 754.9998879965001, 524.2961793060001, 766.9523361965001)
    

    characters 내용



    또한 일부 라인에서는 LTChar가 목록으로 나란히 있습니다. 그 중에는 문자 정보와 위치 정보 외에도 font 등도 막혀 있습니다.

    test.py
    characters = find_characters(lines[0])
    print(characters[0])
    # >><LTChar 92.160,755.000,104.160,766.952 matrix=[12.00,0.00,0.00,12.00, (92.16,756.68)] font='AHTYXM+MS-PGothic' adv=1.0 text='か'>
    print(characters[0].get_text())
    # >> か
    print(characters[0].bbox)
    # >> (92.15997480600001, 754.9998879965001, 104.16042480600001, 766.9523361965001)
    

    시간이 생기면, 취득할 수 있던 부분의 색을 바꾸는 등도 소개하고 싶습니다.

    좋은 웹페이지 즐겨찾기