关键词搜索

源码搜索 ×
×

Python批量导出word文档中的图片、嵌入式文件

发布2022-06-18浏览950次

详情内容

学生试卷中的题目有要提交截图的,也有要提交文件的,为了方便学生考试,允许单独交或者嵌入Word中提交,那么事后如何整理学生的答案?单独提交的比较方便,直接扫描文件名匹配名字后放入指定文件夹即可。但是嵌入到Word中的图片和文件怎么提取出来呢?

现有如下需求:提取出一个Word文档中所有的图片(png、jpg)和嵌入的文件(任意格式)放入到指定的文件夹。

解决
docx是一个压缩包,解压缩后图片一般都放在文档名.docx\word\media\目录下:
在这里插入图片描述

而嵌入式文件一般都放在文档名.docx\word\embeddings\目录下:

经过询问度娘,发现提取图片比较简单,直接使用docx库中的Document.part.rels{k:v.target_ref}找到文件的相对路径,用Document.part.rels{k:v.target_part.blob}读出文件内容。简单判断一下路径和文件后缀是不是我们需要的media下的png文件和embeddings下的bin文件,是的话写入到新文件中即可:

提取图片

安装python-docx

pip install python-docx

    提取

    import os
    
    from docx import Document  # pip install python-docx
    
    is_debug = True
    
    if __name__ == '__main__':
        # 需要导出的Word文档路径
        # Python学习交流基地 279199867
        target_file = r'paper\HBase试题.docx'
        # 导出文件所在目录
        output_dir = r'paper\output'
    
        # 加载Word文档
        doc = Document(target_file)
        # 遍历Word包中的所有文件
        dict_rel = doc.part.rels
        # r_id:文件身份码,rel:文件对象 
        for r_id, rel in dict_rel.items():
            if not ( # 如果文件不是在media或者embeddings中的,直接跳过
                    str(rel.target_ref).startswith('media')
                    or str(rel.target_ref).startswith('embeddings')
            ):
                continue
            
            # 如果文件不是我们想要的后缀,也直接跳过
            file_suffix = str(rel.target_ref).split('.')[-1:][0]
            if file_suffix.lower() not in ['png', 'jpg', 'bin']:
                continue
    
            # 如果输出目录不存在,创建
            if not os.path.exists(output_dir):
                os.makedirs(output_dir)
            
            # 构建导出文件的名字和路径
            file_name = r_id + '_' + str(rel.target_ref).replace('/', '_')
            file_path = os.path.join(output_dir,file_name)
            
            # 将二进制数据写入到新位置的文件中
            with open(file_path, "wb") as f:
                f.write(rel.target_part.blob)
            
            # 打印结果
            if is_debug:
                print('导出文件成功:', file_name)
    
      2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    运行结果:
    在这里插入图片描述

    可以看到,图片都能正常导出,但是学生嵌入的JAVA文件并没有导出,或者说导出的是bin文件,没有完全导出。

    提取嵌入式文件

    再次询问度娘发现,这种其实也是zip压缩包,但是不能直接提取出,它有个更专业的名字,叫ole文件,我们之前的doc、xls、ppt等没有带x的上古文档文件都是这种格式。那如何提取出文件呢?度娘告诉我有个叫oletools的项目可以,于是下载下来浅浅地分析了下,发现确实可以!

    oletools项目地址:https://github.com/decalage2/oletools

    或者gitee上别人转存的地址:https://gitee.com/yunqimg/oletools

    我是用的gitee上的版本,因为github打不开 QwQ

    经相关文档介绍,项目下的oletools-master\oletools\oleobj.py就可以提取这种bin后缀的ole文件,简单试一下,在oleobj.py所在目录下打开命令行,把刚刚提取出的rId12_embeddings_oleObject1.bin文件复制到oleobj.py所在目录,执行如下命令:

    注意: 在此之前我执行了一下安装oletools的命令,如果不安装可能会出错:pip install oletools,或者说oleobj.py依赖olefile:pip install olefile,在安装oletools时顺便安装了olefile。

    python oleobj.py rId12_embeddings_oleObject1.bin
    

      成功导出

      Microsoft Windows [版本 10.0.22000.708]
      (c) Microsoft Corporation。保留所有权利。
      
      D:\Minuy\Downloads\oletools-master\oletools-master\oletools>python oleobj.py rId12_embeddings_oleObject1.bin
      oleobj 0.56 - http://decalage.info/oletools
      THIS IS WORK IN PROGRESS - Check updates regularly!
      Please report any issue at https://github.com/decalage2/oletools/issues
      
      -------------------------------------------------------------------------------
      File: 'rId12_embeddings_oleObject1.bin'
      extract file embedded in OLE object from stream '\x01Ole10Native':
      Parsing OLE Package
      Filename = "Boos.java"
      Source path = "D:\111\´ó20´óÊý¾Ý Àî¾üÁé\Boos.java"
      Temp path = "C:\Users\ADMINI~1\AppData\Local\Temp\Boos.java"
      saving to file rId12_embeddings_oleObject1.bin_Boos.java
      
      D:\Minuy\Downloads\oletools-master\oletools-master\oletools>
      
        2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      导出的文件也能正常访问:

      在这里插入图片描述

      于是把oletools目录复制到工程项目下,稍微修改一下oleobj.py能让我的代码调用它,在oleobj.py中添加如下代码:

      def export_main(ole_files, output_dir, log_leve=DEFAULT_LOG_LEVEL):
          ensure_stdout_handles_unicode()
      
          logging.basicConfig(level=LOG_LEVELS[log_leve], stream=sys.stdout,
                              format='%(levelname)-8s %(message)s')
          # 启用日志模块
          log.setLevel(logging.NOTSET)
      
          any_err_stream = False
          any_err_dumping = False
          any_did_dump = False
      
          for container, filename, data \
                  in xglob.iter_files(ole_files,
                                      recursive=False,
                                      zip_password=None,
                                      zip_fname='*'):
      
              if container and filename.endswith('/'):
                  continue
              
              # 输出文件夹
              err_stream, err_dumping, did_dump = \
                  process_file(filename, data, output_dir)
      
              any_err_stream |= err_stream
              any_err_dumping |= err_dumping
              any_did_dump |= did_dump
      
          return_val = RETURN_NO_DUMP
          if any_did_dump:
              return_val += RETURN_DID_DUMP
          if any_err_stream:
              return_val += RETURN_ERR_STREAM
          if any_err_dumping:
              return_val += RETURN_ERR_DUMP
          return return_val
      
      
      def export_ole_file(ole_files, output_dir, debug=False):
          debug_leve = 'critical'
          if debug:
              debug_leve = 'info'
              
          # 导出
          result = export_main(
              ole_files,
              output_dir,
              debug_leve
          )
      
          if result and debug:
              print('导出ole文件出错', ole_files)
      
        2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53

      在提取文件的代码后面加上如下调用:

      if str(rel.target_ref).startswith('embeddings'):
        # 解压嵌入式文件
        export_ole_file([file_path], output_dir)
      
        2
      • 3

      再次运行

      成功导出嵌入到Word中的文件!

      成功解决问题~

      兄弟们快去试试吧!

      相关技术文章

      点击QQ咨询
      开通会员
      返回顶部
      ×
      微信扫码支付
      微信扫码支付
      确定支付下载
      请使用微信描二维码支付
      ×

      提示信息

      ×

      选择支付方式

      • 微信支付
      • 支付宝付款
      确定支付下载