main.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #!/usr/bin/env python
  2. # -*- encoding: utf-8 -*-
  3. '''
  4. @Contact : liuyuqi.gov@msn.cn
  5. @Time : 2023/12/09 14:57:36
  6. @License : Copyright © 2017-2022 liuyuqi. All Rights Reserved.
  7. @Desc : PDF批量转换为HTML工具
  8. 功能说明:
  9. 递归读取指定目录中的所有PDF文件,将其转换为HTML格式,
  10. 并保存到指定的输出目录中。
  11. 使用方法:
  12. python main.py [--input-dir INPUT_DIR] [--output-dir OUTPUT_DIR] [--recursive] [--no-recursive]
  13. 参数说明:
  14. --input-dir, -i 输入目录,包含PDF文件的目录(默认:当前工作目录)
  15. --output-dir, -o 输出目录,保存HTML文件的目录(默认:htmls)
  16. --recursive, -r 递归搜索子目录中的PDF文件(默认:True)
  17. --no-recursive 不递归搜索子目录
  18. --zoom 转换时的缩放比例(默认:1.3)
  19. --help, -h 显示帮助信息
  20. '''
  21. import argparse
  22. import os
  23. import shutil
  24. import subprocess
  25. from pathlib import Path
  26. def convert_pdf_to_html(pdf_path: Path, output_dir: Path, zoom: float = 1.3) -> bool:
  27. '''
  28. 转换单个PDF文件为HTML格式
  29. 参数:
  30. pdf_path: PDF文件的完整路径
  31. output_dir: 输出目录路径
  32. zoom: 缩放比例,默认为1.3
  33. 返回:
  34. 转换成功返回True,失败返回False
  35. '''
  36. pdf_name = pdf_path.name
  37. # 生成临时HTML文件名(不包含路径)
  38. html_name = pdf_name.rsplit('.', 1)[0] + '.html'
  39. # 最终输出路径
  40. final_output_path = output_dir / html_name
  41. # 检查输出文件是否已存在
  42. if final_output_path.exists():
  43. print(f'Skipping {pdf_name} - {html_name} already exists in output directory')
  44. return True
  45. # 构建pdf2htmlEX命令
  46. cmd = [
  47. 'pdf2htmlEX',
  48. '--zoom', str(zoom),
  49. '--process-outline', '0',
  50. '--page-filename', html_name,
  51. str(pdf_path)
  52. ]
  53. try:
  54. # 执行转换命令
  55. result = subprocess.run(
  56. cmd,
  57. capture_output=True,
  58. text=True,
  59. timeout=300 # 5分钟超时
  60. )
  61. if result.returncode == 0:
  62. # 检查生成的HTML文件位置
  63. # pdf2htmlEX默认在当前工作目录或PDF目录生成文件
  64. generated_html = Path.cwd() / html_name
  65. if not generated_html.exists():
  66. # 可能在PDF文件所在目录
  67. generated_html = pdf_path.parent / html_name
  68. if generated_html.exists():
  69. # 移动到输出目录
  70. shutil.move(str(generated_html), str(final_output_path))
  71. print(f'Successfully converted {pdf_name} to {html_name}')
  72. return True
  73. else:
  74. print(f'Warning: Conversion command succeeded but {html_name} not found')
  75. return False
  76. else:
  77. print(f'Failed to convert {pdf_name}: {result.stderr}')
  78. return False
  79. except subprocess.TimeoutExpired:
  80. print(f'Timeout while converting {pdf_name}')
  81. return False
  82. except Exception as e:
  83. print(f'Error converting {pdf_name}: {str(e)}')
  84. return False
  85. def find_pdf_files(input_dir: Path, recursive: bool = True) -> list:
  86. '''
  87. 查找指定目录中的所有PDF文件
  88. 参数:
  89. input_dir: 输入目录路径
  90. recursive: 是否递归搜索子目录,默认为True
  91. 返回:
  92. PDF文件路径列表
  93. '''
  94. pdf_files = []
  95. if recursive:
  96. # 递归搜索所有子目录
  97. for root, dirs, files in os.walk(input_dir):
  98. for file in files:
  99. if file.lower().endswith('.pdf'):
  100. pdf_files.append(Path(root) / file)
  101. else:
  102. # 仅搜索当前目录
  103. for file in input_dir.iterdir():
  104. if file.is_file() and file.name.lower().endswith('.pdf'):
  105. pdf_files.append(file)
  106. return pdf_files
  107. def main():
  108. '''
  109. 主函数:解析命令行参数并执行转换流程
  110. '''
  111. # 创建参数解析器
  112. parser = argparse.ArgumentParser(
  113. description='PDF批量转换为HTML工具',
  114. formatter_class=argparse.RawDescriptionHelpFormatter,
  115. epilog='''
  116. 示例用法:
  117. python main.py # 使用默认设置转换当前目录下的PDF
  118. python main.py -i ./pdfs -o ./output # 指定输入和输出目录
  119. python main.py --no-recursive # 不搜索子目录
  120. python main.py --zoom 1.5 # 使用1.5倍缩放比例
  121. '''
  122. )
  123. # 添加命令行参数
  124. parser.add_argument(
  125. '--input-dir', '-i',
  126. default='.',
  127. help='输入目录,包含PDF文件的目录(默认:当前工作目录)'
  128. )
  129. parser.add_argument(
  130. '--output-dir', '-o',
  131. default='htmls',
  132. help='输出目录,保存HTML文件的目录(默认:htmls)'
  133. )
  134. # 创建互斥组(递归和非递归只能选一个)
  135. recursive_group = parser.add_mutually_exclusive_group()
  136. recursive_group.add_argument(
  137. '--recursive', '-r',
  138. action='store_true',
  139. default=True,
  140. help='递归搜索子目录中的PDF文件(默认:True)'
  141. )
  142. recursive_group.add_argument(
  143. '--no-recursive',
  144. action='store_false',
  145. dest='recursive',
  146. help='不递归搜索子目录'
  147. )
  148. parser.add_argument(
  149. '--zoom', '-z',
  150. type=float,
  151. default=1.3,
  152. help='转换时的缩放比例(默认:1.3)'
  153. )
  154. # 解析命令行参数
  155. args = parser.parse_args()
  156. # 处理路径
  157. input_dir = Path(args.input_dir).resolve()
  158. output_dir = Path(args.output_dir).resolve()
  159. # 验证输入目录
  160. if not input_dir.exists():
  161. print(f'错误: 输入目录不存在: {input_dir}')
  162. return 1
  163. if not input_dir.is_dir():
  164. print(f'错误: 输入路径不是目录: {input_dir}')
  165. return 1
  166. # 创建输出目录(如果不存在)
  167. try:
  168. output_dir.mkdir(parents=True, exist_ok=True)
  169. except Exception as e:
  170. print(f'错误: 无法创建输出目录 {output_dir}: {str(e)}')
  171. return 1
  172. # 显示配置信息
  173. print('=' * 60)
  174. print('PDF批量转换为HTML工具')
  175. print('=' * 60)
  176. print(f'输入目录: {input_dir}')
  177. print(f'输出目录: {output_dir}')
  178. print(f'递归搜索: {"是" if args.recursive else "否"}')
  179. print(f'缩放比例: {args.zoom}')
  180. print('=' * 60)
  181. # 查找PDF文件
  182. print(f'\n正在搜索PDF文件...')
  183. pdf_files = find_pdf_files(input_dir, args.recursive)
  184. if not pdf_files:
  185. print(f'未找到PDF文件,退出程序。')
  186. return 0
  187. print(f'找到 {len(pdf_files)} 个PDF文件\n')
  188. # 转换PDF文件
  189. success_count = 0
  190. failed_count = 0
  191. skipped_count = 0
  192. for i, pdf_path in enumerate(pdf_files, 1):
  193. print(f'[{i}/{len(pdf_files)}] 处理: {pdf_path.name}')
  194. # 转换文件
  195. result = convert_pdf_to_html(pdf_path, output_dir, args.zoom)
  196. if result:
  197. # 检查是成功转换还是跳过
  198. html_name = pdf_path.name.rsplit('.', 1)[0] + '.html'
  199. if (output_dir / html_name).exists():
  200. success_count += 1
  201. else:
  202. skipped_count += 1
  203. else:
  204. failed_count += 1
  205. # 显示转换结果
  206. print('\n' + '=' * 60)
  207. print('转换完成')
  208. print('=' * 60)
  209. print(f'总PDF文件数: {len(pdf_files)}')
  210. print(f'成功转换: {success_count}')
  211. print(f'跳过(已存在): {skipped_count}')
  212. print(f'转换失败: {failed_count}')
  213. print(f'输出目录: {output_dir}')
  214. print('=' * 60)
  215. # 根据失败情况返回退出码
  216. return 0 if failed_count == 0 else 1
  217. if __name__ == '__main__':
  218. exit(main())