extract_pdfs_v2.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #!/usr/bin/env python3
  2. """
  3. PDF extraction v2: table-aware
  4. - Extracts tables as Markdown tables
  5. - Marks table boundaries with [TABLE_START] [TABLE_END] tags
  6. - Regular text extracted normally
  7. """
  8. import os
  9. import sys
  10. import re
  11. import fitz
  12. from pathlib import Path
  13. RAW_DIR = Path('/home/67/knowledge/maritime/raw')
  14. OUT_DIR = Path('/home/67/knowledge/maritime/processed')
  15. OUT_DIR.mkdir(exist_ok=True)
  16. pdf_files = sorted(RAW_DIR.glob('*.pdf'))
  17. total = len(pdf_files)
  18. print(f'Total: {total} PDFs')
  19. success = 0
  20. failed = 0
  21. tables_found = 0
  22. def table_to_markdown(table_data, headers=None):
  23. """Convert extracted table data to Markdown table format"""
  24. if not table_data or not table_data[0]:
  25. return ''
  26. rows = []
  27. for row in table_data:
  28. # Clean cells: replace None with empty, remove newlines
  29. cells = []
  30. for cell in row:
  31. if cell is None:
  32. cells.append('')
  33. else:
  34. cells.append(str(cell).replace('\n', ' ').strip())
  35. rows.append(cells)
  36. if not rows:
  37. return ''
  38. # Ensure all rows have same number of columns
  39. max_cols = max(len(r) for r in rows)
  40. for r in rows:
  41. while len(r) < max_cols:
  42. r.append('')
  43. # Build markdown table
  44. lines = []
  45. # Header row
  46. lines.append('| ' + ' | '.join(rows[0]) + ' |')
  47. lines.append('| ' + ' | '.join(['---'] * max_cols) + ' |')
  48. # Data rows
  49. for row in rows[1:]:
  50. lines.append('| ' + ' | '.join(row) + ' |')
  51. return '\n'.join(lines)
  52. for i, pdf_path in enumerate(pdf_files):
  53. out_path = OUT_DIR / (pdf_path.stem + '.txt')
  54. try:
  55. doc = fitz.open(str(pdf_path))
  56. text_parts = []
  57. for page_num in range(len(doc)):
  58. page = doc[page_num]
  59. # Try to find tables on this page
  60. page_tables = page.find_tables()
  61. table_rects = []
  62. if page_tables.tables:
  63. for tab in page_tables.tables:
  64. table_data = tab.extract()
  65. if table_data and len(table_data) > 1:
  66. md_table = table_to_markdown(table_data)
  67. if md_table:
  68. text_parts.append(f'[TABLE_START]\n{md_table}\n[TABLE_END]')
  69. tables_found += 1
  70. table_rects.append(tab.bbox)
  71. # Get regular text, excluding table areas
  72. if not table_rects:
  73. page_text = page.get_text()
  74. if page_text.strip():
  75. text_parts.append(page_text)
  76. else:
  77. # Extract text only from non-table regions
  78. # Get page full rect, then extract text blocks and skip those inside table rects
  79. blocks = page.get_text("blocks") # list of (x0, y0, x1, y1, text, block_no, type)
  80. for block in blocks:
  81. bx0, by0, bx1, by1, block_text = block[:5]
  82. if not block_text.strip():
  83. continue
  84. # Check if this text block overlaps any table rect
  85. in_table = False
  86. for trect in table_rects:
  87. tx0, ty0, tx1, ty1 = trect
  88. # Check overlap: block center inside table rect
  89. bcx = (bx0 + bx1) / 2
  90. bcy = (by0 + by1) / 2
  91. if tx0 <= bcx <= tx1 and ty0 <= bcy <= ty1:
  92. in_table = True
  93. break
  94. if not in_table:
  95. text_parts.append(block_text)
  96. doc.close()
  97. full_text = '\n'.join(text_parts)
  98. if full_text.strip():
  99. with open(out_path, 'w', encoding='utf-8') as f:
  100. f.write(f'# {pdf_path.name}\n\n')
  101. f.write(full_text)
  102. success += 1
  103. else:
  104. with open(out_path, 'w', encoding='utf-8') as f:
  105. f.write(f'# {pdf_path.name}\n# [scan PDF]\n')
  106. failed += 1
  107. except Exception as e:
  108. failed += 1
  109. if (i + 1) % 100 == 0:
  110. print(f' ERROR [{i+1}/{total}] {pdf_path.name}: {e}', file=sys.stderr)
  111. if (i + 1) % 200 == 0:
  112. print(f' Progress: {i+1}/{total}, tables: {tables_found}')
  113. print(f'\nDone! success={success} failed={failed} tables={tables_found}')