一、使用PyPDF2提取文本
1.1 基本文本提取
import PyPDF2
defextract_text(filepath):
"""提取PDF所有文本"""
withopen(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
return text
# 使用
content = extract_text('document.pdf')
print(f"文本长度: {len(content)} 字符")
print(content[:500])1.2 按页提取
import PyPDF2
defextract_text_by_page(filepath):
"""按页提取PDF文本"""
withopen(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
pages_text = []
for i, page inenumerate(reader.pages):
text = page.extract_text()
pages_text.append({
'page': i + 1,
'text': text,
'length': len(text)
})
return pages_text
# 使用
pages = extract_text_by_page('document.pdf')
for page in pages:
print(f"第{page['page']}页: {page['length']} 字符")
if page['text']:
print(page['text'][:100] + "...")
print()1.3 提取特定页面
import PyPDF2
defextract_pages(filepath, page_numbers):
"""提取指定页面文本"""
withopen(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
result = []
for page_num in page_numbers:
if0 <= page_num < len(reader.pages):
text = reader.pages[page_num].extract_text()
result.append({
'page': page_num + 1,
'text': text
})
return result
# 提取第1、3、5页
pages = extract_pages('document.pdf', [0, 2, 4])
for page in pages:
print(f"第{page['page']}页:")
print(page['text'][:200])
print()二、使用pdfplumber提取文本
2.1 安装pdfplumber
pip install pdfplumber2.2 基本文本提取
import pdfplumber
defextract_with_pdfplumber(filepath):
"""使用pdfplumber提取文本(更准确)"""
text = ""
with pdfplumber.open(filepath) as pdf:
for page in pdf.pages:
text += page.extract_text() + "\n"
return text
# 使用
content = extract_with_pdfplumber('document.pdf')
print(content[:500])2.3 提取表格数据
import pdfplumber
defextract_tables(filepath):
"""提取PDF中的表格数据"""
tables = []
with pdfplumber.open(filepath) as pdf:
for i, page inenumerate(pdf.pages):
page_tables = page.extract_tables()
if page_tables:
for j, table inenumerate(page_tables):
tables.append({
'page': i + 1,
'table': j + 1,
'data': table
})
return tables
# 使用
tables = extract_tables('document.pdf')
for table in tables:
print(f"第{table['page']}页 表格{table['table']}:")
for row in table['data']:
print(row)
print()2.4 提取带格式的文本
import pdfplumber
defextract_with_format(filepath):
"""提取带格式信息的文本"""
with pdfplumber.open(filepath) as pdf:
for page_num, page inenumerate(pdf.pages, 1):
print(f"\n=== 第{page_num}页 ===")
# 提取文本块
words = page.extract_words()
if words:
print("文本块:")
for word in words[:10]:
print(f" {word['text']} (位置: {word['x0']:.1f}, {word['top']:.1f})")
# 提取文本(保留位置信息)
text = page.extract_text(x_tolerance=3, y_tolerance=3)
if text:
print("\n提取的文本:")
print(text[:200] + "...")
extract_with_format('document.pdf')三、文本处理与清洗
3.1 文本清洗
import re
defclean_text(text):
"""清洗PDF提取的文本"""
# 移除多余空白
text = re.sub(r'\s+', ' ', text)
# 移除特殊字符
text = re.sub(r'[^\w\s\u4e00-\u9fff\.\,\-\:;]', '', text)
# 移除页码标记
text = re.sub(r'\d+\s*/\s*\d+', '', text)
# 移除页眉页脚(根据实际模式调整)
# text = re.sub(r'页眉.*?页脚', '', text, flags=re.DOTALL)
return text.strip()
defextract_and_clean(filepath):
"""提取并清洗文本"""
withopen(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ""
for page in reader.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
return clean_text(text)
# 使用
clean_content = extract_and_clean('document.pdf')
print(clean_content[:500])3.2 提取结构化信息
import re
import PyPDF2
classPDFTextParser:
"""PDF文本解析器"""
def__init__(self, filepath):
self.filepath = filepath
self.text = self._extract_text()
def_extract_text(self):
"""提取文本"""
withopen(self.filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
return text
deffind_pattern(self, pattern):
"""查找特定模式"""
return re.findall(pattern, self.text, re.IGNORECASE)
defextract_emails(self):
"""提取邮箱地址"""
pattern = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
returnself.find_pattern(pattern)
defextract_phone_numbers(self):
"""提取电话号码"""
patterns = [
r'\d{3}-\d{4}-\d{4}',
r'\d{10,11}',
r'\(\d{2,4}\)\s*\d{3,4}-\d{4}'
]
phones = []
for pattern in patterns:
phones.extend(self.find_pattern(pattern))
return phones
defextract_urls(self):
"""提取URL"""
pattern = r'https?://[^\s]+'
returnself.find_pattern(pattern)
defextract_dates(self):
"""提取日期"""
patterns = [
r'\d{4}-\d{2}-\d{2}',
r'\d{2}/\d{2}/\d{4}',
r'\d{2}\s+[A-Za-z]+\s+\d{4}'
]
dates = []
for pattern in patterns:
dates.extend(self.find_pattern(pattern))
return dates
defget_statistics(self):
"""获取文本统计"""
return {
'总字符数': len(self.text),
'单词数': len(re.findall(r'\w+', self.text)),
'行数': len(self.text.splitlines()),
'段落数': len(re.split(r'\n\s*\n', self.text.strip())),
'邮箱数': len(self.extract_emails()),
'电话号码数': len(self.extract_phone_numbers()),
'URL数': len(self.extract_urls()),
'日期数': len(self.extract_dates())
}
# 使用
parser = PDFTextParser('document.pdf')
print("统计信息:")
for key, value in parser.get_statistics().items():
print(f" {key}: {value}")
print("\n邮箱地址:")
for email in parser.extract_emails():
print(f" {email}")
print("\n电话号码:")
for phone in parser.extract_phone_numbers():
print(f" {phone}")四、实战案例
4.1 PDF内容搜索工具
import PyPDF2
import re
from pathlib import Path
classPDFSearchEngine:
"""PDF搜索工具"""
def__init__(self):
self.results = []
defsearch_in_pdf(self, filepath, keyword, case_sensitive=False):
"""在PDF中搜索关键词"""
results = []
withopen(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
for i, page inenumerate(reader.pages):
text = page.extract_text()
ifnot text:
continue
if case_sensitive:
matches = re.finditer(re.escape(keyword), text)
else:
matches = re.finditer(re.escape(keyword), text, re.IGNORECASE)
formatchin matches:
start = max(0, match.start() - 50)
end = min(len(text), match.end() + 50)
context = text[start:end]
results.append({
'page': i + 1,
'keyword': match.group(),
'context': context,
'position': match.start()
})
return results
defsearch_multiple(self, folder_path, keyword, output_file='search_results.txt'):
"""在多个PDF中搜索"""
pdf_files = list(Path(folder_path).glob('*.pdf'))
withopen(output_file, 'w', encoding='utf-8') as f:
f.write(f"PDF搜索报告\n")
f.write(f"关键词: {keyword}\n")
f.write("="*50 + "\n\n")
for pdf_path in pdf_files:
results = self.search_in_pdf(str(pdf_path), keyword)
if results:
f.write(f"文件: {pdf_path.name}\n")
f.write(f"找到 {len(results)} 处匹配\n")
f.write("-"*30 + "\n")
for result in results:
f.write(f" 第{result['page']}页:\n")
f.write(f" ...{result['context']}...\n")
f.write("\n")
f.write("\n")
print(f"搜索结果已保存到: {output_file}")
# 使用
searcher = PDFSearchEngine()
results = searcher.search_in_pdf('document.pdf', 'Python')
for result in results:
print(f"第{result['page']}页: ...{result['context']}...")4.2 PDF数据提取器
import PyPDF2
import re
import json
from datetime import datetime
classPDFDataExtractor:
"""PDF数据提取器"""
def__init__(self, filepath):
self.filepath = filepath
self.text = self._extract_text()
def_extract_text(self):
"""提取文本"""
withopen(self.filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ""
for page in reader.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
return text
defextract_invoice_info(self):
"""提取发票信息"""
info = {}
# 提取发票号
match = re.search(r'发票号[::]\s*([A-Z0-9-]+)', self.text)
ifmatch:
info['invoice_no'] = match.group(1)
# 提取日期
match = re.search(r'日期[::]\s*(\d{4}-\d{2}-\d{2})', self.text)
ifmatch:
info['date'] = match.group(1)
# 提取金额
match = re.search(r'(合计|总)金额[::]\s*([\d,]+\.?\d*)', self.text)
ifmatch:
info['amount'] = float(match.group(2).replace(',', ''))
# 提取客户名称
match = re.search(r'客户[::]\s*([^\n\r]+)', self.text)
ifmatch:
info['customer'] = match.group(1).strip()
# 提取项目
items = []
item_pattern = r'(\d+)\s+([^\d]+)\s+([\d.]+)\s+([\d.]+)'
formatchin re.finditer(item_pattern, self.text):
items.append({
'id': match.group(1),
'description': match.group(2).strip(),
'quantity': float(match.group(3)),
'price': float(match.group(4))
})
info['items'] = items
return info
defextract_resume_info(self):
"""提取简历信息"""
info = {}
# 姓名
match = re.search(r'姓名[::]\s*([^\n\r]+)', self.text)
ifmatch:
info['name'] = match.group(1).strip()
# 联系方式
match = re.search(r'电话[::]\s*([\d-]+)', self.text)
ifmatch:
info['phone'] = match.group(1)
match = re.search(r'邮箱[::]\s*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})', self.text)
ifmatch:
info['email'] = match.group(1)
# 技能
skills = []
skill_match = re.search(r'技能[::]\s*([^\n\r]+)', self.text)
if skill_match:
skills = [s.strip() for s in skill_match.group(1).split(',')]
info['skills'] = skills
# 工作经验
experiences = []
exp_pattern = r'(\d{4}-\d{2})\s*[-–]\s*(\d{4}-\d{2}|至今)\s*([^\n\r]+)'
formatchin re.finditer(exp_pattern, self.text):
experiences.append({
'start': match.group(1),
'end': match.group(2),
'title': match.group(3).strip()
})
info['experience'] = experiences
return info
defextract_contract_info(self):
"""提取合同信息"""
info = {}
# 合同编号
match = re.search(r'合同编号[::]\s*([A-Z0-9-]+)', self.text)
ifmatch:
info['contract_no'] = match.group(1)
# 签约日期
match = re.search(r'签约日期[::]\s*(\d{4}-\d{2}-\d{2})', self.text)
ifmatch:
info['sign_date'] = match.group(1)
# 甲方/乙方
match = re.search(r'甲方[::]\s*([^\n\r]+)', self.text)
ifmatch:
info['party_a'] = match.group(1).strip()
match = re.search(r'乙方[::]\s*([^\n\r]+)', self.text)
ifmatch:
info['party_b'] = match.group(1).strip()
# 金额
match = re.search(r'合同金额[::]\s*([\d,]+\.?\d*)', self.text)
ifmatch:
info['amount'] = float(match.group(1).replace(',', ''))
return info
defto_json(self, output_file=None):
"""导出为JSON"""
data = {
'file': self.filepath,
'extracted_at': datetime.now().isoformat(),
'invoice': self.extract_invoice_info(),
'resume': self.extract_resume_info(),
'contract': self.extract_contract_info()
}
if output_file:
withopen(output_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f"数据已保存到: {output_file}")
return data
# 使用
extractor = PDFDataExtractor('document.pdf')
data = extractor.to_json('extracted_data.json')
print(json.dumps(data, ensure_ascii=False, indent=2)[:500])五、性能优化
5.1 大文件处理
import PyPDF2
defextract_large_pdf(filepath, max_pages=None):
"""处理大PDF文件"""
withopen(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
total_pages = len(reader.pages)
# 限制处理页数
if max_pages and max_pages < total_pages:
total_pages = max_pages
for i inrange(total_pages):
page = reader.pages[i]
text = page.extract_text()
# 逐页处理,不存储全部文本
yield {
'page': i + 1,
'text': text,
'progress': (i + 1) / total_pages * 100
}
# 使用
for page_info in extract_large_pdf('large_document.pdf', max_pages=10):
print(f"第{page_info['page']}页: {len(page_info['text'])} 字符")
### 5.2 缓存提取结果
```python
import hashlib
import pickle
import os
classCachedPDFExtractor:
"""带缓存的PDF提取器"""
def__init__(self, cache_dir='cache'):
self.cache_dir = cache_dir
os.makedirs(cache_dir, exist_ok=True)
def_get_cache_key(self, filepath):
"""生成缓存键"""
withopen(filepath, 'rb') as f:
content = f.read(1024 * 1024) # 读取前1MB
return hashlib.md5(content).hexdigest()
defextract_text(self, filepath):
"""提取文本(带缓存)"""
cache_key = self._get_cache_key(filepath)
cache_file = os.path.join(self.cache_dir, f"{cache_key}.pkl")
# 检查缓存
if os.path.exists(cache_file):
withopen(cache_file, 'rb') as f:
print("使用缓存")
return pickle.load(f)
# 提取文本
print("提取文本...")
withopen(filepath, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
# 保存缓存
withopen(cache_file, 'wb') as f:
pickle.dump(text, f)
return text
# 使用
extractor = CachedPDFExtractor()
text = extractor.extract_text('document.pdf')
print(f"文本长度: {len(text)}")六、总结
# 快速参考
# 1. PyPDF2提取
withopen('file.pdf', 'rb') as f:
reader = PyPDF2.PdfReader(f)
text = reader.pages[0].extract_text()
# 2. pdfplumber提取(更准确)
with pdfplumber.open('file.pdf') as pdf:
text = pdf.pages[0].extract_text()
# 3. 表格提取
with pdfplumber.open('file.pdf') as pdf:
table = pdf.pages[0].extract_table()
# 4. 文本清洗
clean = re.sub(r'\s+', ' ', text)
# 5. 提取特定信息
emails = re.findall(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', text)
# 6. 批量提取
for pdf in Path('.').glob('*.pdf'):
process(pdf)
# 7. 缓存提取
defcached_extract(filepath):
# 检查缓存,避免重复提取
passPDF文本提取是处理PDF文档的基础功能。PyPDF2提供基本提取功能,pdfplumber提供更精确的提取和表格支持。根据文档类型和质量选择合适的工具,结合文本清洗和结构化提取,可以实现高效的数据提取。对于大规模处理,建议使用缓存和分页处理来优化性能。
夜雨聆风