Убедитесь, что цикл проходит через каждый файл, даже если возникают ошибки

#python #exception #error-handling #pdfminer #pdf-parsing

Вопрос:

Я перебираю кучу PDF — файлов в папке, анализирую их содержимое и добавляю его в список. Он работает с подмножеством pdf-файлов. Я не хочу вручную удалять некоторые файлы PDF, запускать код, а затем добавлять некоторые, чтобы запустить его снова, пока я не найду неисправные PDF-файлы. Поскольку некоторые файлы pdf не могут быть открыты или, возможно, содержимое повреждено, я сделал следующее, чтобы обеспечить выполнение цикла: check_extractable (pdfminer должен выдавать ошибку, если файл pdf не извлекается) — это метод внутреннего класса (PDFTextExtractionNotAllowed), который может предотвратить попытку открыть файл PDF, который на самом деле не может быть открыт

Вопрос: Что мне нужно сделать, чтобы код продолжал работать, даже если файл pdf не может быть открыт или не содержит содержимого (предполагая, что именно поэтому ошибка возникает в этой конкретной точке кода)

 import pdfminer
from pdfminer.pdfpage import PDFPage, PDFTextExtractionNotAllowed
import os
import io
from io import StringIO
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter, PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox, LTFigure, LTImage, 
LTTextLine, LTTextContainer, LTChar, LTTextBoxHorizontal
from pdfminer.pdfpage import PDFPage, PDFTextExtractionNotAllowed
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfparser import PDFParser, PDFSyntaxError

directory = 'C:/Users/'
data = []
for file in os.listdir(directory):
    if not file.endswith(".pdf"):
        continue
    fake_file_handle = io.StringIO()


    with open(os.path.join(directory, file), 'rb') as fh:
        resource_manager = PDFResourceManager()
        laparams = LAParams(line_margin = 0.6)
        device = PDFPageAggregator(resource_manager, laparams = laparams)
        page_interpreter = PDFPageInterpreter(resource_manager, device)

        positions = []
        raw_text = []
        for page in PDFPage.get_pages(fh, caching=True, check_extractable=True):
            page_interpreter.process_page(page)
            text = fake_file_handle.getvalue()
            layout = device.get_result()
            for lobj in layout:
                
                if isinstance(lobj, LTTextContainer) or isinstance(lobj, LTTextBox) or isinstance(lobj, pdfminer.layout.LTTextBoxHorizontal):
                    coord, word = int(lobj.bbox[1]), lobj.get_text().strip()
                    raw_text.append([coord, word])

                    for text_line in lobj:
                        for character in text_line:
                            if isinstance(character, LTChar):
                                if character.matrix[0]>0 :
                                    position = character.bbox 
                        positions.append(position)

                # if it's a container, recurse
                elif isinstance(lobj, LTFigure):
                    pass

        # extract elements below y0=781 und above y0=57
        text_pos = []
        maxFontpos = 780
        minFontpos = 58
        for coord, word in raw_text:
            if coord <= maxFontpos and coord >= minFontpos:
                text_pos.append(word)
            else:
                pass
 
        try:
            wap = text_pos[0]
        except:
            pass
        
    data.append([text_pos, wap])
    fake_file_handle.close()
 

Конкретная ошибка возникает при

 ---> 28                         for character in text_line:
     29                             if isinstance(character, LTChar):
     30                                 if character.matrix[0]>0 :

TypeError: 'LTChar' object is not iterable
 

Комментарии:

1. Если вам все равно, почему некоторые файлы не работают — просто оберните неисправную часть в try .

2. Вы проверяете if isinstance(lobj, LTTextContainer) , но это также будет успешным, если lobj это a LTTextLine , и в этом случае оно содержит только символы. (см. github.com/pdfminer/pdfminer.six/blob/… )

3. Леонард, твой ответ заставил меня полюбопытствовать, почему он терпит неудачу. Я все еще оцениваю последствия, если он содержит только символы. В конце концов, это то, что я анализирую и добавляю. Кроме того, я сделал то, что сказал Гаррит, и теперь он возвращает все данные файлов 1995 года, за исключением 2 файлов

Ответ №1:

Если это просто быстрый и грязный скрипт, я бы рекомендовал просто окружить весь with блок в общем try / except . Как правило, вы не хотите просто слепо исключать/перехватывать исключения, не указывая, какой тип вы ищете, в случае возникновения другого исключения/ошибки, которых вы не ожидали, но в подобной ситуации, я думаю, это было бы нормально:

 from pdfminer.pdfpage import PDFPage, PDFTextExtractionNotAllowed

directory = 'C:/Users/'
data = []
for file in os.listdir(directory):
    if not file.endswith(".pdf"):
        continue
    fake_file_handle = io.StringIO()

    try:
        with open(os.path.join(directory, file), 'rb') as fh:
            resource_manager = PDFResourceManager()
            laparams = LAParams(line_margin = 0.6)
            device = PDFPageAggregator(resource_manager, laparams = laparams)
            page_interpreter = PDFPageInterpreter(resource_manager, device)

            positions = []
            raw_text = []
            for page in PDFPage.get_pages(fh, caching=True, check_extractable=True):
                page_interpreter.process_page(page)
                text = fake_file_handle.getvalue()
                layout = device.get_result()
                for lobj in layout:
                
                    if isinstance(lobj, LTTextContainer) or isinstance(lobj, LTTextBox) or isinstance(lobj, pdfminer.layout.LTTextBoxHorizontal):
                        coord, word = int(lobj.bbox[1]), lobj.get_text().strip()
                        raw_text.append([coord, word])

                        for text_line in lobj:
                            for character in text_line:
                                if isinstance(character, LTChar):
                                    if character.matrix[0]>0 :
                                        position = character.bbox  # font-positon
                            positions.append(position)

                    # if it's a container, recurse
                    elif isinstance(lobj, LTFigure):
                        pass

            # extract elements below y0=781 und above y0=57
            text_pos = []
            maxFontpos = 780
            minFontpos = 58
            for coord, word in raw_text:
                if coord <= maxFontpos and coord >= minFontpos:
                    text_pos.append(word)
                else:
                    pass
 
            try:
                wap = text_pos[0]
            except:
                pass
    except:
        continue # Move on to next loop iteration

    data.append([text_pos, wap])
    fake_file_handle.close()