Umutkgz/worldguessr-cheat
GitHub: Umutkgz/worldguessr-cheat
利用Playwright自动化提取WorldGuessr地理位置信息。
Stars: 5 | Forks: 1
```
██╗ ██╗ ██████╗ ██████╗ ██╗ ██████╗ ██████╗ ██╗ ██╗███████╗███████╗███████╗██████╗
██║ ██║██╔═══██╗██╔══██╗██║ ██╔══██╗██╔════╝ ██║ ██║██╔════╝██╔════╝██╔════╝██╔══██╗
██║ █╗ ██║██║ ██║██████╔╝██║ ██║ ██║██║ ███╗██║ ██║█████╗ ███████╗███████╗██████╔╝
██║███╗██║██║ ██║██╔══██╗██║ ██║ ██║██║ ██║██║ ██║██╔══╝ ╚════██║╚════██║██╔══██╗
╚███╔███╔╝╚██████╔╝██║ ██║███████╗██████╔╝╚██████╔╝╚██████╔╝███████╗███████║███████║██║ ██║
╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝
H U N T E R
```
**WorldGuessr 的自动地理位置提取与地图注入工具**
[](https://www.python.org/)
[](https://playwright.dev/python/)
[](LICENSE)
[]()
*实时拦截 Google Street View 元数据 · 提取 GPS 坐标 · 自动点击正确的地图位置*
## 工作原理
WorldGuessr 在加载每一轮时,会调用 Google 内部的 `GetMetadata` 端点,该端点会在用户与地图交互之前返回 Street View 位置的精确 GPS 坐标。此工具会拦截该响应,从嵌套的 JSON 中解析出坐标,并在 Leaflet 地图上的精确位置触发一个合成的点击事件。
```
Browser opens worldguessr.com
│
▼
Playwright listens to every HTTP response
│
▼ (filters for GetMetadata only)
Google API response arrives ──▶ strip XSSI prefix ──▶ parse JSON
│
▼ (recursive tree search)
Coordinates extracted: lat, lng, country, address
│
▼
Injected JS fires map.fire('click', { latlng: L.latLng(lat, lng) })
│
▼
Game registers correct guess ✓
```
无需屏幕抓取。无需像素计算。无需 OCR。游戏直接把答案交给了我们。
## 功能
- **零延迟提取** — 坐标在网络层被捕获,甚至在 Street View 完全加载之前
- **与 Schema 无关的解析** — 采用递归模式匹配,而非硬编码的 JSON 路径;能够从容应对 Google API 格式变化
- **感知 XSSI** — 在解析前正确剔除 Google 的 `)]}'\n` 防滥用前缀
- **与像素无关的点击** — 直接使用 `L.LatLng`,因此缩放级别和平移状态无关紧要
- **预加载 JS Hooking** — 在任何页面脚本运行之前,通过 `Object.defineProperty` setter 陷阱捕获 Leaflet 的地图对象
- **双重 Hook 策略** — 同时使用 `addInitHook` 和重写 `prototype.initialize`,以实现跨版本 Leaflet 兼容
- **去重** — 防止在同一位置的重复 API 调用中触发多次点击
- **抗 Bot 检测启动** — 屏蔽 `navigator.webdriver` 特征,设置真实的 User-Agent
## 环境要求
- Python **3.10** 或更高版本
- 正常工作的显示环境(Linux 上的 X11,或 macOS/Windows 上的标准桌面)
## 安装说明
### 1. 克隆仓库
```
git clone https://github.com/Umutkgz/worldguessr-cheat/
cd worldguessr-hunter
```
### 2. 创建虚拟环境(推荐)
```
python -m venv venv
# Linux / macOS
source venv/bin/activate
# Windows
venv\Scripts\activate
```
### 3. 安装 Python 依赖
```
pip install -r requirements.txt
```
`requirements.txt`:
```
playwright>=1.40.0
```
### 4. 安装 Playwright 的 Chromium 浏览器
```
playwright install chromium
```
### 5.(仅限 Linux)安装系统依赖
```
playwright install-deps chromium
```
## 使用方法
```
python hunter.py
```
将会打开一个 Chromium 窗口并导航至 `https://worldguessr.com`。正常开始游戏即可 —— 脚本会在后台静默运行,并在每一轮加载时自动点击地图上的正确位置。
**如需停止:**关闭浏览器窗口,或在终端中按 `Ctrl+C`。
### 终端中的输出信息
```
╔════════════════════════════════ ══════════╗
║ WorldGuessr Hunter ║
╚════════════════════════════════ ══════════╝
To stop: Ctrl+C
═══════════════════ ═════════════════════════════════
📍 LOCATION DETECTED
════════════════════════════ ════════════════════════
Coordinates: 41.015137, 28.979530
Country: TR
Address: Sultanahmet, Istanbul
═══════════════════════════ ═════════════════════════
✅ Clicked!
```
## 在 Headless 服务器上运行 (Linux)
如果你在没有物理显示器的服务器上运行此程序,你需要一个虚拟帧缓冲区 (virtual framebuffer):
```
# 安装 Xvfb
sudo apt-get install xvfb
# 启动虚拟显示器
Xvfb :1 -screen 0 1280x800x24 &
# 设置 DISPLAY 并运行
DISPLAY=:1 python hunter.py
```
脚本会通过 `os.environ.setdefault` 自动将 `DISPLAY=:1` 设置为后备值,但你仍需事先运行 Xvfb。
## 项目结构
```
worldguessr-hunter/
├── hunter.py # Main script
├── requirements.txt # Python dependencies
├── README.md
└── LICENSE
```
### `hunter.py` 中的核心组件
| 组件 | 描述 |
|-----------|-------------|
| `extract_json()` | 在解析 JSON 之前,剔除 API 响应中 Google 的 XSSI 前缀 (`)]}'`) |
| `find_coords()` | 递归搜索嵌套的 JSON,查找 `[None, None, lat, lng]` 坐标签名 |
| `find_country()` | 递归查找 ISO 3166-1 alpha-2 国家代码(2 个字符的大写字符串) |
| `find_addresses()` | 收集响应树中找到的所有 `[address_string, locale_code]` 键值对 |
| `INIT_SCRIPT` | 在页面加载前注入的 JavaScript;通过陷阱拦截 `window.L` 赋值,以 hook Leaflet 的地图构造函数 |
| `CLICK_JS` | JavaScript 模板,用于在 Leaflet 地图上的指定坐标处触发合成的 `click` 事件 |
| `inject_click()` | Python 包装器,使用真实坐标格式化 `CLICK_JS` 并通过 `page.evaluate()` 执行 |
| `on_response()` | Playwright 响应事件处理器;统筹整个提取与点击的 pipeline |
| `main()` | 使用防 Bot 检测标志启动浏览器,注册 hook,并导航至游戏页面 |
## 技术说明
### 为什么对 Leaflet 使用 `Object.defineProperty`?
Leaflet 将其实例存储在模块局部变量中,无法从 `window` 访问。脚本没有使用 `setInterval` 进行轮询,而是在 `window.L` 上安装了一个属性陷阱,这样当 Leaflet 加载并将其自身分配给全局对象的那一刻,我们就能同步拦截它,并在任何页面代码运行之前 hook 进 `L.Map.prototype`。
### 为什么使用两种 hook 方法?
`addInitHook` 是 Leaflet 官方的插件 API;重写 `prototype.initialize` 是一种更底层的后备方案。不同的 Leaflet 构建版本在次要版本之间可能会有不同的行为。同时使用这两种方法可以确保至少有一个能够触发。
### 为什么使用 `L.LatLng` 而不是像素坐标?
将 GPS 坐标转换为屏幕像素需要知道当前的地图缩放级别和中心点,而这两者随时都可能发生变化。直接向 `map.fire('click', ...)` 提供 `latlng` 彻底绕过了这个问题;Leaflet 的事件系统向处理器传递的是地理坐标,而不是像素位置。
### 为什么要剔除 `)]}'\n`?
Google 在内部 API 的 JSON 响应前加上这段字符串,以防止 [XSSI 攻击](https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/11-Client_Side_Testing/13-Testing_for_Cross_Site_Script_Inclusion)。浏览器的 `fetch()` 调用会接收到完整文本,并在解析前将其剔除。我们在这里手动复现了这一步骤。
## 故障排除
**浏览器打开了但没有反应**
`GetMetadata` 的 URL 匹配模式可能已经改变。打开浏览器的开发者工具 (DevTools) → Network 标签页,开始一局游戏,并查找对 `maps.googleapis.com` 的请求。如有必要,请更新 `on_response()` 中的 URL 过滤字符串。
**终端中出现 `NO_MAP`**
Leaflet hook 未能及时捕获地图对象。这可能是因为游戏的地图在 `add_init_script` 运行之前就已经初始化了 —— 这种情况不太可能发生,但在极快的网络连接下是有可能的。尝试在 `page.goto()` 之前添加一个简短的 `await asyncio.sleep(0.5)`。
**`ModuleNotFoundError: playwright`**
请确保在运行脚本之前激活了你的虚拟环境:
```
source venv/bin/activate # Linux/macOS
venv\Scripts\activate # Windows
```
**`Error: browserType.launch: Executable doesn't exist`**
再次运行 `playwright install chromium`。可能是浏览器二进制文件未能正确下载。
**在 Linux 上:`error while loading shared libraries`**
运行 `playwright install-deps chromium` 来安装缺失的系统库。
## 免责声明
本项目仅供教育和研究目的使用 —— 专门用于演示浏览器自动化、网络拦截和 JavaScript runtime hooking 技术。请负责任地使用。在在线多人游戏中运行自动化脚本可能会违反这些游戏的服务条款。
## 许可证
MIT — 详情请参阅 [LICENSE](LICENSE)。
使用 [Playwright](https://playwright.dev/python/) 构建 · 好奇心驱动
标签:Playwright, Python, 地理定位, 无后门, 游戏外挂, 网络调试, 自动化