二、安装 PageEyes Agent
一、Python 环境
需要 Python 3.12 或更高版本。(我用的 3.13 运行会报 No module named 'imghdr'。查后 imghdr 在 3.13 中废弃了)
我用的 conda 虚拟环境,版本检查命令如下:
conda create -n py312 python=3.12 conda activate py312 python -V
显示如下图

conda 安装及创建 python 虚拟环境,参见 https://www.cnblogs.com/rslai/p/18741276
二、使用 pip 安装 page-eyes
pip install page-eyes
三、安装 Playwright 浏览器驱动
# 安装 Playwright 浏览器驱动 playwright install # 如果只需要特定浏览器,可以指定 playwright install chromium
四、运行脚本
创建一个 unit_01.py 文件,并将如下内容复制进去,注意将 OPENAI_API_KEY 修改为你自己的 API Key
import os
import asyncio
from page_eyes.agent import WebAgent
async def main():
os.environ["OPENAI_API_KEY"] = "XXX" # 替换为您的 DeepSeek API Key
os.environ["OPENAI_BASE_URL"] = "https://api.deepseek.com/v1"
web_agent = await WebAgent.create(simulate_device='Intel MacBook Pro 13-inch', debug=True)
await web_agent.run(
"""
- 打开 url "https://wma.wavecommittee.com/"
- 点击"浪潮评委会成员"tab
- 上滑页面,直到出现"查看浪潮评委会"
- 点击"查看浪潮评委会"按钮
"""
)
if __name__ == "__main__":
asyncio.run(main())
执行如下命令运行脚本
# 启动 python 3.12 环境 conda activate py312 # 运行脚本 python unit_01.py
此时浏览器可以正常启动,也能正常打开页面,但执行到点击时会报错。原因是没有安装 OmniParserV2。
五、安装 OmniParser 服务
安装方法参见 OmniParser - 安装 linux 系统(一)
六、引入 OmniParser
6.1、客户端脚本目录中,添加 .env 文件,内容如下
# OmniParser 配置, 仅当 AGENT_MODEL_TYPE 为 llm 时需要配置 OMNI_BASE_URL=http://172.31.100.27:8000
修改完,如下图

注:OmniParser 文档上说,可以通过添加环境变量的方式设置 OMNI_BASE_URL ,但我试过后不知道为啥不生效。
6.2、修改 omniparserserver.py
不知道是因为 OmniParser 有改动,还是 page-eyes 有改动,又或是我的用法不对。反正在执行客户端脚本时报错,如下图。

原因是 服务端 只支持上传 json 格式数据,但 客户端 脚本上传的是 multipart/form-data 格式。
所以没办法还要修改 omniparserserver.py 让其支持 form-data 格式。
# 进入 omniparserserver 目录 cd ~/OmniParser/omnitool/omniparserserver # 编辑 omniparserserver.py vi omniparserserver.py
共有三处改动:
A)修改 URL 路径
将第 37 行的:
@app.post("/parse/")
修改为:
@app.post("/omni/parse/")
修改后,如下图:

B)修改 host 参数,支持远程访问
如果你的 服务端 跟 客户端 在同一台电脑上,则不需要修改。
将第 23 行的:
parser.add_argument('--host', type=str, default='127.0.0.1', help='Host for the API')
修改为:
parser.add_argument('--host', type=str, default='0.0.0.0', help='Host for the API')
修改后,如下图:

B)支持 form-data 格式
将第 9 行的:
from fastapi import FastAPI
修改为:
from fastapi import FastAPI, UploadFile, File, HTTPException import base64 from typing import Optional
修改后如下图:
将第 38 行的:
async def parse(parse_request: ParseRequest):
print('start parsing...')
start = time.time()
dino_labled_img, parsed_content_list = omniparser.parse(parse_request.base64_image)
latency = time.time() - start
print('time:', latency)
return {"som_image_base64": dino_labled_img, "parsed_content_list": parsed_content_list, 'latency': latency}
修改为:
async def parse(
# 同时支持 JSON 和文件上传两种方式
parse_request: Optional[ParseRequest] = None,
file: Optional[UploadFile] = File(None)
):
print('start parsing...')
start = time.time()
try:
# 处理 JSON 格式的请求
if parse_request and parse_request.base64_image:
base64_image = parse_request.base64_image
# 处理 multipart/form-data 文件上传
elif file:
file_content = await file.read() # 读取上传的文件内容
base64_image = base64.b64encode(file_content).decode('utf-8') # 将文件内容编码为 base64
else:
raise HTTPException(
status_code=400,
detail="Either 'base64_image' in JSON body or 'file' in form data must be provided"
)
dino_labled_img, parsed_content_list = omniparser.parse(base64_image)
latency = time.time() - start
print('time:', latency)
return {"som_image_base64": dino_labled_img, "parsed_content_list": parsed_content_list, 'latency': latency}
except Exception as e:
import traceback
traceback.print_exc()
raise HTTPException(
status_code=500,
detail=str(e)
)
修改后如下图:

完整代码如下:
'''
python -m omniparserserver --som_model_path ../../weights/icon_detect/model.pt --caption_model_name florence2 --caption_model_path ../../weights/icon_caption_florence --device cuda --BOX_TRESHOLD 0.05
'''
import sys
import os
import time
from fastapi import FastAPI, UploadFile, File, HTTPException
import base64
from typing import Optional
from pydantic import BaseModel
import argparse
import uvicorn
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(root_dir)
from util.omniparser import Omniparser
def parse_arguments():
parser = argparse.ArgumentParser(description='Omniparser API')
parser.add_argument('--som_model_path', type=str, default='../../weights/icon_detect/model.pt', help='Path to the som model')
parser.add_argument('--caption_model_name', type=str, default='florence2', help='Name of the caption model')
parser.add_argument('--caption_model_path', type=str, default='../../weights/icon_caption_florence', help='Path to the caption model')
parser.add_argument('--device', type=str, default='cpu', help='Device to run the model')
parser.add_argument('--BOX_TRESHOLD', type=float, default=0.05, help='Threshold for box detection')
parser.add_argument('--host', type=str, default='0.0.0.0', help='Host for the API')
parser.add_argument('--port', type=int, default=8000, help='Port for the API')
args = parser.parse_args()
return args
args = parse_arguments()
config = vars(args)
app = FastAPI()
omniparser = Omniparser(config)
class ParseRequest(BaseModel):
base64_image: str
@app.post("/omni/parse/")
async def parse(
# 同时支持 JSON 和文件上传两种方式
parse_request: Optional[ParseRequest] = None,
file: Optional[UploadFile] = File(None)
):
print('start parsing...')
start = time.time()
try:
# 处理 JSON 格式的请求
if parse_request and parse_request.base64_image:
base64_image = parse_request.base64_image
# 处理 multipart/form-data 文件上传
elif file:
file_content = await file.read() # 读取上传的文件内容
base64_image = base64.b64encode(file_content).decode('utf-8') # 将文件内容编码为 base64
else:
raise HTTPException(
status_code=400,
detail="Either 'base64_image' in JSON body or 'file' in form data must be provided"
)
dino_labled_img, parsed_content_list = omniparser.parse(base64_image)
latency = time.time() - start
print('time:', latency)
return {"som_image_base64": dino_labled_img, "parsed_content_list": parsed_content_list, 'latency': latency}
except Exception as e:
import traceback
traceback.print_exc()
raise HTTPException(
status_code=500,
detail=str(e)
)
@app.get("/probe/")
async def root():
return {"message": "Omniparser API ready"}
if __name__ == "__main__":
uvicorn.run("omniparserserver:app", host=args.host, port=args.port, reload=True)
七、启动 omniparserserver(服务端)
# 进入 omniparserserver 目录 cd ~/OmniParser/omnitool/omniparserserver # 激活 python3.12 环境 conda activate py312 # 启动 omniparserserver python -m omniparserserver
八、启动客户端
# 进入 omniparserserver 目录 cd unit_01 # 激活 python3.12 环境 conda activate py312 # 运行客户端(脚本) sudo python unit_01.py
注:尽量给 root 权限
九、运行效果
运行效果,如下图:

十、费用
调试脚本,次数记不太清了,但应该有个20-30次。deepseek 显示 395 次 API 请求,1.9M token,0.65元人民币。

十一、完整代码

浙公网安备 33010602011771号