【实战教程】基于 Playwright+MCP 实现裁判文书网自动化爬取(含验证码自动识别 + AI 结果整理)
本文介绍了一个基于Playwright和AI技术的裁判文书网自动化爬取系统。系统采用Playwright实现浏览器自动化操作,ddddocr完成验证码自动识别,MCP工具进行标准化封装,并通过Ollama的qwen3-vl模型实现爬取结果的AI结构化整理。文章详细解析了系统架构、核心代码实现和运行流程,包括浏览器初始化反爬措施、验证码识别、数据模型定义等关键技术点。值得注意的是,在测试过程中发现系
在法律数据分析、案例研究等场景中,裁判文书网的文书爬取是高频需求。本文将手把手教你搭建一套完整的裁判文书网自动化爬取系统,核心基于 Playwright 实现浏览器自动化、ddddocr 实现验证码自动识别、MCP(Model Context Protocol)实现工具化封装,最后通过 Ollama LLM 完成爬取结果的 AI 结构化整理。
一、技术栈与核心能力
1. 核心技术栈
| 技术 / 库 | 作用 |
| Playwright | 浏览器自动化(模拟登录、搜索、页面操作) |
| ddddocr | 验证码自动识别(无需手动输入) |
| Pydantic | 数据模型定义与参数校验 |
| BeautifulSoup4 | HTML 页面解析,提取文书信息 |
| MCP(FastMCP) | 工具化封装,提供标准化调用接口 |
| Ollama(qwen3-vl:4b) | AI 整理爬取结果,生成结构化回答 |
注意:ollama的模型选用需要具备调用工具能力,即具有tools标签

2. 核心能力
- 自动完成裁判文书网登录(账号密码输入 + 验证码自动识别);
- 关键词搜索 + 文书列表解析(去重 + 数量控制);
- MCP 标准化接口封装,支持客户端便捷调用;
- AI 自动整理爬取结果,输出易读的结构化内容;
- 完善的异常处理,预留手动操作兜底时间
二、环境准备
1. 依赖安装
# 基础依赖
pip install playwright pydantic beautifulsoup4 ddddocr langchain-ollama mcp
# 安装Playwright浏览器(Chromium)
playwright install chromium
# 启动Ollama并拉取模型(需提前安装Ollama)
ollama run qwen3-vl:4b
三、核心代码解析
1. 项目结构
├── mcp_serve.py # 服务端:MCP工具封装+核心爬取逻辑
└── client.py # 客户端:调用MCP服务,接收用户输入并展示结果
2. 服务端核心代码(mcp_serve.py)
(1)数据模型定义
通过 Pydantic 定义标准化的数据模型,实现参数校验和类型约束:
# 用户认证模型(适配登录流程)
class UserAuth(BaseModel):
username: str = Field(description="登录账号/手机号")
password: str = Field(description="登录密码")
verify_code: Optional[str] = Field(default="", description="验证码(自动识别)")
# 法律文书模型(存储爬取结果)
class LegalDoc(BaseModel):
title: str = Field(description="法律文书的名称")
url: str = Field(description="法律文书的详情页完整链接")
summary: str = Field(description="法律文书的理由")
# 爬取响应模型(标准化返回结果)
class CrawlResponse(BaseModel):
success: bool = Field(description="是否爬取成功")
message: str = Field(description="结果说明")
data: Optional[List[LegalDoc]] = Field(default=None, description="文书列表")
detail: Optional[Dict] = Field(default=None, description="爬取详情")
(2)浏览器初始化(规避反爬检测)
Playwright 初始化时禁用自动化特征,模拟真实浏览器环境:
@classmethod
async def init_browser_context(cls) -> Tuple[Playwright, Browser, BrowserContext]:
playwright = await async_playwright().start()
# 启动Chromium,关闭无头模式,禁用自动化检测
browser = await playwright.chromium.launch(
headless=False,
args=["--disable-blink-features=AutomationControlled", "--start-maximized"]
)
# 设置中文环境、自定义UA、最大化窗口
context = await browser.new_context(
viewport=None, locale="zh-CN",
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
)
# 移除webdriver标识,避免被反爬检测
await context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
""")
return playwright, browser, context
(3)验证码自动识别
通过 ddddocr 识别登录页面的验证码,无需手动输入:
@classmethod
async def capture_verify_code(cls, page: Page) -> str:
try:
# 等待验证码图片加载
verify_code_selector = "img[src*='verifyCode']"
await page.wait_for_selector(verify_code_selector, timeout=10000)
# 临时文件保存验证码截图
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_file:
temp_path = temp_file.name
# 截图验证码区域并识别
await page.locator(verify_code_selector).screenshot(path=temp_path)
with open(temp_path, "rb") as f:
verify_code = ocr.classification(f.read())
os.unlink(temp_path) # 删除临时文件
return verify_code
except Exception as e:
logger.error(f"验证码识别失败:{str(e)}")
return ""
(4)核心爬取流程
整合登录、搜索、页面解析全流程:
@classmethod
async def crawl_legal_docs(cls, auth: UserAuth, keyword: str, doc_count: int = 5) -> CrawlResponse:
playwright, browser, context = None, None, None
try:
# 1. 初始化浏览器
playwright, browser, context = await cls.init_browser_context()
page = await context.new_page()
# 2. 访问裁判文书网
await page.goto("https://wenshu.court.gov.cn", wait_until="networkidle")
# 3. 执行登录+搜索
await cls.handle_operations(page, auth, keyword)
# 4. 等待文书列表加载并解析
await page.wait_for_selector(".LM_list", timeout=30000)
page_content = await page.content()
soup = BeautifulSoup(page_content, "html.parser")
docs, parse_detail = cls.parse_doc_page(soup, doc_count)
# 5. 返回成功结果
return CrawlResponse(
success=True,
message=f"成功爬取到 {len(docs)} 条法律文书(关键词:{keyword})",
data=docs,
detail=parse_detail
)
except Exception as e:
# 异常处理
return CrawlResponse(
success=False,
message=f"爬取失败:{str(e)}",
detail={"error": str(e), "status": "failed"}
)
finally:
# 释放浏览器资源
if context: await context.close()
if browser: await browser.close()
if playwright: await playwright.stop()
(5)MCP 工具封装
将爬取逻辑封装为 MCP 工具,提供标准化调用接口:
mcp_server = FastMCP("legal_crawler")
@mcp_server.tool(
name="crawl_legal_docs",
description="爬取裁判文书网的法律文书并返回AI整理后的结果,需要用户提供账号密码、关键词和数量(验证码自动识别)"
)
async def mcp_crawl_legal_docs(request: CrawlToolRequest) -> AIResponse:
# 1. 参数校验
if not request.keyword:
return AIResponse(final_answer="❌ 请提供爬取关键词")
if not request.account or not request.password:
return AIResponse(final_answer="❌ 请提供完整的账号和密码")
# 2. 转换参数模型
crawl_request = CrawlRequest.from_tool_request(request)
# 3. 执行爬取
crawl_response = await LegalDocCrawler.crawl_legal_docs(
auth=crawl_request.auth,
keyword=crawl_request.keyword,
doc_count=crawl_request.doc_count
)
# 4. AI整理结果
final_answer = LegalDocCrawler.ai_format_crawl_result(crawl_response, crawl_request.user_query)
# 5. 返回AI整理后的结果
return AIResponse(final_answer=final_answer)
# 启动MCP服务
if __name__ == "__main__":
mcp_server.run(transport="streamable-http")
3. 客户端核心代码(client.py)
客户端负责接收用户输入、调用 MCP 服务、展示结果:
async def main():
# 1. 配置MCP客户端
client = MultiServerMCPClient(
{
"legal_crawler": {
"transport": "streamable-http",
"url": "http://localhost:8000/mcp",
"timeout": 300.0, # 延长超时,适配登录/爬取耗时
}
}
)
# 2. 循环接收用户输入
while True:
user_input = input("\n请输入你的需求(示例:账号13800138000,密码123456,关键词合同纠纷,数量3):")
if user_input.strip().lower() in ["退出", "break", ""]:
break
# 3. 解析用户输入
try:
account = re.search(r'账号[::]?\s*(\S+)', user_input).group(1)
password = re.search(r'密码[::]?\s*(\S+)', user_input).group(1)
keyword = re.search(r'关键词[::]?\s*([^,,]+)', user_input).group(1)
count = int(re.search(r'数量[::]?\s*(\d+)', user_input).group(1))
except:
print("❌ 输入格式错误,请按示例格式输入!")
continue
# 4. 调用MCP爬取工具
tools = await client.get_tools()
crawl_tool = next(tool for tool in tools if tool.name == "crawl_legal_docs")
result = await crawl_tool.ainvoke({
"request": {
"account": account,
"password": password,
"keyword": keyword,
"count": count
}
})
# 5. 展示结果(AI二次优化格式)
llm = OllamaLLM(model="qwen3-vl:4b", base_url="http://localhost:11434", temperature=0.3)
optimized_result = llm.invoke(f"整理以下结果为分条格式:{result}")
print("🎯 爬取结果:\n", optimized_result)
if __name__ == "__main__":
asyncio.run(main())
四、运行步骤
1. 启动 Ollama 服务
确保本地 Ollama 已启动,且qwen3-vl:4b模型已拉取,在终端输入:
ollama serve # 启动Ollama服务
ollama pull qwen3-vl:4b # 拉取模型(首次需执行)
2. 启动 MCP 服务端
3. 启动客户端
按照提示输入爬取需求(示例:账号13800138000,密码123456,关键词合同纠纷,数量3),等待爬取完成即可看到 AI 整理后的结果。
五、关键注意事项
1. 反爬规避
- Playwright 禁用了
AutomationControlled特征,移除了webdriver标识,降低被检测概率; - 所有操作添加了合理的
sleep间隔,模拟真人操作节奏; - 验证码识别失败时,预留 10 秒手动输入时间,登录失败时预留 15 秒手动登录时间。
2. 稳定性优化
- 页面元素等待使用
wait_for_selector,避免因加载慢导致的操作失败; - 文书解析时做了去重处理(按标题去重),确保结果唯一性;
- 所有浏览器资源在
finally块中释放,避免内存泄漏。
3. 兼容性调整
- 页面选择器(如登录按钮、搜索框、验证码图片)需根据裁判文书网页面结构实时调整;
- 若 Ollama 模型调用失败,会自动降级展示原始爬取结果,保证核心功能可用。
六、扩展方向
- 多页爬取:当前仅爬取第一页结果,可扩展分页逻辑,支持更多文书获取;
- Cookie 持久化:登录成功后保存 Cookie,避免重复登录;
- 代理池集成:添加 IP 代理池,规避单 IP 访问频率限制;
- 结果导出:将爬取结果导出为 Excel/JSON 格式,方便后续分析;
- 可视化面板:基于 Streamlit/FastAPI 搭建可视化界面,降低使用门槛。
总结
本文实现的裁判文书网爬取系统,整合了浏览器自动化、验证码识别、MCP 工具化、AI 结果整理等核心能力,既保证了爬取的自动化和稳定性,又通过 MCP 封装和 AI 整理提升了易用性。无论是法律案例研究、数据分析,还是工具化封装,这套方案都具备较高的实用价值,可根据实际需求灵活扩展。
效果讲解:
不知道是什么原因,测试发现能够提取关键信息,但是无法定位到输入框,因此无法自动输入账号密码、关键词等,需要手动输入
更多推荐


所有评论(0)