Как получить расположение текста в PDF с PDFMiner?
Документация PDFMiner гласит:
PDFMiner позволяет получить точное местоположение текста на странице
Тем не менее, я не смог найти, как это сделать. Документация PDFMiner довольно скудная, поэтому я не понял, как это сделать.
2 ответа
Вы ищете bbox
свойство на каждый объект макета. В документации PDFMiner есть немного информации о том, как анализировать иерархию макетов, но она не охватывает все.
Вот пример:
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox, LTTextLine, LTFigure
def parse_layout(layout):
"""Function to recursively parse the layout tree."""
for lt_obj in layout:
print(lt_obj.__class__.__name__)
print(lt_obj.bbox)
if isinstance(lt_obj, LTTextBox) or isinstance(lt_obj, LTTextLine):
print(lt_obj.get_text())
elif isinstance(lt_obj, LTFigure):
parse_layout(lt_obj) # Recursive
fp = open('example.pdf', 'rb')
parser = PDFParser(fp)
doc = PDFDocument(parser)
rsrcmgr = PDFResourceManager()
laparams = LAParams()
device = PDFPageAggregator(rsrcmgr, laparams=laparams)
interpreter = PDFPageInterpreter(rsrcmgr, device)
for page in PDFPage.create_pages(doc):
interpreter.process_page(page)
layout = device.get_result()
parse_layout(layout)
Если вы заинтересованы в расположении человека LTChar
объекты, вы можете рекурсивно разобрать в дочерние объекты макета LTTextBox
а также LTTextLine
так же, как то, что сделано с LTFigure
в приведенном выше примере.
Полное раскрытие, я являюсь одним из сопровождающих pdfminer.six . Это поддерживаемая сообществом версия pdfminer для Python 3.
В настоящее время pdfminer.six имеет несколько API для извлечения текста и информации из PDF. Для программного извлечения информации я бы посоветовал использовать
extract_pages()
. Это позволяет вам проверять все элементы на странице, упорядоченные в осмысленной иерархии, созданной алгоритмом макета.
Следующий пример представляет собой питонический способ отображения всех элементов в иерархии. Он использует файл simple1.pdf из каталога примеров файла pdfminer.six.
from pathlib import Path
from typing import Iterable, Any
from pdfminer.high_level import extract_pages
def show_ltitem_hierarchy(o: Any, depth=0):
"""Show location and text of LTItem and all its descendants"""
if depth == 0:
print('element x1 y1 x2 y2 text')
print('------------------------------ --- --- --- ---- -----')
print(
f'{get_indented_name(o, depth):<30.30s} '
f'{get_optional_bbox(o)} '
f'{get_optional_text(o)}'
)
if isinstance(o, Iterable):
for i in o:
show_ltitem_hierarchy(i, depth=depth + 1)
def get_indented_name(o: Any, depth: int) -> str:
"""Indented name of LTItem"""
return ' ' * depth + o.__class__.__name__
def get_optional_bbox(o: Any) -> str:
"""Bounding box of LTItem if available, otherwise empty string"""
if hasattr(o, 'bbox'):
return ''.join(f'{i:<4.0f}' for i in o.bbox)
return ''
def get_optional_text(o: Any) -> str:
"""Text of LTItem if available, otherwise empty string"""
if hasattr(o, 'get_text'):
return o.get_text().strip()
return ''
path = Path('~/Downloads/simple1.pdf').expanduser()
pages = extract_pages(path)
show_ltitem_hierarchy(pages)
Вывод показывает различные элементы в иерархии. Ограничительная рамка для каждого. И текст, который содержит этот элемент.
element x1 y1 x2 y2 text
------------------------------ --- --- --- ---- -----
generator
LTPage 0 0 612 792
LTTextBoxHorizontal 100 695 161 719 Hello
LTTextLineHorizontal 100 695 161 719 Hello
LTChar 100 695 117 719 H
LTChar 117 695 131 719 e
LTChar 131 695 136 719 l
LTChar 136 695 141 719 l
LTChar 141 695 155 719 o
LTChar 155 695 161 719
LTAnno
LTTextBoxHorizontal 261 695 324 719 World
LTTextLineHorizontal 261 695 324 719 World
LTChar 261 695 284 719 W
LTChar 284 695 297 719 o
LTChar 297 695 305 719 r
LTChar 305 695 311 719 l
LTChar 311 695 324 719 d
LTAnno
LTTextBoxHorizontal 100 595 161 619 Hello
LTTextLineHorizontal 100 595 161 619 Hello
LTChar 100 595 117 619 H
LTChar 117 595 131 619 e
LTChar 131 595 136 619 l
LTChar 136 595 141 619 l
LTChar 141 595 155 619 o
LTChar 155 595 161 619
LTAnno
LTTextBoxHorizontal 261 595 324 619 World
LTTextLineHorizontal 261 595 324 619 World
LTChar 261 595 284 619 W
LTChar 284 595 297 619 o
LTChar 297 595 305 619 r
LTChar 305 595 311 619 l
LTChar 311 595 324 619 d
LTAnno
LTTextBoxHorizontal 100 495 211 519 H e l l o
LTTextLineHorizontal 100 495 211 519 H e l l o
LTChar 100 495 117 519 H
LTAnno
LTChar 127 495 141 519 e
LTAnno
LTChar 151 495 156 519 l
LTAnno
LTChar 166 495 171 519 l
LTAnno
LTChar 181 495 195 519 o
LTAnno
LTChar 205 495 211 519
LTAnno
LTTextBoxHorizontal 321 495 424 519 W o r l d
LTTextLineHorizontal 321 495 424 519 W o r l d
LTChar 321 495 344 519 W
LTAnno
LTChar 354 495 367 519 o
LTAnno
LTChar 377 495 385 519 r
LTAnno
LTChar 395 495 401 519 l
LTAnno
LTChar 411 495 424 519 d
LTAnno
LTTextBoxHorizontal 100 395 211 419 H e l l o
LTTextLineHorizontal 100 395 211 419 H e l l o
LTChar 100 395 117 419 H
LTAnno
LTChar 127 395 141 419 e
LTAnno
LTChar 151 395 156 419 l
LTAnno
LTChar 166 395 171 419 l
LTAnno
LTChar 181 395 195 419 o
LTAnno
LTChar 205 395 211 419
LTAnno
LTTextBoxHorizontal 321 395 424 419 W o r l d
LTTextLineHorizontal 321 395 424 419 W o r l d
LTChar 321 395 344 419 W
LTAnno
LTChar 354 395 367 419 o
LTAnno
LTChar 377 395 385 419 r
LTAnno
LTChar 395 395 401 419 l
LTAnno
LTChar 410 395 424 419 d
LTAnno
(Аналогичный ответ и вопрос здесь , здесь и здесь , я постараюсь синхронизировать их.)