12:内容检索——搜索文件夹内数千个文档中的特定关键字
第二阶段:文件管理与系统自动化(9-15)
场景引入
领导问你:“去年 3 月份关于华为项目的合同在哪里?”
你面对 50 个文件夹、3000 多个文档,用 Windows 自带的搜索只能搜文件名,搜不到文件内容。逐份打开文件 Ctrl+F 查找?太慢了。
本节教你用 Python 实现全文内容搜索,3000 份文档只需几秒钟。
技术原理
核心流程:
遍历文件夹 → 识别文件类型 → 读取文本内容 → 正则/字符串匹配关键词 → 输出匹配结果
支持的文件类型
| .txt, .csv, .md, .py | 直接 open().read() | 内置 |
| .docx | python-docx | 需安装 |
| PyPDF2 / pdfplumber | 需安装 | |
| .xlsx | pandas / openpyxl | 需安装 |
环境准备
pip install python-docx PyPDF2 openpyxl
完整代码
import os
import re
from pathlib import Path
from datetime import datetime
# ==================== 文本提取器 ====================
def extract_text_txt(file_path):
"""读取纯文本文件"""
encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']
for enc in encodings:
try:
with open(file_path, 'r', encoding=enc) as f:
return f.read()
except UnicodeDecodeError:
continue
return ""
def extract_text_docx(file_path):
"""读取 Word 文档"""
try:
from docx import Document
doc = Document(str(file_path))
return '\\n'.join([p.text for p in doc.paragraphs])
except Exception:
return ""
def extract_text_pdf(file_path):
"""读取 PDF 文档"""
try:
import PyPDF2
text = ""
with open(str(file_path), 'rb') as f:
reader = PyPDF2.PdfReader(f)
for page in reader.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\\n"
return text
except Exception:
return ""
def extract_text_xlsx(file_path):
"""读取 Excel 文件的所有单元格文本"""
try:
import pandas as pd
df = pd.read_excel(file_path, engine='openpyxl')
return df.to_string()
except Exception:
return ""
# 提取器映射
EXTRACTORS = {
'.txt': extract_text_txt,
'.csv': extract_text_txt,
'.md': extract_text_txt,
'.py': extract_text_txt,
'.log': extract_text_txt,
'.ini': extract_text_txt,
'.json': extract_text_txt,
'.docx': extract_text_docx,
'.pdf': extract_text_pdf,
'.xlsx': extract_text_xlsx,
'.xls': extract_text_xlsx,
}
# ==================== 核心搜索函数 ====================
def search_files_content(search_dir, keywords, file_types=None, max_results=50, case_sensitive=False):
"""
在文件夹中搜索包含特定关键字的文件
参数:
search_dir: 搜索目录
keywords: 搜索关键字(字符串或列表)
file_types: 文件后缀过滤(如 ['.docx', '.pdf']),None 表示全部
max_results: 最多返回结果数
case_sensitive: 是否区分大小写
"""
if isinstance(keywords, str):
keywords = [keywords]
search_path = Path(search_dir)
results = []
files_searched = 0
print(f"开始搜索: {search_dir}")
print(f"关键字: {', '.join(keywords)}")
print("-" * 50)
for file_path in search_path.rglob('*'):
if file_path.is_file():
suffix = file_path.suffix.lower()
# 文件类型过滤
if file_types and suffix not in file_types:
continue
# 获取提取器
extractor = EXTRACTORS.get(suffix)
if not extractor:
continue
files_searched += 1
try:
content = extractor(file_path)
if not content:
continue
# 搜索关键字
matched_keywords = []
search_content = content if case_sensitive else content.lower()
for kw in keywords:
search_kw = kw if case_sensitive else kw.lower()
if search_kw in search_content:
matched_keywords.append(kw)
if matched_keywords:
# 提取关键字周围的上下文
context = extract_context(content, keywords, case_sensitive)
results.append({
'file': str(file_path),
'matched': matched_keywords,
'context': context,
'size': file_path.stat().st_size,
'modified': datetime.fromtimestamp(file_path.stat().st_mtime)
})
print(f"[匹配] {file_path.name}")
print(f" 关键字: {', '.join(matched_keywords)}")
print(f" 上下文: …{context}…")
print()
if len(results) >= max_results:
break
except Exception as e:
pass # 跳过无法读取的文件
# 汇总报告
print("=" * 50)
print(f"搜索完成!")
print(f"扫描文件: {files_searched}")
print(f"匹配文件: {len(results)}")
# 导出报告
if results:
import pandas as pd
report = pd.DataFrame(results)
report_file = "搜索结果报告.xlsx"
report.to_excel(report_file, index=False, engine='openpyxl')
print(f"详细报告已导出: {report_file}")
return results
def extract_context(text, keywords, case_sensitive, context_length=50):
"""提取关键字周围的上下文文本"""
search_text = text if case_sensitive else text.lower()
for kw in keywords:
search_kw = kw if case_sensitive else kw.lower()
pos = search_text.find(search_kw)
if pos >= 0:
start = max(0, pos – context_length)
end = min(len(text), pos + len(kw) + context_length)
return text[start:end].replace('\\n', ' ')
return ""
# ==================== 使用示例 ====================
if __name__ == "__main__":
# ========== 示例 1:搜索单个关键字 ==========
results = search_files_content(
search_dir=r"D:\\工作文档",
keywords="华为",
file_types=['.docx', '.pdf', '.xlsx']
)
# ========== 示例 2:搜索多个关键字 ==========
# results = search_files_content(
# search_dir=r"D:\\工作文档",
# keywords=["合同", "华为", "2024"],
# max_results=20
# )
# ========== 示例 3:正则表达式搜索 ==========
# (需要自定义扩展,在提取内容后用 re.search 匹配)
代码逐行解析
1. 多编码兼容
encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']
for enc in encodings:
try:
with open(file_path, 'r', encoding=enc) as f:
return f.read()
except UnicodeDecodeError:
continue
Windows 中文环境下,文本文件可能使用 GBK 编码。依次尝试多种编码,确保能正确读取中文内容。
2. 提取器映射
EXTRACTORS = {
'.txt': extract_text_txt,
'.docx': extract_text_docx,
'.pdf': extract_text_pdf,
...
}
通过字典映射,根据文件后缀自动选择对应的文本提取函数,扩展性极强。添加新类型只需增加一行映射。
3. 上下文提取
def extract_context(text, keywords, context_length=50):
pos = search_text.find(search_kw)
start = max(0, pos – context_length)
end = min(len(text), pos + len(kw) + context_length)
return text[start:end]
在搜索结果中显示关键字前后 50 个字符的上下文,方便快速判断是否为目标文件。
进阶技巧
技巧 1:使用正则表达式搜索
import re
def search_with_regex(search_dir, pattern):
"""使用正则表达式搜索"""
regex = re.compile(pattern, re.IGNORECASE)
for file_path in Path(search_dir).rglob('*'):
if file_path.is_file() and file_path.suffix == '.txt':
content = file_path.read_text(encoding='utf-8', errors='ignore')
matches = regex.findall(content)
if matches:
print(f"{file_path}: {matches}")
技巧 2:全文索引加速(适合频繁搜索)
如果经常搜索同一批文件,可以先建立索引:
import sqlite3
def build_index(search_dir, db_file="file_index.db"):
conn = sqlite3.connect(db_file)
conn.execute('''CREATE TABLE IF NOT EXISTS files
(path TEXT, content TEXT, modified REAL)''')
for file_path in Path(search_dir).rglob('*'):
if file_path.is_file():
content = extract_text(file_path)
conn.execute(
"INSERT OR REPLACE INTO files VALUES (?, ?, ?)",
(str(file_path), content, file_path.stat().st_mtime)
)
conn.commit()
conn.close()
然后用 SQL 查询:
results = conn.execute("SELECT path FROM files WHERE content LIKE '%华为%'")
常见问题
Q1:PDF 搜索不到内容?
有些 PDF 是扫描件(图片格式),PyPDF2 无法提取文本。需要使用 OCR:
pip install pdfplumber pytesseract pillow
Q2:搜索速度太慢?
- 使用 file_types 参数缩小搜索范围
- 排除不需要搜索的大文件(如日志文件可能很大)
- 对于频繁搜索的场景,建立索引(技巧 2)
Q3:搜索结果太多怎么看?
导出为 Excel 报告后,可以按文件大小、修改时间排序,或在 Excel 中筛选。
总结
| 识别类型 | file_path.suffix | 获取文件扩展名 |
| 提取文本 | 多提取器字典 | 按类型选择提取方法 |
| 匹配关键字 | kw in content | 字符串包含判断 |
| 提取上下文 | extract_context() | 显示关键字周围文本 |
| 导出报告 | pd.DataFrame().to_excel() | Excel 格式结果 |
本节掌握了全文内容搜索的能力,再也不怕"找不到文件"的问题。
下一节预告:13:空间清理——自动识别并删除电脑中的重复文件与大内存垃圾!