使用python根据大pdf中特定字符串的外观将大pdf拆分为多个不同页面长度的小pdf

rsl1atfo  于 2023-01-16  发布在  Python
关注(0)|答案(2)|浏览(166)
    • 问题**

我有一个很长的PDF文件,有很多页。我希望这个PDF文件被分割成很多小文件,这些小文件的长度是从长PDF文件的文本内容中得到的。你可以把字符串想象成一把剪刀,它可以剪下长PDF文件,甚至把文件名给小PDF文件。
"scissors"字符串由以下迭代器生成,并由"text"表示:

for municipality in array_merged_zone_send:
    text = f'PANORAMICA DI {municipality.upper()}'

如果我在迭代器中打印('text '),结果是:

PANORAMICA DI BELLINZONA
PANORAMICA DI RIVIERA
PANORAMICA DI BLENIO
PANORAMICA DI ACQUAROSSA

上面的字符串是唯一的值,它们只出现一次。上面我只展示了前四个,还有更多,每一项都写在我想要分割的原始PDF中。每一项在原始PDF中只出现一次,不超过一次,不少于一个(始终一对一匹配),并且pdf中从未有尚未通过迭代获得的项目的额外"PANORAMICA DI ........"。PANORAMICA在英语中表示概述。
以下是原始pdf中的页面示例,其中包含来自项目"PANORAMICA DI BLENIO"

的字符串
我想做的是:我想拆分原来的pdf每次出现的字符串项目。在上面的图像原来的pdf必须一分为二:第一个pdf结束在"PANORAMICA DI BLENIO"之前的页面,第二个开始在页面"PANORAMICA DI BLENIO",并将结束在下一个"PANORAMICA DI {自治市. upper"之前的页面得到的pdf名称是"zp_Blenio. pdf",对于第一个"zp_Acquarossa".对于这应该是没有问题的,因为"自治市"时,它是没有上()已经可以了(换句话说是"阿夸罗萨"和"布莱尼奥")
其他例子,以了解与简化模拟(我的文件有更多的页面):原来pdf 12页长,注意不是代码,而是我把代码当成写的不错:

page 1: "PANORAMICA DI RIVIERA"
page 2: no match with "text" item
page 3: no match with "text" item 
page 4: "PANORAMICA DI ACQUAROSSA"
page 5: no match with "text" item 
page 6: "PANORAMICA DI BLENIO"
page 7: no match with "text" item 
page 8: no match with "text" item
page 9: no match with "text" item 
page 10: no match with "text" item
page 11: "PANORAMICA DI BELLINZONA"
page 12: no match with "text" item

结果将是(再次注意,这不是一个代码,但我把作为一个代码,以显示你好):

first created pdf is from page 1 to page 3
second created pdf is from page 4 to page 5
third pdf is from page 6 to 10
forth pdf is from page 11 to 12

规则是这样的:当文本出现时在页面上拆分,直到该文本的前一页再次出现,当文本出现时在页面上拆分,直到该文本的前一页再次出现,等等。
注意:我原来的pdf是一个长的py代码的一部分,pdf每次都改变,但是"PANORAMICA DI ....."的规则没有改变。换句话说,可能是"PANORAMICA DI ACQUAROSSA"和"PANORAMICA DI BLENIO"之间的页面间隔长度改变了。这防止了使用一个变通办法,手动设置页面间隔来分割,忽略了上面建立的规则。

    • 尝试解决问题**

我找到的唯一一个解决这个问题的方法是一个过时的代码,作者没有检查,可以在这个页面中找到:https://stackoverflow.com/a/62344714/13769033
我已经采取的代码和变化取决于新的函数和类,并整合迭代获得"文本"。
旧代码在我更新后的结果如下:

from PyPDF2 import PdfWriter, PdfReader
import re

def getPagebreakList(file_name: str)->list:
    pdf_file = PyPDF2.PdfReader(file_name)
    num_pages = len(pdf_file.pages)
    page_breaks = list()
    for i in range(0, num_pages):
        Page = pdf_file.pages[i] 
        Text = PageObject.extract_text() 
        for municipality in array_merged_zone_send:
            text = f'PANORAMICA DI {municipality.upper()}'
            if re.search(text, Text):
                page_breaks.append(i)
    return page_breaks

inputpdf = PdfReader(open("./report1.pdf", "rb"))
num_pages = len(inputpdf.pages)
page_breaks = getPagebreakList("./report1.pdf")

i = 0
while (i < num_pages):
    if page_breaks:
        page_break = page_breaks.pop(0)
    else:
        page_break = num_pages
    output = PdfWriter()
    while (i != page_break + 1):
        output.add_page(inputpdf.pages[i])
        i = i + 1
    with open(Path('.')/f'zp_{municipality}.pdf',"wb") as outputStream:
        output.write(outputStream)

不幸的是,我不明白大部分的代码。
从我完全看不懂,也不知道作者是否写错的部分:

  • "output = PdfWriter()"的缩进
  • "getPagebreakList('./report1.pdf ')",我在其中放置了要拆分的相同PDF,但作者在其中放置了"getPagebreakList('yourPDF. pdf')",这与PdfFileReader(open("80...pdf","rb"))不同。我假设它应该为两者编写yourPDF. pdf

注意:"./report1.pdf"是要拆分PDF的路径,我确信这是正确的。
代码错误,执行时得到"TypeError:"list"对象不可调用"。
我希望有人能帮我找到解决办法。你可以修改我更新的代码或建议其他方法来解决。谢谢。

    • 建议模拟**

为了简化,在开始时我建议考虑pdf的静态字符串(每x页重复一次的字符串),而不是数组的一部分。
就我而言,我考虑过:

Text = PageObject.extract_text() 
text = 'PANORAMICA'
if re.search(text, Text):
    page_breaks.append(i)

......甚至改变了输出路径。
你可以简单地使用一个长的pdf,里面有周期性但不规则的重复固定文本(3页一次,5页一次等等)。
只有找到解决方案后,才能集成市政当局的迭代。文本上"市政当局"的集成仅用于集成新pdf文件名称中的"市政当局"。仅使用"PANORAMICA"不会影响新pdf的页面间隔长度。

mwngjboj

mwngjboj1#

我的建议是把问题分解成更小的问题,本质上使用一种 * 分治 * 的方法_。通过使单任务函数在出现错误的情况下调试应该更容易。注意getPagebreakList略有不同。

from PyPDF2 import PdfWriter, PdfReader

def page_breaks(pdf_r:PdfReader) -> dict:
    page_breaks = {}
    for i in range(len(pdf_r.pages)):
        pdf_text = pdf_r.pages[i].extract_text() 
        for municipality in array_merged_zone_send:
            pattern = f'PANORAMICA DI {municipality.upper()}'
            if re.search(pattern, pdf_text):
                page_breaks[municipality] = i
    return page_breaks
 

def filenames_range_mapper(pdf_r:PdfReader, page_indices:dict) -> dict:
    num_pages = list(page_indices.values()) + [len(pdf_r.pages)+1] # add last page as well
    # slice the pages from the reader object
    return {name: pdf_r[start:end] for name, start, end in zip(page_indices, num_pages, num_pages[1:])}

def save(file_name:str, pdf_pages:list[PdfReader]) -> None:
    # pass the pages to the writer
    pdf_w = PdfWriter()
    for p in pdf_pages:
        pdf_w.add_page(p)
    
    # write to file
    with open(file_name, "wb") as outputStream:
        pdf_w.write(outputStream) 
    
    # message
    print(f'Pdf "{file_name}" created.')

# main
# ####
# initial location of the file
file_name = "./report1.pdf"
# create reader object
pdf_r = PdfReader(open(file_name, "rb"))
# get index locations of matches
page_breaks = page_breaks(pdf_r)
# dictionary of name-pages slice objects
mapper = filenames_range_mapper(pdf_r, page_breaks)

# template file name
template_output = './zp_{}.pdf'
# iterate over the location-pages mapper
for municipality, pages in ranges.items():
    # set file name
    new_file_name = template_output.format(municipality.title()) # eventually municipality.upper()
    # save the pages into a new file
    save(new_file_name, pages)

使用辅助功能测试代码,以避免不必要的输出。
在这种情况下,考虑filenames_range_mapper的一个稍微不同的实现就足够了,在这个实现中,值将只是一个整数列表(而不是PdfReader对象)。

def filenames_range_mapper_tester(pdf_r:PdfReader, page_indices:dict) -> dict:
    num_pages = list(page_indices.values()) + [len(pdf_r.pages)+1] # add last page as well
    # slice the pages from the reader object
    return {name: list(range(len(pdf_r.pages)))[start,end] for name, start, end in zip(page_indices, num_pages, num_pages[1:])}

# auxiliary test
file_name = "./report1.pdf"
pdf_r = PdfReader(open(file_name, "rb"))
page_breaks = page_breaks(pdf_r)
mapper = filenames_range_mapper_tester(pdf_r, page_breaks)

template_output = './zp_{}.pdf'
for name, pages in mapper.items():
   print(template_output.format(name.title()), pages)

如果输出有意义,那么您可以继续使用非测试代码。
关于如何获得正确页面的抽象

# mimic return of "page_breaks"
page_breaks = {
    "RIVIERA": 1,
    "ACQUAROSSA": 4,
    "BLENIO": 6,
    "BELLINZONA": 11
}

# mimic "filenames_range_mapper"
last_page_of_pdf = 12 + 1 # increment by 1 the number of pages of the pdf!

num_pages = list(page_breaks.values()) + [last_page_of_pdf]
#[1, 4, 6, 11, 12]

mapper = {name: list(range(start, end)) for name, start, end in zip(page_breaks, num_pages, num_pages[1:])}
#{'RIVIERA': [1, 2, 3],
# 'ACQUAROSSA': [4, 5],
# 'BLENIO': [6, 7, 8, 9, 10],
# 'BELLINZONA': [11, 12]}
disho6za

disho6za2#

@卡片
我做了你在评论中建议的测试。
我发布代码是为了确保我已经用正确的方式进行了测试。我运行的代码正是:

from PyPDF2 import PdfWriter, PdfReader

def page_breaks(pdf_r:PdfReader) -> dict:
    page_breaks = {}
    for i in range(len(pdf_r.pages)):
        pdf_text = pdf_r.pages[i].extract_text() 
        for municipality in array_merged_zone_send:
            pattern = f'PANORAMICA DI {municipality.upper()}'
            if re.search(pattern, pdf_text):
                page_breaks[municipality] = i
    return page_breaks

def filenames_range_mapper_tester(pdf_r:PdfReader, page_indices:dict) -> dict:
    num_pages = list(page_indices.values()) + [len(pdf_r.pages)+1] # add last page as well
    # slice the pages from the reader object
    return {name: list(range(len(pdf_r.pages)))[start,end] for name, start, end in zip(page_indices, num_pages, num_pages[1:])}

# auxiliary test
file_name = "./report1.pdf"
pdf_r = PdfReader(open(file_name, "rb"))
page_breaks = page_breaks(pdf_r)
mapper = filenames_range_mapper_tester(pdf_r, page_breaks)

template_output = './zp_{}.pdf'
for name, pages in mapper.items():
   print(template_output.format(name.title()), pages)

测试在位置“num_pages=....."处出现错误

File "<string>", line 15, in filenames_range_mapper_tester
TypeError: 'list' object is not callable

一些观察:我还不明白,在上述代码中,您如何具有:

def filenames_range_mapper_tester(pdf_r:PdfReader, page_indices:dict) -> dict:
    num_pages = list(page_indices.values()) + [len(pdf_r.pages)+1]

我在你的代码中找遍了所有地方,但是我没有找到之前“page_indices”定义的痕迹。在应用方法“values()"之前,你在哪里定义了“page_indices”?
answer中,您确认了abstractionpage_indexs =page_breaks,然后为什么不以这种方式插入代码:

def filenames_range_mapper_tester(pdf_r:PdfReader, page_breaks:dict) -> dict:
    num_pages = list(page_breaks.values()) + [len(pdf_r.pages)+1] # add last page as well
    # slice the pages from the reader object
    return {name: list(range(len(pdf_r.pages)))[start,end] for name, start, end in zip(page_breaks, num_pages, num_pages[1:])}

如果你使用page_breaks,这个“page_breaks”已经从函数def page_breaks的结果中定义了,所以你用一个之前定义的方法来应用,但是即使我用page_breaks替换page_indices,错误还是一样的。

相关问题