Игра Яндекс Практикума
Игра Яндекс Практикума
Игра Яндекс Практикума

Основные задачи парсинга PDF

Отредактировано

Рассмотрим задачи парсинга PDF: разбиение документа на страницы и их сохранение; объединение листов в один; извлечение текста и таблиц.

13К открытий14К показов

В этой статье мы выполним задачи парсинга PDF:

  1. Из файла PDF удалить страницы с таблицами.
  2. Страницы без таблиц сохранить отдельными файлами pdf, прибавив к названию основного файла суффикс «_n», где n — порядковый номер страницы.
  3. Страницы без таблиц объединить в один файл pdf с названием основного файла + ‘ no tables’.
  4. Из страниц с таблицами извлечь таблицы и сохранить в файл csv/xlsx c названием «table-n», где n – порядковый номер таблицы.

Для выполнения задания пока потребуются такие модули: pdfplumber — для извлечения таблиц и pdfrw — для чтения/записи файлов pdf. Эти модули не входят в стандартную библиотеку Python, по этому их нужно устанавливать.

Установить их можно командами: pip install pdfrw, pip install pdfplumber.

Работа с библиотекой pdfrw

Чтение документа

			from pdfrw import PdfReader

path_pdf = 'path/to/file.pdf'
x = PdfReader(path_pdf)
print(len(x.pages))
		

Всё просто. Импорт подкласса PdfReader, создание его объекта, который читает файл, атрибут pages возвращает список всех страниц в документе.

Запись одной страницы

			# запись выбранной страницы
from pdfrw import PdfWriter

y = PdfWriter()
y.addpage(x.pages[0])
y.write('result1.pdf')
		

Также всё просто. Объекту подкласса PdfWriter Y передаём через метод .addpage прочитанную объектом X нужную нам страницу для записи, метод .write делает запись.

Запись нескольких страниц в один файл

			# запись нескольких страниц в одну
from pdfrw import PdfWriter

y = PdfWriter()
y.addpage(x.pages[0])
y.addpage(x.pages[2])
y.write('result2.pdf')
		

Думаю, тут комментарий не нужен.

Здесь приведены примеры только тех методов и атрибутов, которые нужны для выполнения задания. Для более глубокого изучения ссылки на документации: https://pypi.org/project/pdfrw, https://pypi.org/project/pdfplumber/.

Работа с модулем pdfplumber

			import pdfplumber

path_pdf = 'path/to/file.pdf'
with pdfplumber.open(path_pdf) as pdf: 
    print(pdf.pages)
    print()
    page = pdf.pages[2]
    table = page.extract_table() 
    print(table)
    print()
    text = page.extract_text()
    print(text)
		

Здесь метод .open возвращает экземпляр pdfplumber.PDF класса, атрибут pages возвращает список экземпляров pdfplumber.Page каждой страницы документа, метод .extract_table извлекает табличные данные со страницы. Он возвращает двухмерный список, где элементами списка есть списки с данными каждой строки таблицы. Дальше этими данными мы воспользуемся для записи таблицы в файл csv/xlsx. Метод .extract_text возвращает текст таблицы одной строкой с символами перехода на новую строку.

Печать:

			[<Page:1>, <Page:2>, <Page:3>, <Page:4>]

[['43', '1', '143112230', 'Ball bearing, 6x19x6', '22'], ['44', '1', '310010660', 'Armature compl.,230V', '31'], ['45', '1', '143115890', 'Ball bearing, 8X 22X  7', '16'], ['46', '1', '343398310', 'Sealing washer', '11'], ['47', '1', '311012030', 'Field coil compl.', '28'], ['48', '1', '341511630', 'Depth stop', '16'], ['49', '1', '314000840', 'Support handle cpl.', '20'], ['50', '1', '343409850', 'Electronic switch', '26'], ['51', '1', '343362490', 'Cable clip', '12'], ['52', '1', '343254560', 'Suppressor', '11'], ['53', '1', '344099380', 'Cable sleeve', '11'], ['54', '1', '344489060', 'Cable with plug', '26'], ['55', '1', '343013250', 'Carbon brush set', '23'], ['56', '1', '338059040', 'Rating plate', '11'], ['57', '1', '316057090', 'Maintenance kit', '43'], ['90', '1', '341059290', 'Catching sleeve', '16'], ['880', '1', '344130800', 'Grease FG 126', '26'], ['890', '1', '344130560', 'Grease SF 011 50Gr 1,8oz', '30'], ['1001', '1', '338505760', 'Diagram', '']]

BHE 2444
Position Amount Type number Description PG
43 1 143112230 Ball bearing, 6x19x6 22
44 1 310010660 Armature compl.,230V 31
45 1 143115890 Ball bearing, 8X 22X  7 16
46 1 343398310 Sealing washer 11
47 1 311012030 Field coil compl. 28
48 1 341511630 Depth stop 16
49 1 314000840 Support handle cpl. 20
50 1 343409850 Electronic switch 26
51 1 343362490 Cable clip 12
52 1 343254560 Suppressor 11
53 1 344099380 Cable sleeve 11
54 1 344489060 Cable with plug 26
55 1 343013250 Carbon brush set 23
56 1 338059040 Rating plate 11
57 1 316057090 Maintenance kit 43
90 1 341059290 Catching sleeve 16
880 1 344130800 Grease FG 126 26
890 1 344130560 Grease SF 011 50Gr 1,8oz 30
1001 1 338505760 Diagram  
3/4
		

Запись таблицы

Запись в CSV-файл

Для записи в файл с расширением csv, ничего устанавливать не надо.  Модуль CSV уже уставлен в стандартную библиотеку Python.

CSV-модуль имеет два класса: reader — для чтения табличных данных формата csv, writer — для записи.

Нам нужен класс writer:

			import csv
with open('path/to/file.csv', 'w') as f:
    writer = csv.writer(f,  delimiter=';')
    writer.writerows(table)
		

Здесь writer — экземпляр класса writer с параметрами: f — дескриптор открытого файла для записи, delimiter — задаёт значение для разделения полей в строках. Запись делает метод .writerows для всех строк table.

Запись в XLSX-файл

Для записи документов с расширением .xlsx в стандартной библиотеке нет модулей. Здесь будут рассмотрены модуль xlsxWriter и библиотека Pandas.

Модуль xlsxWriter

Установка модуля: pip install XlsxWriter.

			import xlsxwriter

workbook = xlsxwriter.Workbook('path/to/file.xlsx)
worksheet = workbook.add_worksheet()

for row, el in enumerate(table):
    for column, data in enumerate(el):
        worksheet.write(row, column, data)
        
workbook.close()
		

Здесь:

  1. Импортируем модуль.
  2. Создаём объект книги конструктором Workbook().
  3. Метод add_worksheet() добавляет новый рабочий лист книги.
  4. Перебор данных и запись методом write() с параметрами:
  • row — номер строки;column— номер колонки;data — данные для записи в ячейку.

5.   Закрываем объект книги.

Библиотека Pandas

Pandas — это мощная библиотека для работы с данными. Инструкции по установке: https://pandas.pydata.org/docs/getting_started/install.html

Для создания и записи в документ  .xlsx или .csv (pandas предоставляет обе возможности), нужно создать объект класса DataFrme, и сохранить документ с нужным расширением.

			import pandas as pd

df = pd.DataFrame(table)

df.to_excel('path/to/file.xlsx')
df.to_csv('path/to/file.csv')
		

Выполняем задание

			import csv
import pdfplumber
from pdfrw import PdfReader, PdfWriter

path_pdf = 'path/to/file.pdf’


def wrt_csv(k, table):
    # функция записывает файл .csv
    # k - номер таблицы
    # table - двухмерный список данных
    with open(f'table-{k}.csv', 'w') as f:
        writer = csv.writer(f,  delimiter=';')
        writer.writerows(table)
        
def wrt_page(path_pdf, i, n, file_name):
    # функция записывает одну страницу .pdf
    # path_pdf - путь к файлу
    # i - номер страницы
    # n - номер страницы для названия
    # file_name - название документа
    x = PdfReader(path_pdf)
    y = PdfWriter()
    y.addpage(x.pages[i])
    y.write(f'{file_name}_{str(n)}.pdf')  
    
def wrt_pages(path_pdf, list_page, file_name):
    x = PdfReader(path_pdf)
    y = PdfWriter() 
    for n in list_page:
        y.addpage(x.pages[n])
    y.write(f'{file_name} no tables.pdf') 
    
def rezult(path_pdf):
    file_name = path_pdf.split('\')[-1].split('.')[0]
    with pdfplumber.open(path_pdf) as pdf:
        n = 0
        list_page = []
        k = 0
        for i in range(len(pdf.pages)):
            page = pdf.pages[i]
            table = page.extract_table() # None, list
            
            if not table:
                list_page.append(i)
                n += 1
                # запись одной страницы.pdf без таблицы
                wrt_page(path_pdf, i, n, file_name)
                
                # объединение страниц в одну страницу .pdf
                wrt_pages(path_pdf, list_page, file_name)
            else:
                k += 1
                # функция для записи таблицы .csv
                wrt_csv(k, table)

                
if __name__ == '__main__':
    rezult(path_pdf)
		

Итоги

Мы рассмотрели основные задачи парсинга PDF:

  • разбиение документа PDF на отдельные страницы и их сохранение;
  • объединение нескольких листов .pdf в один лист;
  • извлечение текста и таблиц из PDF.

Ещё очень часто приходится извлекать из PDF картинки, но это уже тема для следующей статьи.

Следите за новыми постами
Следите за новыми постами по любимым темам
13К открытий14К показов