用 browser-use 纯 DOM 提取京东商品:踩坑与一根本地代理解决 Gemini 断连
最近在折腾用 browser-use 自动抓取京东搜索结果,过程中把”browser-use 必须靠视觉模型””免费模型跑不动 Agent””Gemini 动不动断连”这几个误区逐一踩了一遍。本文记录完整的排查思路和最终可复用的方案,给同样在这条路上卡住的人省点时间。
误区一:browser-use 需要视觉模型才能”看懂”页面?
不是。browser-use 的默认工作模式(use_vision=False)只读 DOM 快照——也就是页面的 HTML 文本结构和元素文本——交给 LLM 做决策和提取。视觉模型(use_vision=True)只是在页面布局特别复杂、需要”看截图”时才加的辅助,每一步还会多烧一次视觉调用。
我们的任务很固定:打开京东搜索页 → 读商品列表。URL 已知、结构已知,LLM 做”下一步点哪、滚不滚、提取完没”的决策在这里其实是冗余开销,但 DOM 提取本身完全不依赖视觉。结论:纯 DOM 模式不仅可行,还比视觉模式省 token。
误区二:免费模型撑不住 browser-use Agent?
半对。Agent 每步确实要调一次 LLM 做决策,免费模型连续跑十几步会撞配额。但真正让我前面反复崩溃的,不是限流,是传输层断连——下面详细说。
真正的坑:Gemini genai SDK 经 SOCKS5 的 “Server disconnected”
我的环境出网走本地 SOCKS5 隧道(127.0.0.1:1080,日本 OCI 出口)。genai SDK(google.genai)默认会用 HTTPS_PROXY=socks5://... 直连,问题是它对 Gemini 是长连接,免费档响应慢时连接池被掐断,抛出 ServerDisconnectedError。每步都崩,Agent 自然跑不完。
我用 curl 走同样的 SOCKS5 却稳如老狗——说明隧道本身没问题,是 genai/httpx 的 SOCKS5 长连接实现有坑。
修复:一根无超时的本地 HTTP 代理
写一个纯标准库的小代理,监听 127.0.0.1:8899,把请求经 SOCKS5 转发出去。关键两点:
- 不设硬超时:旧版本给每条连接设了 15 秒超时,Gemini 慢响应直接被掐。改成
settimeout(None),让连接活到自然结束。 - 正确处理 CONNECT 隧道:HTTPS 走
CONNECT方法建隧道,双向 pipe 转发,不在应用层解析内容。
genai 改成走 HTTPS_PROXY=http://127.0.0.1:8899,本地 TCP 稳定不断。实测连打 6 次 Gemini:ok=6 fail=0,零断连。
另一个坑:Chrome 不能走这个代理
如果进程级把 HTTPS_PROXY 设成 8899,browser-use 起的 Chrome 也会经由它访问京东——而京东是国内直连 IP,被 HTTP 代理一裹反而连不上,browser-use 的 watchdog 30 秒超时直接崩。
修复:给 Browser 加参数强制 Chrome 直连,只让 genai 走代理:
browser = Browser(
executable_path=CHROME,
headless=False,
args=['--proxy-server=direct://', '--proxy-bypass-list=*'], # Chrome 直连国内站
storage_state=STORAGE,
viewport={'width': 1280, 'height': 800},
)
genai 的环境变量代理和 Chrome 的直连参数各管各的,互不干扰。
登录态与那个无害的警告
browser-use 用 storage_state 注入京东 cookie(一次扫码登录后导出)。它会打印一条警告:
⚠️ BrowserSession was passed both storage_state AND user_data_dir…
意思是它内部自动建了个临时 user_data_dir,和你的 storage_state 撞了。实测无害:storage_state 会正确覆盖临时目录的 cookie,登录态正常、商品能提取。显式传 user_data_dir=None 也消不掉这个警告(它内部会再补一个),而且不要为了消警告改用 user_data_dir=真实路径 + storage_state=None,那样会丢登录态。
最终方案
import asyncio, os
from browser_use import Browser, Agent
from browser_use.llm.google.chat import ChatGoogle
KEY = open('/Users/ai/.gemini_key').read().strip()
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:8899' # genai 走本地代理
os.environ['HTTP_PROXY'] = 'http://127.0.0.1:8899'
os.environ['ALL_PROXY'] = 'http://127.0.0.1:8899'
llm = ChatGoogle(model='gemini-flash-lite-latest', api_key=KEY,
temperature=0.0, max_output_tokens=8096, max_retries=10)
browser = Browser(executable_path=CHROME, headless=False,
args=['--proxy-server=direct://', '--proxy-bypass-list=*'],
storage_state=STORAGE, viewport={'width':1280,'height':800})
agent = Agent(task=task, llm=llm, browser=browser, use_vision=False,
max_failures=20, max_actions_per_step=2, llm_timeout=180, retry_delay=3)
history = await agent.run(max_steps=12)
运行:
HTTPS_PROXY=http://127.0.0.1:8899 HTTP_PROXY=http://127.0.0.1:8899 \
ALL_PROXY=http://127.0.0.1:8899 NO_PROXY='localhost,127.0.0.1' \
python jd_dom_agent.py
京东商品选择器:列表 .gl-item / #J_goodsList li.gl-item,标题 .p-name,价格 .p-price .price,店铺 .p-shop a。跑出来就是干净的前 10 商品表格,全程零视觉调用、免费 Gemini key 稳定驱动。
小结
- browser-use 不用视觉模型、纯 DOM 就能干活,文本模型读 HTML 结构足够;
- Gemini 断连根因是 genai 直连 SOCKS5 长连接不稳,一根无超时本地 HTTP 代理完美绕开;
- Chrome 必须显式直连国内站,否则会被代理拖崩;
- “both storage_state AND user_data_dir” 警告无害,别动它。
本文由 AI 辅助生成