ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenCode 浏览器控制实战:Selenium 复用 Chrome 登录态实现企业批量信息提取

OpenCode 浏览器控制实战:Selenium 复用 Chrome 登录态实现企业批量信息提取 1. 为什么企业尽调场景需要复用 Chrome 登录态做股权穿透、关联企业筛查、投融资背景核查时绕不开一个动作把目标公司的自然人股东全部拉出来再逐个进入股东个人页翻完他名下所有任职企业。一个中等规模的主体股东二三十人每人任职企业十几家手动点下来就是几百次页面跳转眼睛和手都受不了。真正麻烦的不是翻页本身而是登录。企业信息平台对未登录用户的可见字段做了大量裁剪股东持股比例、任职明细这些关键数据往往要登录后才完整展示。如果脚本里用账号密码登录会频繁触发滑块验证、短信校验跑不了几十条就被风控拦下。所以更稳的思路是复用你本机 Chrome 里已经登录好的会话让 Selenium 挂到这个已经带 Cookie 的浏览器实例上脚本只负责点页面、抓数据不碰登录流程。这套方案的核心技术点是 Chrome 的 CDP 远程调试端口。Chrome 启动时加一个--remote-debugging-port参数就会在本地开一个调试服务Selenium 通过debuggerAddress连上去等于直接接管你眼前这个已经登录的浏览器。本文就把这套流程拆成可复制的配置和代码从启动参数到批量提取的验证动作一次讲清。适合做尽调、投研、企业背景分析且有一定 Python 基础的同学跟做。2. TaoToken 在批量提取链路里的前置准备脚本要跑起来除了 Selenium 和 Chrome还需要一个稳定的模型调用入口来处理提取过程中的字段清洗、企业名称标准化、异常判断这类需要语义理解的动作。比如从一堆任职企业里区分「存续」和「注销」或者把「XX科技上海有限公司」和「XX科技上海分公司」归并成同一主体纯正则很难覆盖全交给模型判断更省事。我这边用的是 TaoToken 的 API 来做这层处理。它的接入地址是https://taotoken.net/api兼容常见的 OpenAI 风格调用格式Python 里用requests或openaiSDK 都能直接打。你需要先去控制台拿一个 API Key然后在脚本里配好 base_url 和 key 即可。具体操作路径打开官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content进入控制台创建 API Key地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite。拿到 key 后模型对话调试可以用https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite先验证连通性。如果你后面要把这套采集做成长期跑的 Agent 任务可以看下 Coding Plan 页面https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。注意TaoToken 在这里的角色是「数据后处理与语义判断的模型入口」不是浏览器代理也不参与页面请求。页面抓取仍然走你本机 Chrome 的真实会话两者职责分开链路才清晰。3. 可复制的 config.toml 与 settings.json 配置骨架为了让端口、路径、等待时长这些参数不散落在代码里建议抽成配置文件。下面这份config.toml覆盖了浏览器连接、采集行为、模型后处理三块直接改值就能用。# config.toml [browser] # Chrome 远程调试端口默认 9222 debug_port 9222 # 本机 Chrome 用户数据目录Windows 示例macOS/Linux 换成对应路径 user_data_dir C:/Users/yourname/AppData/Local/Google/Chrome/User Data # 页面加载超时秒 page_load_timeout 30 # 元素显式等待超时秒 element_wait_timeout 15 [collect] # 目标企业清单文件 company_list_file companies.txt # 输出目录 output_dir ./output # 翻页随机延时区间秒 delay_min 1.5 delay_max 3.5 # 单股东任职企业最大翻页数防止死循环 max_pages_per_shareholder 50 [llm] # TaoToken API 入口 base_url https://taotoken.net/api api_key sk-你的key model gpt-4o-mini # 是否启用模型做企业名称标准化 enable_name_normalize true对应的settings.json用来存一些运行时状态和字段映射方便脚本读取{ field_map: { shareholder_name: 股东姓名, share_ratio: 持股比例, profile_link: 个人主页, company_name: 企业名称, position: 职位 }, output: { json_file: result.json, txt_file: readable_result.txt, excel_file: result.xlsx }, dedup: { by_company_id: true, normalize_name: true } }读取配置的代码很短用tomllibPython 3.11或toml库都行import tomllib import json with open(config.toml, rb) as f: config tomllib.load(f) with open(settings.json, r, encodingutf-8) as f: settings json.load(f) DEBUG_PORT config[browser][debug_port] USER_DATA_DIR config[browser][user_data_dir]这样改端口、改路径、改延时都不用动主逻辑换台电脑只改config.toml一处。4. 启动 Chrome 并挂载 Selenium 的完整步骤4.1 手动启动带调试端口的 Chrome关键点不要用 Selenium 自己新建一个 Chrome 实例那样会开一个全新的用户数据目录登录态是空的。正确做法是先手动启动 Chrome指定调试端口和已有的用户数据目录。Windows 下命令chrome.exe --remote-debugging-port9222 --user-data-dirC:/Users/yourname/AppData/Local/Google/Chrome/User DatamacOS 下/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port9222 --user-data-dir/Users/yourname/Library/Application Support/Google/Chrome启动后浏览器里应该已经是你平时登录好的状态。可以先访问一下目标平台确认登录还在。然后打开http://127.0.0.1:9222/json/version能看到返回的 JSON 就说明调试端口通了。4.2 Selenium 挂载现有实例from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() options.add_experimental_option(debuggerAddress, f127.0.0.1:{DEBUG_PORT}) driver webdriver.Chrome(optionsoptions) # 验证挂载成功打印当前页面标题 print(driver.title)如果这一步报Connection refused说明 Chrome 没起来或者端口不对如果报user data directory is already in use说明你重复启动了 Chrome把多余的进程关掉再试。4.3 搜索企业并进入主页直接拼搜索 URL 比模拟输入框点击稳定得多中文和特殊符号用quote编码import urllib.parse import time def search_company(driver, company_name): search_url fhttps://www.tianyancha.com/search?key{urllib.parse.quote(company_name)} driver.get(search_url) time.sleep(2) # 抓取第一条 /company/ 链接 links driver.find_elements(css selector, a[href*/company/]) if not links: return None return links[0].get_attribute(href)4.4 解析股东表格不要用固定 XPath页面一改版就废。更稳的做法是全局找包含「股东信息」文本的节点向上回溯到最近的table再遍历行def parse_shareholders(driver): shareholders [] # 找到包含「股东信息」的容器 nodes driver.find_elements(xpath, //*[contains(text(),股东信息)]) if not nodes: return shareholders table nodes[0].find_element(xpath, ./ancestor::table[1]) rows table.find_elements(tag name, tr) for row in rows[1:]: # 跳过表头 cells row.find_elements(tag name, td) if len(cells) 3: continue name_el cells[1].find_elements(tag name, a) name name_el[0].text if name_el else cells[1].text ratio cells[2].text link name_el[0].get_attribute(href) if name_el else shareholders.append({name: name, ratio: ratio, link: link}) return shareholders4.5 抓取股东任职企业并翻页进入股东个人页后切到「所有任职企业」标签定位表头同时含「职位」和「企业名称」的表格逐行取企业名然后循环点下一页def parse_companies(driver, max_pages50): companies [] seen set() for _ in range(max_pages): tables driver.find_elements(css selector, table) target None for t in tables: header t.text if 职位 in header and 企业名称 in header: target t if not target: break rows target.find_elements(tag name, tr) for row in rows[1:]: cells row.find_elements(tag name, td) if len(cells) 2: continue name cells[1].text.strip() if name and name not in seen: seen.add(name) companies.append(name) # 找下一页按钮 next_btns driver.find_elements(xpath, //a[contains(text(),下一页)]) if not next_btns or disabled in next_btns[0].get_attribute(class): break next_btns[0].click() time.sleep(2) return companies4.6 用 TaoToken 做企业名称标准化抓下来的企业名可能有「XX科技上海有限公司」和「XX科技上海分公司」这种变体去重前先过一遍模型import requests def normalize_names(names, config): prompt 请把以下企业名称做标准化归并输出JSON数组相同主体的名称合并为一项\n \n.join(names) resp requests.post( f{config[llm][base_url]}/v1/chat/completions, headers{Authorization: fBearer {config[llm][api_key]}}, json{ model: config[llm][model], messages: [{role: user, content: prompt}] }, timeout60 ) return resp.json()[choices][0][message][content]5. 验证请求与成功结果跑通的标准动作分三步。第一步确认挂载成功脚本启动后打印driver.title应该是你当前 Chrome 打开的页面标题而不是空白页。第二步确认登录态复用访问目标企业主页检查股东表格是否有数据如果返回的是登录引导页说明 Cookie 没带上回到 4.1 检查user-data-dir是否指向了你平时登录的那个目录。第三步确认批量提取完整以一家股东数 20 的企业为例脚本跑完后result.json里应该有 20 条以上股东记录每条带companies数组数组长度和页面上「所有任职企业」显示的总数对得上。一个正常的输出片段长这样{ company: 目标企业名称, shareholders: [ { name: 张三, ratio: 35%, companies: [A科技有限公司, B信息技术有限公司, C投资合伙企业] } ] }如果companies数组为空但页面上明明有数据多半是表格定位选错了参考下一节的排查。6. 本篇常见错误排查报错一Connection refused连不上 9222。原因通常是 Chrome 没带调试参数启动或者端口被占用。先确认http://127.0.0.1:9222/json/version能打开打不开就换端口比如 9223同时改config.toml里的debug_port。报错二user data directory is already in use。你重复启动了 Chrome。任务管理器里把所有 chrome.exe 进程结束只保留一个带调试参数的实例。报错三翻页后数据不变重复抓同一页。页面里可能有多套分页控件误点了无关的那个。解决办法是通过 DOM 父子关系把分页容器绑定到当前数据表的父节点上而不是全局找「下一页」。报错四部分股东没有个人主页链接。有些股东是法人主体或未实名展示没有/human/页面。脚本里要单独捕获标记为「无链接」跳过不要让它中断整个任务。报错五任职企业不足 10 条时表格匹配失效。如果之前用「行数≥10」来判断目标表格小数据量就会漏。改成「表头关键词 父容器 class 分页绑定」三重特征定位不依赖行数。报错六切换标签后抓到空白数据。标签点击后接口是异步返回的直接抓会拿到旧 DOM。点击后加显式等待等表格行数大于 0 或 Loading 遮罩消失再解析。报错七脚本异常退出后残留大量 Chrome 进程。在finally块里统一执行driver.quit()但注意复用模式下quit()会关掉你手动启动的浏览器如果不想关改成只close()当前标签页。7. 接入与后续动作这套方案跑通后日常尽调的单企业采集基本够用了。如果你要把它做成批量任务比如一次导入几十家企业清单循环跑建议把浏览器连接层、搜索模块、股东解析、任职抓取、数据输出拆成独立模块参数全部走config.toml这样网站改版时只改常量不用动主逻辑。模型后处理这块API Key 在控制台创建https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入细节看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。如果你用的是 Claude Code 这类编码工具来维护这套脚本Anthropic 兼容入口在https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentClaudeCodeAnthropicutm_campaignrewrite长期跑 Agent 任务可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite。最后提醒一句采集频率一定要加随机延时单账号高频请求会触发滑块甚至封号。这套代码的定位是合法尽调场景下的效率工具别拿去做恶意爬取。页面 DOM 和接口会不定期改版解析失效时按「常量抽离」的思路快速适配就行。
返回列表