基于Playwright的企业信用代码自动化查询工具开发实践

发布时间:2026/7/17 1:34:50
基于Playwright的企业信用代码自动化查询工具开发实践 1. 项目概述与核心价值最近在对接一个供应链管理系统需要批量录入合作商家的基础信息其中“统一社会信用代码”是必填项。手动去天眼查一个个复制粘贴面对几百家供应商名单这活儿简直能让人干到怀疑人生。作为一个老程序员我的第一反应当然是能不能用Python写个脚本让它自动去查这个想法催生了今天要分享的这个自动化项目。它的核心目标非常明确输入一个商家名称列表程序能自动访问天眼查精准地找到对应企业并提取出其统一社会信用代码最终整理成结构化的数据比如Excel或CSV文件。这不仅仅是简单的“爬虫”而是一个结合了网络请求、反爬策略应对、HTML解析、数据匹配和流程控制的综合性自动化工具。对于需要处理大量企业信息核验、数据清洗、或系统初始化的财务、风控、运营和开发同学来说能极大提升效率把人力从重复、枯燥的机械劳动中解放出来。整个流程模拟了人工操作的关键步骤打开网页、输入关键词、从搜索结果中识别目标公司、进入详情页、定位并复制信用代码。接下来我就把搭建这个自动化工具的思路、踩过的坑以及核心代码实现毫无保留地拆解给你看。2. 核心思路与技术选型解析2.1 为什么选择Playwright而非Selenium或Requests在决定技术方案时我主要评估了三种主流方案直接requests发包、Selenium和Playwright。requestsBeautifulSoup/parsel方案这是最轻量、最快的方案。但天眼查这类商业网站的反爬机制非常完善包括但不限于请求头校验、Cookie验证、参数加密、IP频率限制等。直接构造请求的难度和后期维护成本极高一个小的网站改版就可能导致整个解析逻辑失效。对于需要稳定、长期运行的自动化任务这不是首选。Selenium方案这是传统的浏览器自动化方案能完美模拟真人操作绕过大部分前端反爬。但它也有明显短板速度相对较慢对浏览器驱动版本管理比较繁琐并且在高并发或资源受限的环境下不够稳定。Playwright方案这是微软开源的现代浏览器自动化库也是我最终的选择。它继承了Selenium模拟真人的优点同时在多个维度上表现更优自动等待内置智能等待机制能自动等待元素加载、网络请求完成大大减少了编写显式等待time.sleep的代码脚本更健壮。多浏览器支持一套API支持Chromium、Firefox和WebKit调试和兼容性测试更方便。强大的选择器支持CSS、XPath以及Playwright独有的text、has等语义化选择器定位元素非常灵活。网络拦截与模拟可以轻松监听和修改网络请求这对于处理动态加载的数据或绕过某些检测很有用。无头模式性能好在无头模式下运行资源占用更少速度更快非常适合服务器环境。实操心得对于需要与复杂JavaScript交互、且反爬严格的现代Web应用Playwright是目前平衡了易用性、功能性和稳定性的最佳选择。它的学习曲线比Selenium更平缓社区活跃问题也更容易找到解决方案。2.2 整体流程设计整个自动化脚本的流程可以抽象为以下几个核心步骤形成一个清晰的管道Pipeline输入与初始化读取包含商家名称的源文件如companies.txt或input.csv初始化Playwright浏览器和上下文。导航与搜索控制浏览器打开天眼查首页在搜索框输入商家名称触发搜索。结果列表解析等待搜索结果页面加载完成从结果列表中定位最匹配的目标公司条目。这里的关键是如何从一堆相似名称中准确识别。详情页跳转点击目标公司条目进入公司详情页。关键信息提取在详情页中定位到包含“统一社会信用代码”的HTML元素提取其文本内容。数据存储与容错将“商家名称”和提取到的“信用代码”配对保存到输出文件如Excel。同时必须处理各种异常情况搜索无结果、网络超时、元素定位失败等并记录日志。循环与收尾处理完一个商家后回到步骤2直到列表中的所有商家处理完毕最后关闭浏览器释放资源。这个流程设计确保了脚本的模块化和可维护性每个环节都可以独立调试和优化。3. 环境准备与核心依赖安装3.1 Python环境搭建确保你有一个可用的Python环境3.7及以上版本。推荐使用conda或venv创建独立的虚拟环境避免包冲突。# 创建虚拟环境以venv为例 python -m venv venv_tianyancha # 激活虚拟环境 # Windows: venv_tianyancha\Scripts\activate # Linux/Mac: source venv_tianyancha/bin/activate3.2 安装必要的Python库核心库就是playwright。此外为了更方便地处理数据我们还会用到pandas。# 安装playwright库 pip install playwright # 安装playwright的浏览器驱动Chromium, Firefox, WebKit playwright install chromium # 通常安装Chromium就够了更轻量 # 安装pandas用于数据读写 pip install pandas openpyxl # openpyxl用于读写Excel的.xlsx格式playwright install这一步会下载浏览器二进制文件可能需要一些时间请确保网络通畅。3.3 项目目录结构建议一个清晰的项目结构有助于后期维护。tianyancha_auto_scraper/ ├── main.py # 主脚本 ├── config.py # 配置文件如超时时间、输出路径 ├── companies.txt # 输入待查询的商家名称列表每行一个 ├── output/ │ └── results.xlsx # 输出查询结果Excel文件 ├── logs/ │ └── scraper.log # 运行日志 └── requirements.txt # 项目依赖列表4. 核心代码实现与分步详解下面我将分模块详细讲解核心代码的实现。请注意以下代码仅为示例用于说明思路和关键操作实际使用时需根据天眼查网站的实际HTML结构进行调整。4.1 主流程框架搭建首先我们构建脚本的主干定义好输入输出和核心函数。import asyncio import pandas as pd import logging from playwright.async_api import async_playwright from typing import Optional, List, Tuple import sys # 配置日志 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(logs/scraper.log), logging.StreamHandler(sys.stdout) ] ) logger logging.getLogger(__name__) async def get_credit_code_by_company_name(page, company_name: str) - Optional[str]: 核心函数根据公司名获取信用代码。 返回信用代码字符串如果失败则返回None。 credit_code None try: # 步骤1 2: 导航到天眼查并搜索 await page.goto(https://www.tianyancha.com, timeout30000) await page.wait_for_load_state(networkidle) # 定位搜索框并输入 search_input page.locator(input[placeholder*企业]) # 根据实际placeholder调整 await search_input.fill(company_name) await search_input.press(Enter) # 步骤3: 等待结果并点击第一个或最匹配的结果 await page.wait_for_selector(.search-result-container, timeout10000) # 结果容器选择器 first_result page.locator(.search-item).first # 第一个结果项选择器 await first_result.click() # 步骤4 5: 等待详情页加载并提取信用代码 await page.wait_for_selector(.company-header, timeout10000) # 详情页头部选择器 # 关键定位信用代码元素。这里需要你手动分析天眼查详情页的HTML结构。 # 通常信用代码在一个包含“统一社会信用代码”文本的标签附近。 # 示例使用XPath根据相邻文本节点定位 credit_code_element page.locator(xpath//div[contains(text(), 统一社会信用代码)]/following-sibling::div) # 或者使用CSS选择器结合:has伪类如果Playwright版本支持 # credit_code_element page.locator(div:has-text(统一社会信用代码) div) if await credit_code_element.count() 0: credit_code (await credit_code_element.first.text_content()).strip() logger.info(f成功获取 {company_name} 的信用代码: {credit_code}) else: logger.warning(f在 {company_name} 的详情页未找到信用代码元素) except Exception as e: logger.error(f处理公司 {company_name} 时发生错误: {e}) finally: # 可选处理完一个稍微延迟一下或者清空搜索框为下一个做准备 await page.goto(https://www.tianyancha.com) return credit_code async def main(): 主异步函数 # 读取公司列表 with open(companies.txt, r, encodingutf-8) as f: company_names [line.strip() for line in f if line.strip()] if not company_names: logger.error(公司列表为空请检查companies.txt文件。) return results [] # 启动Playwright async with async_playwright() as p: # 启动浏览器建议使用无头模式headlessTrue在生产环境运行 browser await p.chromium.launch(headlessFalse) # 调试时可设为False看浏览器操作 context await browser.new_context( viewport{width: 1920, height: 1080}, user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... # 设置UA ) page await context.new_page() for company in company_names: logger.info(f开始查询: {company}) code await get_credit_code_by_company_name(page, company) results.append({公司名称: company, 统一社会信用代码: code if code else 查询失败}) # 每个查询间隔一下避免请求过快 await asyncio.sleep(2) # 关闭浏览器 await browser.close() # 保存结果到Excel df pd.DataFrame(results) output_path output/results.xlsx df.to_excel(output_path, indexFalse) logger.info(f查询完成结果已保存至: {output_path}) print(df) # 在控制台也打印一下 if __name__ __main__: asyncio.run(main())4.2 关键环节深度解析元素定位与反反爬策略上面的框架代码中最核心也最易出问题的就是元素定位和反反爬应对。1. 精准的元素定位策略天眼查的页面结构可能会变化所以不能硬编码选择器。你需要使用浏览器开发者工具F12手动分析。搜索框查看input标签的id、name、class或placeholder属性。page.locator(input[placeholder*企])是一个相对稳健的选择。搜索结果列表打开搜索结果页找到包裹每个公司信息的容器元素的共同CSS类如.search-item或.company-item。使用.first、.nth(index)或通过公司名文本内容过滤locator(text公司名)来定位具体条目。信用代码这是难点。在详情页找到显示“统一社会信用代码”的标签。通常它是一个dt、span或div。然后找到紧邻其后存放代码值的元素。XPath在这里非常强大# 方案1寻找包含特定文本的节点然后取其后兄弟节点 xpath1 //div[contains(text(), 统一社会信用代码)]/following-sibling::div # 方案2寻找特定标题然后找其父容器下的值节点 xpath2 //span[text()统一社会信用代码]/../following-sibling::td/span务必在开发者工具的Elements面板中使用CtrlF测试你的XPath确保它能唯一定位到目标元素。2. 应对反爬机制模拟真人行为Playwright本身已经模拟了真实浏览器。但可以进一步优化await page.wait_for_load_state(networkidle)等待页面基本加载完。在关键操作如点击、输入前后加入随机短延迟await asyncio.sleep(random.uniform(0.5, 2))。使用page.mouse.move()模拟鼠标移动轨迹。管理浏览器上下文new_context时可以传入user_agent、viewport模拟不同设备。可以持久化Cookie避免每次登录。但天眼查可能需要登录才能查看更多信息这涉及到更复杂的会话管理。# 保存和加载Cookie await context.storage_state(pathauth_cookies.json) context await browser.new_context(storage_stateauth_cookies.json)处理验证码如果触发验证码自动化将面临巨大挑战。策略包括降低频率拉长请求间隔是成本最低的规避方式。第三方打码平台接入付费API进行识别如超级鹰、图鉴等但会增加复杂度和成本。人工介入脚本检测到验证码时暂停并提示人工处理处理后再继续。注意事项商业网站的数据受法律保护爬取行为必须遵守其robots.txt协议和服务条款。本示例仅用于技术学习交流请务必控制爬取频率和数量避免对目标网站造成负担切勿用于商业数据抓取或侵犯他人权益。4.3 增强健壮性错误处理与重试机制一个生产级的脚本必须能优雅地处理各种异常。import random from tenacity import retry, stop_after_attempt, wait_random_exponential retry(stopstop_after_attempt(3), waitwait_random_exponential(multiplier1, max10)) async def robust_search_and_extract(page, company_name): 带有重试机制的查询函数 try: # ... (原有的搜索和提取逻辑) # 在可能失败的步骤比如点击、等待元素加入更详细的try-catch await page.wait_for_selector(.some-element, statevisible, timeout15000) # ... return credit_code except Exception as e: logger.warning(f尝试处理 {company_name} 时失败: {e}) # 重试前可以尝试刷新页面或回到首页 await page.goto(https://www.tianyancha.com) raise e # 触发tenacity重试 # 在主循环中调用 for company in company_names: try: code await robust_search_and_extract(page, company) except Exception as final_e: logger.error(f公司 {company} 经过重试后仍失败: {final_e}) code None results.append(...)使用tenacity库可以方便地实现指数退避重试这是处理网络不稳定问题的常见模式。4.4 数据匹配优化处理搜索结果不唯一当搜索一个常见名称如“XX科技”时会返回大量结果。我们需要找到最准确的那个。async def select_best_match(page, company_name): 从搜索结果中选择最佳匹配项 await page.wait_for_selector(.search-item, timeout10000) all_items await page.locator(.search-item).all() best_match None best_score -1 for item in all_items: # 提取结果中的公司名 name_element item.locator(.company-name) # 需根据实际结构调整 if await name_element.count() 0: continue item_name (await name_element.text_content()).strip() # 简单的相似度计算可根据需要引入fuzzywuzzy等库 # 这里使用简单的in操作更优解是计算编辑距离或使用正则 score 0 if company_name item_name: score 100 # 完全匹配 elif company_name in item_name: score 80 # 包含关系 # 可以添加更多规则比如匹配地区关键词 if score best_score: best_score score best_match item if best_match and best_score 50: # 设定一个阈值 await best_match.click() return True else: logger.warning(f未找到与 {company_name} 足够匹配的结果最佳匹配分: {best_score}) return False在get_credit_code_by_company_name函数中用select_best_match替换直接点击第一个结果的逻辑可以大幅提升准确率。5. 常见问题排查与实战技巧在实际运行中你几乎一定会遇到下面这些问题。这里是我的排查清单和经验总结。5.1 元素定位失败TimeoutError这是最常见的问题。可能原因1选择器过时或写错。网站改版了。排查手动打开浏览器使用开发者工具检查目标元素的结构是否变化。更新代码中的CSS选择器或XPath。可能原因2页面加载太慢或元素动态加载。排查增加timeout参数。使用wait_for_selector的不同状态visible,attached,hidden。对于动态加载的内容尝试等待特定的网络请求完成await page.wait_for_response(**/some-api/**)。可能原因3页面内有iframe。排查如果目标元素在iframe内需要先定位到iframe对象然后切换上下文。frame page.frame(frame-name) # 通过name或URL # 或者 frame page.frames[1] # 通过索引 element_on_frame frame.locator(selector)5.2 被网站检测并屏蔽现象弹出验证码、返回空白页、跳转到异常页面、IP被暂时封禁。应对策略降低频率这是首要措施。在查询间加入随机且足够长的延迟如5-15秒。await asyncio.sleep(random.uniform(5, 15))。使用更真实的浏览器环境在browser.new_context()中设置更完整的user_agent、viewport、locale、timezone_id等。使用代理IP对于大规模抓取轮换代理IP池是必须的。context await browser.new_context(proxy{ server: http://your-proxy-server:port, # 如果需要认证 username: user, password: pass })避免模式化操作随机化操作顺序如先移动鼠标再点击、输入速度使用page.type模拟逐字输入。5.3 数据提取不准确或为空可能原因1提取的文本包含多余空格或不可见字符。处理使用.strip()清理首尾空格。对于复杂情况用正则表达式提取核心部分import re; code re.search(r[A-Z0-9]{18}, raw_text).group()。可能原因2信息在更深层的页面如“工商信息”标签页内。处理可能需要先点击切换标签页再等待该标签页下的内容加载。await page.click(text工商信息) # 点击工商信息标签 await page.wait_for_selector(.business-info-content, statevisible) # 等待内容出现 # 然后再定位信用代码5.4 脚本运行速度慢优化1启用无头模式await chromium.launch(headlessTrue)。这是最大的性能提升点。优化2复用浏览器上下文避免为每个查询都打开关闭浏览器。我们的主循环设计就是复用同一个page对象。优化3并行处理高级如果拥有大量代理IP且网站容忍度较高可以考虑使用asyncio.gather控制有限并发。警告并发会极大增加被封风险请谨慎评估并严格遵守法律法规和网站规定。5.5 实战技巧速查表问题场景可能原因快速排查/解决方案页面白屏/不加载网络问题、JS执行错误1. 检查网络。2. 关闭无头模式(headlessFalse)观察。3. 捕获页面console日志page.on(console, lambda msg: print(msg.text))。点击/输入无效元素被遮挡、未处于交互状态1. 使用locator.click(forceTrue)强制点击。2. 先locator.hover()再点击。3. 使用page.evaluate()执行JS点击。验证码频繁出现行为被识别为机器人1.首要大幅降低请求频率增加随机延迟。2. 模拟更真实的鼠标移动轨迹。3. 考虑购买商业IP代理服务。结果匹配错误搜索词不精确有大量同名公司1. 优化select_best_match函数加入更多匹配维度地域、行业标签。2. 在输入数据中预先补充更精确的关键字如“北京XX科技有限公司”。6. 项目扩展与进阶思路这个基础脚本可以作为一个起点根据实际需求进行扩展信息扩展除了信用代码还可以抓取法定代表人、注册资本、注册地址、经营范围、经营状态等。数据源扩展适配其他企业信息平台如企查查、启信宝抽象出统一的接口哪个平台有数据就用哪个。流程集成将脚本封装成API服务使用FastAPI供其他系统调用或者与RPA工具如n8n、影刀结合嵌入更复杂的业务流程。可视化与监控增加一个简单的Web仪表盘展示查询进度、成功率并发送失败告警通过邮件、钉钉、企业微信。持久化与增量更新将结果存入数据库如SQLite、MySQL并记录查询时间。下次运行时只查询新增或需要更新的公司。这个项目的核心价值不在于那几十行抓取代码而在于构建一个稳定、健壮、可维护的自动化流程。从环境搭建、反爬策略到错误处理和性能优化每一个环节都考验着开发者的工程化思维。希望这份详细的拆解能帮你不仅实现“自动获取信用代码”这个具体功能更能掌握用Playwright解决一类Web自动化问题的通用方法。在实际操作中耐心调试元素选择器合理控制请求节奏你的自动化脚本就能稳定可靠地运行起来。