Selenium + 自研小型 Agent:用脚本加简易agent搭建简单数据工作流
这篇文章想分享的不是「又一个爬虫」,而是两件事怎么拼在一起:
Selenium(以及公开接口)负责把网页世界变成结构化数据 ,
自研的小型 Agent Harness 负责听懂人话、选工具、把流水线跑完 。
文末附开源地址,可直接跑。
0. 先看结果长什么样
一句话下令之后,希望得到的是一张固定列的表,而不是一堆临时脚本碎片。
Excel / JSON 里大概是这样(示意,数据会随热榜变化):
| rank | title | author | hot_score | view_count | like_count | comment_count | sample_comments |
|---|---|---|---|---|---|---|---|
| 1 | 【MYSQL】...表的内外连接 | *** | 24050 | 970 | 43 | 41 | (抽样评论,可空) |
| 2 | 【Linux网络】深入 HTTP... | *** | 21071 | 1273 | 61 | 102 | ... |
| 3 | 一个人也能做AI漫剧... | *** | 20350 | 16580 | 15 | 4 | ... |
完整字段还包括:author_id、url、period、favor_count、fetched_at。
后面会讲这些数从哪来、为什么收藏经常是 0。
1. 原理:先把两层能力拆开
很多人一上来就「Agent 自己写爬虫」。短期能出活,长期很脆------模型每次临场发挥,接口一变全盘重写,还容易把 Cookie、频率控制写丢。
更稳的做法是分层:
text
┌─────────────────────────────────────────┐
│ 上层:小型 Agent(听懂 → 规划 → 调工具) │
│ 配方命中 → run_script → 验收 → 汇报 │
└──────────────────┬──────────────────────┘
│ 调用固定脚本
┌──────────────────▼──────────────────────┐
│ 下层:数据流水线(Selenium / HTTP) │
│ 拉榜 → 清洗 → 固定列 → xlsx/json/csv │
└─────────────────────────────────────────┘
1.1 Selenium 在解决什么问题
浏览器自动化的本质很简单:
- 启动真实浏览器(或无头 Chrome)
- 打开目标页,等待页面与脚本执行
- 再去拿数据(DOM、或页面触发过的接口)
对现代站点,页面上的列表往往不是静态 HTML,而是前端再请求 JSON 。
所以实战里常见两种姿势:
| 姿势 | 做法 | 特点 |
|---|---|---|
| A. 直接打 XHR | 用 urllib / requests 请求热榜接口 |
快、稳、无浏览器依赖 |
| B. Selenium 兜底 | 先用浏览器打开榜单页「暖环境」,再走同一套接口 | 慢一点,偶发风控时更像真人 |
我们项目的默认路径是 A;--selenium 走 B。
Selenium 不是主角炫技,而是「当纯 HTTP 不够像浏览器时」的补丁。
1.1.1 Selenium 关键代码(仓库原文)
下面这段就是 scripts/csdn_hot_pipeline.py 里的 fetch_with_selenium:无头 Chrome 打开榜单页,关掉浏览器后,仍然调用同一套 fetch_hot_rank HTTP 分页拉数。
python
def fetch_with_selenium(top: int) -> list[dict[str, Any]]:
"""
Selenium 兜底:先打开榜单页(带浏览器环境),再走同一 hot-rank 接口。
适合接口偶发风控时;日常推荐默认 HTTP 模式。
"""
try:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
except ImportError as exc:
raise SystemExit("缺少 selenium,请 pip install selenium") from exc
options = Options()
options.add_argument("--headless=new") # 无头,不弹窗
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--window-size=1280,900")
driver = webdriver.Chrome(options=options)
try:
driver.get("https://blog.csdn.net/rank/list") # 暖一下页面上下文
time.sleep(2)
finally:
driver.quit()
return fetch_hot_rank(top) # 真正取数仍走公开 XHR
真正分页拉榜的 HTTP 部分(Selenium 与默认模式共用):
python
HOT_RANK_URL = "https://blog.csdn.net/phoenix/web/blog/hot-rank"
def fetch_hot_rank(top: int, page_size: int = 25, sleep_s: float = 0.35) -> list[dict[str, Any]]:
items: list[dict[str, Any]] = []
page = 0
while len(items) < top:
qs = urllib.parse.urlencode({"page": page, "pageSize": page_size, "type": ""})
url = f"{HOT_RANK_URL}?{qs}"
payload = http_get_json(url) # 带 UA / Referer 的 GET JSON
if int(payload.get("code") or 0) != 200:
raise RuntimeError(f"hot-rank 失败 code={payload.get('code')}")
batch = payload.get("data") or []
if not batch:
break
items.extend(batch)
page += 1
if len(batch) < page_size:
break
time.sleep(sleep_s) # 限速,别打爆接口
return items[:top]
入口里如何切换两条路径:
python
if args.selenium:
raw = fetch_with_selenium(top)
else:
raw = fetch_hot_rank(top)
读代码时抓住三点即可:
- Selenium 只负责「像浏览器一样打开榜单页」,不在这里解析 DOM 抠一百条标题。
- 数据仍然来自
hot-rankJSON,和默认模式同一套清洗逻辑。 --sleep/time.sleep限速是必须的;开源分享时也建议读者别去掉。
依赖:pip install selenium,本机已装 Chrome(Selenium 4 一般会自动管理驱动)。
1.2 小型 Agent 在解决什么问题
自研 harness(我们叫它 mini-agent-harness)体量不大,但闭环是完整的:
text
用户自然语言
→ 意图 / 配方(这是不是「CSDN 热榜一条龙」?)
→ 对话态(缺不缺关键槽位?要不要追问?)
→ 任务分解(先跑脚本,再 profile 表格)
→ 工具执行(run_script / read_excel ...)
→ 验收与汇报
和「大而全 Agent 框架」相比,它刻意做小,方便研究:
- 工具是一等公民:爬虫能力不靠模型临场写,而靠仓库里的脚本
- 配方(Recipe):一类话术绑定推荐工具链
- 玻璃盒:过程可追踪,适合学习「Agent 到底在干什么」
关键原则一句话:
Agent 负责编排;Selenium/脚本负责取数。
编排可以换模型,取数逻辑应该版本可控、可单测、可开源。
1.3 两层拼在一起时,最容易踩的坑
若用户说:「清洗后导出 exports/csdn_hot_top100.xlsx」,
通用对话态容易理解成:「你已经有一张表,告诉我路径,我帮你导出」。
于是 Agent 开始追问文件路径------热榜任务还没开始就卡死了。
所以原理上还要多一步特判:命中「CSDN 热榜」配方时,这是生成新表 ,不是加工旧表;跳过 file 槽位澄清,直接 run_script。
2. 爬虫长什么样
脚本入口:scripts/csdn_hot_pipeline.py(约 300 行,可独立运行)。
2.1 数据从哪来
公开接口(页面 Network 里也能看到同类请求):
text
热榜:https://blog.csdn.net/phoenix/web/blog/hot-rank?page=0&pageSize=25&type=
评论:https://blog.csdn.net/phoenix/web/v1/comment/list?articleId=...
流程:
text
分页拉热榜 Top N
→ 字段归一化(标题、作者、热度、阅读、点赞...)
→ 可选:每篇抽样 N 条评论内容
→ 写出 xlsx(默认)+ json(默认)+ 可选 csv
2.2 清洗时要注意的「脏细节」
热榜原始 JSON 字段名并不「人类友好」,例如:
- 接口里的
favorCount,语义上更接近点赞,不是收藏 - 收藏字段经常缺失 → 导出里
favor_count常为0 - 阅读量可能是
"2.4w"这种字符串 → 要先解析成整数 - 评论内容可能带 HTML → 简单剥标签再截断
归一化后的一行,概念上类似:
json
{
"rank": 1,
"title": "【MYSQL】MYSQL学习的一大重点:表的内外连接",
"author": "艾莉丝努力练剑",
"author_id": "2401_89899187",
"url": "https://blog.csdn.net/.../article/details/163200338",
"period": "2026-07-30-14",
"hot_score": 24050,
"view_count": 970,
"like_count": 43,
"favor_count": 0,
"comment_count": 41,
"sample_comments": "",
"fetched_at": "2026-07-30 15:11:07"
}
固定列(给 Agent / Excel 对齐用):
text
rank, title, author, author_id, url, period,
hot_score, view_count, like_count, favor_count,
comment_count, sample_comments, fetched_at
2.3 命令行长什么样
不经过 Agent,直接跑流水线:
bash
pip install openpyxl
# 默认 HTTP,Top100,每篇抽 2 条评论
python scripts/csdn_hot_pipeline.py \
--top 100 \
--enrich-comments 2 \
--out exports/csdn_hot_top100.xlsx \
--also-csv
# 接口偶发不稳时,打开 Selenium 兜底(需本机 Chrome)
pip install selenium
python scripts/csdn_hot_pipeline.py --top 100 --selenium --out exports/csdn_hot_top100.xlsx
终端大致会看到:
text
[csdn] fetch top=100 selenium=False
[csdn] got 100 raw items, cleaning...
cleaned 10/100
...
[csdn] done rows=100
[csdn] xlsx=.../exports/csdn_hot_top100.xlsx
3. Agent 侧长什么样
对 harness 说人话即可(REPL):
text
/forget
抓取 CSDN 今天热榜前 100,带点赞收藏评论和评论内容,清洗后导出 exports/csdn_hot_top100.xlsx
内部推荐链路:
text
命中配方 csdn-hot-pipeline
→ run_script scripts/csdn_hot_pipeline.py
args: --top 100 --enrich-comments 2 --out ... --also-csv
→ profile_dataset / read_excel 确认行列
→ 用自然语言汇报结果路径
也就是说:读者看到的「智能」,一大半是配方 + 固定脚本;模型负责选择与解释,而不是每次重写爬虫。
4. 开源了什么、怎么用
项目已开源(MIT):
仓库: https://gitcode.com/z996262114/agent-harness-tool
你可以重点看这些文件:
| 路径 | 内容 |
|---|---|
scripts/csdn_hot_pipeline.py |
Selenium / HTTP 抓取 + 清洗 + 导出 |
src/policy/DataRecipes.ts |
配方 csdn-hot-pipeline |
src/policy/DialogueState.ts |
热榜任务跳过「缺文件」误澄清 |
src/policy/TaskDecomposer.ts |
优先 run_script |
docs/本地使用-CSDN热榜工作流.md |
本机一步步跑通 |
快速体验 Agent:
bash
git clone https://gitcode.com/z996262114/agent-harness-tool.git
cd agent-harness-tool # 或你本地的 mini-agent-harness 目录
npm install
# 配置 DeepSeek API Key:key.txt 或环境变量
npm run repl
本地上手文档写得更细,适合照着敲命令。
5. 写给正在研究的人
- 先独立跑通脚本,再挂到 Agent 上------出问题先分清是爬虫层还是编排层。
- 把输出列定死,Agent 才好验收;列名天天变,上层永远在修提示词。
- Selenium 当兜底,日常优先接口;浏览器自动化贵且慢。
- 限速、学习用途、尊重内容版权------接口可能变更,代码不保证永久可用。
6. 结语
这篇文章想留下的画面很简单:
- 原理:Selenium/HTTP 取数,小型 Agent 编排;两层不要揉成一团。
- 样子:固定列的热榜表 + 可独立运行的 Python 流水线。
- 开源:MIT 仓库已公开,欢迎直接跑、提 Issue / PR。
如果你也在做「Agent + 真实数据任务」,不妨试试:先把脏活收成脚本,再让 Agent 学会按铃。 往往比指望模型每次现场发明轮子,更接近能长期维护的工程。