Извлечение текста из файлов PDF в 1С

07.08.24

Разработка - Инструментарий разработчика

В данной статье я расскажу, как извлечь текст из документа в формате PDF для его дальнейшей обработки в 1С.

Подготовка скрипта на Python

Для решения задачи извлечения текста из PDF файла был использован движок распознавания Google Tesseract OCR. Ссылка на статью, которой я руководствовался при написании скрипта на python: Извлечение текста из файлов PDF при помощи Python. Повторяться не буду, так как она довольно подробная и понятная.

Итоговый код, получившийся на python:

import PyPDF2
from pdfminer.high_level import extract_pages, extract_text
from pdfminer.layout import LTTextContainer, LTChar, LTRect, LTFigure, LTImage
from PIL import Image
import pdfplumber
from pdf2image import convert_from_path
import pytesseract
import os
import argparse

pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

def text_extraction(element):
    line_text=element.get_text()

    line_format = []
    for text_line in element:
        if isinstance(text_line, LTTextContainer):
            for character in text_line:
                if isinstance(character, LTChar):
                    line_format.append(character.fontname)
                    line_format.append(character.size)
    format_per_line = list(set(line_format))
    return (line_text, format_per_line)

def crop_image(element, pageIMAGE):
    [list_x0,list_y0,list_x1,list_y1]=[element.x0,element.y0,element.x1,element.y1]
    pageIMAGE.mediabox.lower_left=(list_x1,list_y0)
    pageIMAGE.mediabox.right=(list_x0,list_y1)

    cropped_pdf_writer=PyPDF2.PdfWriter()
    cropped_pdf_writer.add_page(pageIMAGE)

    with open('cropped_image.pdf','wb') as cropped_pdf_file:
        cropped_pdf_writer.write(cropped_pdf_file)

def convert_to_Image(input_file,):
    images=convert_from_path(input_file, poppler_path=r'poppler-24.07.0\Library\bin')

    image=images[0]

    output_file="PDF_Image.png"
    image.save(output_file, format='PNG')

def image_to_text(image_path):
    image=Image.open(image_path)

    text=pytesseract.image_to_string(image)

    return text

def extract_table(pdf,page_number,table_number):
    pdf=pdfplumber.open(pdf)

    table_page=pdf.pages[page_number]
    table=table_page.extract_tables()[table_number]
    return table

def table_converter(table):
    table_string = ''
    # Итеративно обходим каждую строку в таблице
    for row_num in range(len(table)):
        row = table[row_num]
        # Удаляем разрыв строки из текста с переносом
        cleaned_row = [item.replace('\n', ' ') if item is not None and '\n' in item else 'None' if item is None else item for item in row]
        # Преобразуем таблицу в строку
        table_string+=('|'+'|'.join(cleaned_row)+'|'+'\n')
    # Удаляем последний разрыв строки
    table_string = table_string[:-1]
    return table_string

def main():
    parser = argparse.ArgumentParser(description="Конвертация pdf УПД в Excel")
    parser.add_argument("input_file", type = str, help = "Путь к входному файлу")

    args = parser.parse_args()

    #pdf_path = ('upd2.pdf')
    pdf_path = (args.input_file)
    pdfFile = open(pdf_path, 'rb')

    pdfReader = PyPDF2.PdfReader(pdfFile)

    text_per_page = {}

    for pagenum, page in enumerate(extract_pages(pdf_path)):

        pageIMAGE = pdfReader.pages[pagenum]
        page_text = []
        line_format = []
        text_from_Image = []
        text_from_Table = []
        page_content = []

        table_number = 0

        first_element = True
        table_extraction_flag = False

        pdf = pdfplumber.open(pdf_path)
        page_tables = pdf.pages[pagenum]
        tables = page_tables.find_tables()

        page_elements = [(element.y1, element) for element in page._objs]
        page_elements.sort(key=lambda a: a[0], reverse=True)

        for i, component in enumerate(page_elements):
            pos = component[0]
            element = component[1]

            if isinstance(element, LTTextContainer):

                if (table_extraction_flag == False):
                    (line_text, format_per_line) = text_extraction(element)
                    page_text.append(line_text)
                    line_format.append(format_per_line)
                    page_content.append(line_text)


                else:
                    pass

            if isinstance(element, LTFigure):
                crop_image(element, pageIMAGE)
                # Преобразуем обрезанный pdf в изображение
                convert_to_Image('cropped_image.pdf')
                # Извлекаем текст из изображения
                image_text = image_to_text('PDF_image.png')
                text_from_Image.append(image_text)
                page_content.append(image_text)
                # Добавляем условное обозначение в списки текста и формата
                page_text.append('image')
                line_format.append('image')

            if isinstance(element, LTRect):

                if (first_element == True and (table_number + 1) <= len(tables)):
                    lower_side = page.bbox[3] - tables[table_number].bbox[3]
                    upper_side = element.y1

                    table = extract_table(pdfFile, pagenum, table_number)

                    table_string = table_converter(table)

                    text_from_Table.append(table_string)
                    page_content.append(table_string)

                    table_element = False

                    page_text.append('table')
                    line_format.append('table')

                if (element.y0 >= lower_side) and (element.y1 <= upper_side):

                    pass

                elif not isinstance(page_elements[i + 1][1], LTRect):

                    table_extraction_flag = False

                    first_element = True

                    table_number += 1

        dctkey = 'Page_' + str(pagenum)
        text_per_page[dctkey] = [page_text, line_format, text_from_Image, text_from_Table, page_content]

    pdfFile.close()

    if os.path.exists('cropped_image.pdf'):
        os.remove('cropped_image.pdf')

    if os.path.exists('PDF_image.png'):
        os.remove('PDF_image.png')

    result = ''.join(text_per_page['Page_0'][4])
    file = open("test.txt", "w")
    file.write(result)
    file.close()

if __name__ == "__main__":
    main()


Выделю следующие строки:

import argparse

Здесь я импортирую модуль argparse, который предназначен для работы с аргументами командной строки.
 

parser = argparse.ArgumentParser(description="Конвертация pdf УПД в Excel")
parser.add_argument("input_file", type = str, help = "Путь к входному файлу")
args = parser.parse_args()
pdf_path = (args.input_file)

Здесь я инициализирую аргумент "Путь к входному файлу" и прокидываю его в качестве пути к pdf файлу.

Результат работы скрипта является строкой. Эту строку я записываю в обычный текстовый файл.

После написания скрипта на python необходимо скомпилировать скрип в exe файл.

Для этого необходимо установить pyinstaller.

Прописываем в командной строке

pip install pyinstaller
cd путь/к/вашему/скрипту
pyinstaller --onefile script.py

Параметр --onefile указывает PyInstaller упаковать все файлы в один исполняемый файл.

После выполнения команды PyInstaller, в директории вашего проекта появятся несколько новых папок и файлов:

build – временная папка для файлов сборки.

dist – папка, в которой находится ваш .exe файл.

.spec файл – спецификация сборки, которую можно использовать для дальнейших настроек.

Перейдите в папку dist и найдите там файл script.exe. Это и есть ваш исполняемый файл.
 

Интеграция с 1С

	ТабличныйДокумент = Новый ТабличныйДокумент;
	
	Путь = СтрЗаменить(ПутьКPDFФайлу, "\", "\\");    
	МассивПапок = СтрРазделить(ПутьКИсполняемомуФайлу, "\", Ложь);  
	ПутьКТекстовомуФайлу = ""; 
	
	Для Счетчик = 0 По МассивПапок.Количество() - 2 Цикл
		
		  ПутьКТекстовомуФайлу = ПутьКТекстовомуФайлу + МассивПапок[Счетчик] + "\";
		
	КонецЦикла;
	
	КомандаСистемы("main.exe " + Путь, ПутьКТекстовомуФайлу); 
	
	Сек = 5;
	КонДата = ТекущаяДата() + сек;
	Пока ТекущаяДата() < КонДата Цикл
		// ждемссс....
	КонецЦикла; 
	
	ТекстовыйФайл = Новый ТекстовыйДокумент;
		
	ТекстовыйФайл.Прочитать(ПутьКТекстовомуФайлу + "test.txt",
	КодировкаТекста.Системная, 
	Символы.ПС
	);
	
	ТекстФайла = ТекстовыйФайл.ПолучитьТекст(); 
	КонвертироватьНаСервере(ТабличныйДокумент, ТекстФайла); 
	
	ТабличныйДокумент.Записать(ПутьКExcelФайлу, ТипФайлаТабличногоДокумента.XLSX);  
	ЗапуститьПриложение(ПутьКExcelФайлу);

Теперь этот скрипт необходимо запустить из 1С.

В данном примере я считываю текстовый файл, созданный скриптом на python, заполняю с помощью него табличный документ, записываю этот табличный документ в excel и открываю его.

Скрипт запускает следующая строка:

КомандаСистемы("main.exe " + Путь, ПутьКТекстовомуФайлу); 

Она принимает в качестве первого аргумента имя exe-файла вместе с аргументами (в нашем случае это путь к pdf файлу), а в качестве второго аргумента - каталог, в котором находится exe.

Переменные ПутьКPDFФайлу и ПутьКИсполняемомуФайлу задаются на форме.

При этом в пути к pdf файлу необходимо экранировать символ "\", поэтому заменяем его на "\\".

Далее ждём 5 секунд, чтобы исполняемый файл точно успел выполнить свою функцию и производим считывание текстового файла.

Сам текстовый файл уже парсим как душе угодно через СтрРазделить() или СтрНайти().

В моем примере я считывал документ УПД и вот, что я получил на выходе:

И разделив его следующим образом

МассивСтрок = СтрРазделить(ТекстФайла, Символы.ПС, Ложь);

Получил массив, с которым уже вполне можно работать.


Спасибо за внимание! Если остались вопросы - жду в комментариях.

Обработка PDF python парсинг.

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    150210    832    393    

839

Инструментарий разработчика Программист Платформа 1С v8.3 Конфигурации 1cv8 Платные (руб)

Инструмент для написания и отладки кода в режиме «1С:Предприятие». Представляет собой консоль кода с возможностью пошаговой отладки, просмотра значений переменных любых типов, использования процедур и функций, просмотра стека вызовов, вычисления произвольных выражений на встроенном языке в контексте точки останова, синтаксического контроля и остановки по ошибке. В консоли используется удобный редактор кода с подсветкой, контекстной подсказкой, возможностью вызова конструкторов запроса и форматной строки.

9360 руб.

17.05.2024    19981    58    42    

105

Инструментарий разработчика Программист Платформа 1С v8.3 Платные (руб)

OneRPA - платформа роботизации рутинных операций, в основе которой лежит платформа 1С:Предприятие. Данная платформа позволяет использовать современные технологии роботизации, не теряя при этом наработки, которые были созданы в ходе автоматизации. При этом платформа роботизации сохраняет все преимущества платформы 1С: гибкость, кроссплатформенность, мобильный и Web доступ, простоту расширения. Ну и конечно же роботы создаются и сопровождаются простыми специалистами 1С, при этом не обязательно программистами. Self hosted версия

300000 руб.

03.03.2021    14110    12    27    

36

Пакетная печать Печатные формы Инструментарий разработчика Программист Платформа 1С v8.3 Запросы 1С:Зарплата и кадры бюджетного учреждения 1С:Конвертация данных 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 Платные (руб)

Инструмент, позволяющий абсолютно по-новому взглянуть на процесс разработки печатных форм. Благодаря конструктору можно значительно снизить затраты времени на разработку печатных форм, повысить качество и "прозрачность" разработки, а также навести порядок в многообразии корпоративных печатных форм.

18000 руб.

06.10.2023    13800    35    7    

68

Инструменты администратора БД Инструментарий разработчика Роли и права Программист Платформа 1С v8.3 Конфигурации 1cv8 Россия Платные (руб)

Расширение позволяет без изменения кода конфигурации выполнять проверки при вводе данных, скрывать от пользователя недоступные ему данные, выполнять код в обработчиках. Не изменяет данные конфигурации, легко устанавливается практически на любую конфигурацию на управляемых формах.

10000 руб.

10.11.2023    8917    32    10    

55

Инструментарий разработчика Чистка данных Свертка базы Инструменты администратора БД Платформа 1С v8.3 Россия Платные (руб)

Инструмент представляет собой обработку для проведения свёртки или обрезки баз данных. Работает на ЛЮБЫХ конфигурациях. Поддерживаются управляемые и обычные формы. Может выполнять свертку сразу нескольких баз данных и выполнять их автоматически без непосредственного участия пользователя.

6900 руб.

20.08.2024    3283    18    6    

32

SALE! %

Инструментарий разработчика Инструменты администратора БД Системный администратор Программист Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Россия Платные (руб)

Универсальный инструмент программиста для администрирования конфигураций. Сборник наиболее часто используемых обработок под единым интерфейсом.

4800 3840 руб.

14.01.2013    184759    1126    0    

898

Инструментарий разработчика Программист 8.3.14 1С:Конвертация данных Россия Платные (руб)

Расширение для конфигурации “Конвертация данных 3”. Добавляет подсветку синтаксиса, детальную контекстную подсказку, глобальный поиск по коду.

15000 руб.

07.10.2021    16662    6    29    

42
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. VVladislav 07.08.24 15:12 Сейчас в теме
ого, бесплатный скрипт для срипткидди. благодарю.
извините, а Маин.эхе не может вернуть код возврата? посидели бы, подождали.
ну и цикл жрёт ресурсы. если совсем никак по другому, то ожидание через обработчик проще делать, и там уже сравнивать текущее время с целевым.
2. siamagic 08.08.24 02:29 Сейчас в теме
(1) Tcnm ЗапуститьПриложение, ожидание в цикле трешак конечно.
3. siamagic 08.08.24 02:31 Сейчас в теме
pdf2text распознает лучше, в идеале неиронку прикрутить. Думаю таблица у вас не будет парсится - в чем смысл тогда этих движений?
4. user1201514 09.08.24 15:21 Сейчас в теме
(3)
pdf2text
Тоже перепробовал много, но остановился на pdf2text, работает намного быстрее и тем более что работает и с linux.
Оставьте свое сообщение