python 批量从PDF中提取表格(使用yield语法迭代数据)

Python批量提取PDF表格数据:使用yield优化性能
本文介绍如何使用pdfplumber和pandas库从PDF文件中批量提取和分析表格数据。通过应用yield语法,实现数据迭代输出,以提高性能并避免大列表导致的内存问题。虽然已通过next方法进行部分优化,但在处理大量数据时,仍存在内存占用过快的问题,可能需要采取分批提取的策略。

很多时候,在处理各种数据业务时候,会受到到PDF格式的文件,需要提取并分析里面的数据。本文利用pdfplumber,pandas 提取保存表格。同时学习利用yield语法,形成迭代器,输出每页数据。避免使用列表的办法输出,提升性能,简化代码。
代码如下

import gc
import pdfplumber
import pandas as pd
import time
from time import  ctime
import openpyxl
pdf = pdfplumber.open(r"E:\python项目\python\小阳工程\提取pdf表格,筛选表格数据\条件筛选表格输出\1032+第三册+第二分册+路基路面.pdf")
N=len(pdf.pages)
print('总共有',N,'页')

def yieldlist( ):
    for i in range(249,N):
        
        print('********************************************************************************************************************************************************')
        print('正在搜寻第',str(i+1),'页表格')
        print('********************************************************************************************************************************************************')   
        p0 = pdf.pages[i]
        yield p0
        


def dojob1():  #此函数没有多线程 直接循环提取表格
    i=249
    for item in yieldlist():
        try:
            table = item.extract_table()
        #print(table)
        
            df = pd.DataFrame(table[1:])
        #print(df)
            
            
            
            print(df)
            #excel_writer = pd.ExcelWriter("1032+第三册+第二分册+路基路面"+str(i+1)+".xlsx","1032+第三册+第二分册+路基路面第"+str(i+1)+'页表格', engine='openpyxl', mode='a', if_sheet_exists='replace')
            #df.to_excel("1032+第三册+第二分册+路基路面"+str(i+1)+".xlsx","1032+第三册+第二分册+路基路面第"+str(i+1)+'页表格',   header=True, index=False) #去掉注释符号 这句把数据保存到excel
            del df
            gc.collect()
            
        except Exception as e:
            print(e)
            print('该页面无表格,或者检查是否存在表格')
           
           
            pass
        #print('目前内存占用率是百分之',str(ps.virtual_memory().percent),'    第',str(i+1),'页输出完毕')
        print('**********************************************************************************************************************************************************')
        print('\n\n\n') 
        i+=1

    #print('我是单线程循环,可能会导致内存溢出')
    
   
        
if __name__ == '__main__':
    dojob1()        
  

下面是加了next方法,稍微优化了一下,如果try:输出表格,内存不会溢出。但是如果到了except那里,每次循环之后,内存就会很快满起来,不知道该怎么弄,next稍微优化了一点,但是后面那个还是解决不了,有大神懂得,给我留言指点一下。

import gc
import pdfplumber
import pandas as pd
import time
from time import  ctime
import openpyxl
pdf = pdfplumber.open(r"E:\python项目\python\小阳工程\提取pdf表格,筛选表格数据\条件筛选表格输出\1032+第三册+第二分册+路基路面.pdf")
N=len(pdf.pages)
print('总共有',N,'页')

def yieldlist( ):
    for i in range(249,N):
        
        #print('********************************************************************************************************************************************************')
        print('正在搜寻第',str(i+1),'页表格')
        #print('********************************************************************************************************************************************************')
        
        
        p0 = pdf.pages[i]
        yield p0
        
        


def dojob1():  #此函数没有多线程 直接循环提取表格
    k=249
    iter=yieldlist( )
    #next(iter)
    for i in range(k,N):
        try:
            table = next(iter).extract_table()  #利用next方法降低内存占用,逐个提取页面
        #print(table)
        
            df = pd.DataFrame(table[1:])
        #print(df)
            
            
            #df.to_excel("1032+第三册+第二分册+路基路面"+str(i+1)+".xlsx","1032+第三册+第二分册+路基路面第"+str(i+1)+'页表格',   header=True, index=False)   #去掉注释可以把表格保存到excel
            print('表格已保存')           
            del  table , df #删除数据,进一步优化内存,实际还是全靠next方法才最有效果
            gc.collect()
            
            
        except Exception as e:
            #print(e)
            print('该页面无表格,或者检查是否存在表格')
            
            gc.collect()
            continue
            
        
        
        
        #print('目前内存占用率是百分之',str(ps.virtual_memory().percent),'    第',str(i+1),'页输出完毕')
        #print('**********************************************************************************************************************************************************')
        print('\n\n\n') 
        

    #print('我是单线程循环,可能会导致内存溢出')
    
   
        
if __name__ == '__main__':
    dojob1()        

运行效果如下,只显示提取效果,目前无法彻底解决内存满的问题,就得分每次取200页的办法,分开循环提取
在这里插入图片描述

在这里插入图片描述

<think>我们正在讨论Python中的yieldfrom语法yieldfrom是Python3.3引入的一个语法结构,它用于在生成器函数中委托给另一个生成器(或任何可迭代对象)。它的主要作用有两个:1.简化生成器中的代码,特别是当需要嵌套生成数据时。2.允许生成器将部分操作委托给另一个生成器,形成一个生成器链,这样可以直接将子生成器的值传递到外层生成器,同时也能正确处理子生成器的返回值(在Python3.3之后支持)。基本语法yieldfrom<expression>其中,expression必须是一个可迭代对象(比如生成器、迭代器、列表等)。使用yieldfrom的好处:-避免我们手动迭代子生成器,并逐个yield值,使代码更简洁。-自动处理子生成器中的StopIteration异常,并可以捕获返回值(通过StopIteration.value)。一个典型的例子是我们在生成器中需要嵌套另一个生成器:在没有yieldfrom的时候,我们需要这样写:foriteminsub_generator():yielditem有了yieldfrom之后,我们可以简写为:yieldfromsub_generator()此外,yieldfrom还允许我们在子生成器返回时获取返回值。注意:子生成器必须是一个生成器函数,并且通过return语句返回值(在生成器中,returnexpr会引发StopIteration(expr)异常,而yieldfrom会捕获这个异常,并将其value作为返回值)。下面我们通过一个例子来详细说明。例1:使用yieldfrom扁平化嵌套列表例2:使用yieldfrom与子生成器通信,并获取返回值我们将给出两个示例代码。</think> 在 Python 中,`yield from` 语法(PEP 380)用于**委托生成器操作**,让一个生成器将其部分操作委托给另一个生成器(或任何可迭代对象)。它的核心作用是简化生成器协作代码,避免手动处理嵌套生成器的迭代和值传递。 ### 主要作用: 1. **委托生成**:直接将子生成器的所有值返回给主调用方 2. **双向通信**:传递 `send()` 和 `throw()` 到子生成器 3. **自动结束处理**:自动捕获子生成器的 `StopIteration` 并提取返回值 ### 示例代码及详解 #### 基础用法:扁平化嵌套生成器 ```python def sub_generator(): yield 2 yield 3 def main_generator(): yield 1 # 委托给 sub_generator yield from sub_generator() # 等价于 for x in sub_generator(): yield x yield 4 # 测试 gen = main_generator() print(list(gen)) # 输出: [1, 2, 3, 4] ``` **解释**: - `main_generator` 将执行权委托给 `sub_generator` - 无需手动迭代 `sub_generator` 并 `yield` 每个值 - 输出结果等同于在 `main_generator` 中直接 `yield 1; yield 2; yield 3; yield 4` --- #### 高级用法:带返回值的委托 ```python def sub_generator(): yield "A" yield "B" return "Done!" # 返回值通过 StopIteration 传递 def main_generator(): result = yield from sub_generator() yield f"Sub-generator returned: {result}" # 测试 gen = main_generator() print(next(gen)) # 输出: A print(next(gen)) # 输出: B try: print(next(gen)) # 输出: Sub-generator returned: Done! except StopIteration: pass ``` **解释**: 1. `sub_generator` 返回的 `"Done!"` 会被赋值给 `result` 2. 委托机制自动捕获 `StopIteration` 并提取返回值 3. `main_generator` 继续执行后续的 `yield` 语句 --- #### 双向通信示例 ```python def sub_generator(): val = yield "Ready" yield f"Received: {val}" return "Complete" def main_generator(): ret = yield from sub_generator() yield f"Final: {ret}" # 测试 gen = main_generator() print(gen.send(None)) # 输出: Ready(必须用 None 启动) print(gen.send("Hello")) # 输出: Received: Hello print(gen.send(None)) # 输出: Final: Complete(触发 StopIteration) ``` **关键点**: - `gen.send("Hello")` 直接传递到 `sub_generator` 内部 - 子生成器恢复执行并处理发送的值 - 返回值继续由委托机制处理 --- ### 与手动迭代对比 **不使用 `yield from` 的等效代码**: ```python def manual_delegation(): yield 1 # 手动迭代子生成器 sub = sub_generator() try: while True: x = next(sub) yield x except StopIteration as e: result = e.value yield f"Result: {result}" ``` `yield from` 自动完成了: 1. 迭代子生成器 2. 传递 `send()/throw()` 3. 处理 `StopIteration` 和返回值 ### 适用场景 - 生成器组合和管道处理 - 异步编程(`asyncio` 的基础) - 递归生成器(如树遍历) - 实现基于生成器的协程
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬码工琪老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值