银行询证函是审计程序中防范被审计单位资金造假的最关键物理证据。在许多会计师事务所中,项目组需要在银行回函平台上手动下载几百个银行账户的 PDF 回函,并逐一与试算平衡表核对,工作量极其繁重。
本文将演示如何利用 Python + Playwright 构建一个自动下载并勾稽比对的“询证函机器人”。
一、 机器人工作流设计
整个机器人分为以下三个核心模块:
- 浏览器接管下载:模拟人类登录询证函系统,动态处理登录验证,将回函 PDF 下载到指定项目的
\\NAS\回函\目录。 - 多模态 PDF 表格抽取:利用 PDFPlumber 或 Vision API 读取 PDF 内容,提取出银行盖章确认的各项科目金额。
- 科目勾稽校验:将回函金额与总账余额做双向轧差,计算不符差额。
二、 Playwright 自动登录与拉取 Python 代码
以下为使用 Playwright 自动从询证中心抓取回函 PDF 的核心逻辑:
import asyncio
from playwright.async_api import async_playwright
async def download_confirmations(username, password, project_name):
async with async_playwright() as p:
# 使用本地持久化浏览器上下文,保持登录状态与 Cookie
browser = await p.chromium.launch_persistent_context(
user_data_dir="./playwright_profile",
headless=False,
args=["--disable-blink-features=AutomationControlled"]
)
page = await browser.new_page()
# 导航至询证函平台(示意网址)
await page.goto("https://confirmation.example.com/login")
# 填写登录凭证
await page.fill("#login-user", username)
await page.fill("#login-pass", password)
# 检测是否需要图形验证码,若需要则等待人工输入
if await page.query_selector("#verify-code-img"):
print("请在浏览器中手动输入验证码并点击登录...")
# 等待成功跳转到主页
await page.wait_for_url("**/dashboard", timeout=90000)
else:
await page.click("#btn-submit")
await page.wait_for_url("**/dashboard")
# 进入项目回函管理页
await page.goto(f"https://confirmation.example.com/projects?search={project_name}")
await page.wait_for_selector(".project-row")
# 点击所有已回函的下载链接
download_links = await page.query_selector_all("text='下载回函 PDF'")
for idx, link in enumerate(download_links):
async with page.expect_download() as download_info:
await link.click()
download = await download_info.value
save_path = f"./confirmations/{project_name}_{idx}.pdf"
await download.save_as(save_path)
print(f"成功保存回函至: {save_path}")
await browser.close()
# 启动下载任务
# asyncio.run(download_confirmations("user_audit", "secure_pass_123", "A_Technology"))三、 PDF 回函提取与科目勾稽比对
下载回函后,我们使用 pdfplumber 解析表格,并与财务明细表数据做自动比对:
import pdfplumber
def extract_and_match(pdf_path, ledger_balance):
"""
pdf_path: 回函 PDF 文件路径
ledger_balance: 账面金额(float)
"""
extracted_balance = None
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
for row in table:
# 寻找“银行存款余额”或“账户余额”等关键词所在行
if any(kw in str(row) for kw in ["银行存款", "余额", "Balance"]):
# 提取行中的数值(清除货币符号和逗号)
for cell in row:
cleaned = str(cell).replace(",", "").replace("¥", "").strip()
try:
extracted_balance = float(cleaned)
break
except ValueError:
continue
if extracted_balance is not None:
diff = round(extracted_balance - ledger_balance, 2)
if diff == 0.0:
return "SUCCESS", extracted_balance, 0.0
else:
return "DIFF_WARNING", extracted_balance, diff
return "EXTRACT_FAILED", None, None四、 总结
将 Playwright 的强大控制能力与大语言模型结合,我们能跨越任何无 API 接口的系统壁垒,自动将审计最繁重的手工作业流程化。这不仅是事务所年审提效的核心方法,更为数据审计的准确性建立了双保险。