Разбор PDF без /Root объекта с использованием PDFMiner

Я пытаюсь извлечь текст из большого количества PDF-файлов, используя привязки Python PDFMiner. Модуль, который я написал, работает для многих PDF-файлов, но я получаю эту несколько загадочную ошибку для подмножества PDF-файлов:

трассировка стека ipython:

/usr/lib/python2.7/dist-packages/pdfminer/pdfparser.pyc in set_parser(self, parser)
    331                 break
    332         else:
--> 333             raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
    334         if self.catalog.get('Type') is not LITERAL_CATALOG:
    335             if STRICT:

PDFSyntaxError: No /Root object! - Is this really a PDF?

Конечно, я сразу же проверил, не повреждены ли эти PDF-файлы, но их можно прочитать очень хорошо.

Есть ли способ прочитать эти PDF-файлы, несмотря на отсутствие корневого объекта? Я не слишком уверен, куда идти отсюда.

Большое спасибо!

Редактировать:

Я попытался использовать PyPDF в попытке получить некоторую дифференциальную диагностику. Трассировка стека ниже:

In [50]: pdf = pyPdf.PdfFileReader(file(fail, "rb"))
---------------------------------------------------------------------------
PdfReadError                              Traceback (most recent call last)
/home/louist/Desktop/pdfs/indir/<ipython-input-50-b7171105c81f> in <module>()
----> 1 pdf = pyPdf.PdfFileReader(file(fail, "rb"))

/usr/lib/pymodules/python2.7/pyPdf/pdf.pyc in __init__(self, stream)
    372         self.flattenedPages = None
    373         self.resolvedObjects = {}
--> 374         self.read(stream)
    375         self.stream = stream
    376         self._override_encryption = False

/usr/lib/pymodules/python2.7/pyPdf/pdf.pyc in read(self, stream)
    708             line = self.readNextEndLine(stream)
    709         if line[:5] != "%%EOF":
--> 710             raise utils.PdfReadError, "EOF marker not found"
    711 
    712         # find startxref entry - the location of the xref table


PdfReadError: EOF marker not found

Quonux предположил, что, возможно, PDFMiner прекратил синтаксический анализ после достижения первого символа EOF. Казалось бы, можно предположить иное, но я очень невежественен. Какие-нибудь мысли?

4 ответа

Решение

Интересная проблема. я провел какое-то исследование:

функция, которая анализировала pdf (из исходного кода майнера):

def set_parser(self, parser):
        "Set the document to use a given PDFParser object."
        if self._parser: return
        self._parser = parser
        # Retrieve the information of each header that was appended
        # (maybe multiple times) at the end of the document.
        self.xrefs = parser.read_xref()
        for xref in self.xrefs:
            trailer = xref.get_trailer()
            if not trailer: continue
            # If there's an encryption info, remember it.
            if 'Encrypt' in trailer:
                #assert not self.encryption
                self.encryption = (list_value(trailer['ID']),
                                   dict_value(trailer['Encrypt']))
            if 'Info' in trailer:
                self.info.append(dict_value(trailer['Info']))
            if 'Root' in trailer:
                #  Every PDF file must have exactly one /Root dictionary.
                self.catalog = dict_value(trailer['Root'])
                break
        else:
            raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
        if self.catalog.get('Type') is not LITERAL_CATALOG:
            if STRICT:
                raise PDFSyntaxError('Catalog not found!')
        return

если у вас возникнут проблемы с EOF, возникнет еще одно исключение: '''другая функция из источника' ''

def load(self, parser, debug=0):
        while 1:
            try:
                (pos, line) = parser.nextline()
                if not line.strip(): continue
            except PSEOF:
                raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
            if not line:
                raise PDFNoValidXRef('Premature eof: %r' % parser)
            if line.startswith('trailer'):
                parser.seek(pos)
                break
            f = line.strip().split(' ')
            if len(f) != 2:
                raise PDFNoValidXRef('Trailer not found: %r: line=%r' % (parser, line))
            try:
                (start, nobjs) = map(long, f)
            except ValueError:
                raise PDFNoValidXRef('Invalid line: %r: line=%r' % (parser, line))
            for objid in xrange(start, start+nobjs):
                try:
                    (_, line) = parser.nextline()
                except PSEOF:
                    raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
                f = line.strip().split(' ')
                if len(f) != 3:
                    raise PDFNoValidXRef('Invalid XRef format: %r, line=%r' % (parser, line))
                (pos, genno, use) = f
                if use != 'n': continue
                self.offsets[objid] = (int(genno), long(pos))
        if 1 <= debug:
            print >>sys.stderr, 'xref objects:', self.offsets
        self.load_trailer(parser)
        return

из вики (спецификации pdf): PDF-файл состоит в основном из объектов, которых существует восемь типов:

Boolean values, representing true or false
Numbers
Strings
Names
Arrays, ordered collections of objects
Dictionaries, collections of objects indexed by Names
Streams, usually containing large amounts of data
The null object

Объекты могут быть прямыми (встроенными в другой объект) или косвенными. Косвенные объекты нумеруются номером объекта и номером поколения. Индексная таблица, называемая таблицей внешних ссылок, дает смещение байтов каждого косвенного объекта от начала файла. Такая конструкция обеспечивает эффективный произвольный доступ к объектам в файле, а также позволяет вносить небольшие изменения без перезаписи всего файла (инкрементное обновление). Начиная с PDF версии 1.5, косвенные объекты могут также находиться в специальных потоках, известных как потоки объектов. Этот метод уменьшает размер файлов с большим количеством мелких косвенных объектов и особенно полезен для PDF с тегами.

Я думаю, проблема в том, что у вашего "поврежденного pdf" есть несколько "корневых элементов" на странице.

Possible solution:

Вы можете скачать исходники и написать `функцию печати 'в каждом месте, где извлекаются внешние объекты и где анализатор пытался проанализировать эти объекты. можно будет определить полный стек ошибок (до появления этой ошибки).

PS: я думаю, что это какая-то ошибка в продукте.

Решение в slate pdf - использовать 'rb' -> чтение в двоичном режиме.

Поскольку формат PDF зависит от PDFMiner, и у меня та же проблема, это должно решить вашу проблему.

fp = open('C:\Users\USER\workspace\slate_minner\document1.pdf','rb')
doc = slate.PDF(fp)
print doc

У меня была такая же проблема в Ubuntu. У меня очень простое решение. Просто распечатайте PDF-файл в формате PDF. Если вы находитесь в Ubuntu:

  1. Откройте файл PDF с помощью (ubuntu) средства просмотра документов.

  2. Перейти к файлу

  3. Перейти к печати

  4. Выберите печать как файл и отметьте галочкой "pdf"

Если вы хотите сделать процесс автоматическим, следуйте, например, этому, т. Е. Используйте этот скрипт для автоматической печати всех ваших PDF-файлов. Подобный сценарий Linux также работает:

for f in *.pdfx
do
lowriter --headless --convert-to pdf "$f"
done

Обратите внимание, что я назвал оригинальные (проблемные) файлы PDF как PDFX.

Я также получил эту ошибку и продолжал пробовать fp = open('example','rb')

Тем не менее, я все еще получил сообщение об ошибке. Что я обнаружил, так это то, что в моем коде была ошибка, когда PDF все еще был открыт другой функцией.
Поэтому убедитесь, что у вас нет открытых файлов PDF в других местах.

Ответ выше правильный. Эта ошибка появляется только в Windows, и обходной путь должен заменить with open(path, 'rb') вfp = open(path,'rb')

Другие вопросы по тегам