Initial commit: Trading skills collection
- OKX交易自动化 (okx-auto-position, okx-crypto, okx-exchange) - 交易信号处理 (signal-confirmation-templates, trading-signal-aggregator) - 量化因子挖掘 (quant-factor-mining) - 长桥集成 (longbridge-cli, longbridge-python-sdk) - 六合彩分析 (lottery-hk) - 股息投资 (dividend-investing, dividend-scanner) - 日内交易 (intraday-trading) - 同花顺 (tonghuashun)
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
---
|
||||
name: lottery-hk
|
||||
description: "香港六合彩开奖抓取与分析。从天空彩票(tktk4.cc)抓取开奖结果,支持历史记录、号码频率分析、生肖/五行/波色统计、热号冷号。数据存SQLite,不存图片。"
|
||||
version: 1.1.0
|
||||
tags: [lottery, hk, 六合彩, analysis]
|
||||
---
|
||||
|
||||
# 香港六合彩开奖分析
|
||||
|
||||
从天空彩票抓取香港六合彩开奖结果,提供数据分析。**数据存SQLite,不存图片。**
|
||||
|
||||
## 数据源
|
||||
|
||||
- 网站: https://tktk.tktk4.cc/ww.htm
|
||||
- **当前开奖JSON API**: `https://btc.tktk.app/data/v_xg.json`(直接返回JSON,无需浏览器)
|
||||
- 开奖页: https://btc.tktk.app/e/api/kj.php?xg (Vue.js动态加载,仅渲染用)
|
||||
- **sol.0051.cc 历史API已失效**: `/e/api/api.php?get=sixlist&year=YYYY` 返回空数据(2026-07确认)
|
||||
- 开奖时间: 每周二、四、六 21:30(北京时间)
|
||||
- 49个号码,6个平码 + 1个特码
|
||||
|
||||
### tktk API架构(从public.js逆向)
|
||||
|
||||
tktk Vue.js应用的数据源URL模式: `https://btc.tktk.app/data/v_{cod}.json?{timestamp}`
|
||||
|
||||
| cod | 彩种 | 说明 |
|
||||
|-----|------|------|
|
||||
| xg | 香港六合彩 | 每周二/四/六 21:30 |
|
||||
| 48am | 天天澳门彩 | 每天 22:14-22:40 |
|
||||
| am | 新澳门六合彩 | 每天 21:14-21:40 |
|
||||
| tw | 台湾六合彩 | 每天 20:28-20:58 |
|
||||
| xjp | 新加坡六合彩 | 每天 18:35-18:55 |
|
||||
| fckl8 | 快乐8 | 每天 21:25-21:40 |
|
||||
|
||||
JSON返回格式:
|
||||
```json
|
||||
{
|
||||
"Data": {
|
||||
"1": {"nim":"金","number":"34","color":"红","style":"red","sx":"鸡"},
|
||||
"2": {...}, ... "7": {...}
|
||||
},
|
||||
"Time": "21点30分", "Day": "05", "Moon": "07", "Year": 2026,
|
||||
"Qi": "071", "Nq": "072", "Week": "周日", "Auto": false
|
||||
}
|
||||
```
|
||||
- `Data.1`-`Data.6`: 平码,`Data.7`: 特码
|
||||
- `Qi`: 当前期号,`Nq`: 下期号
|
||||
- `nim`: 五行,`sx`: 生肖,`color`: 波色(红/蓝/绿)
|
||||
|
||||
## 数据存储
|
||||
|
||||
**SQLite数据库**: `~/.hermes/trading/lottery.db`
|
||||
|
||||
- `draws` 表: 开奖记录(期号、日期、6个号码+特码、生肖、五行、波色)
|
||||
- `cold_data` 表: 冷数据(key-value,网页文本内容)
|
||||
- `image_links` 表: 图片链接(类别、标题、URL、期号,不下载图片)
|
||||
- **不存图片文件**,图片类只存URL链接
|
||||
|
||||
## 脚本用法
|
||||
|
||||
```bash
|
||||
SCRIPT=~/.hermes/skills/trading/lottery-hk/scripts/lottery.py
|
||||
|
||||
python3 $SCRIPT add <期号> <号码> # 手动添加
|
||||
python3 $SCRIPT add_full <期号> <号码> <生肖> # 带生肖添加
|
||||
python3 $SCRIPT history [期数] # 查看历史
|
||||
python3 $SCRIPT analyze # 分析(频率/热号/冷号/生肖/五行/波色)
|
||||
python3 $SCRIPT zodiac # 生肖号码对照表
|
||||
python3 $SCRIPT next # 下期开奖时间
|
||||
python3 $SCRIPT import_json <文件> # 导入JSON到SQLite
|
||||
python3 $SCRIPT save_cold <key> <content> # 保存冷数据
|
||||
python3 $SCRIPT get_cold <key> # 读取冷数据
|
||||
python3 $SCRIPT save_image <类别> <标题> <URL> [期号] # 保存图片链接
|
||||
python3 $SCRIPT list_images [类别] # 列出图片链接
|
||||
```
|
||||
|
||||
## 生肖映射(网站实际映射,已验证)
|
||||
|
||||
网站的生肖表和标准12生肖轮转不同,用 mod 12 映射:
|
||||
```
|
||||
0=狗, 1=猪, 2=蛇, 3=马, 4=羊, 5=虎, 6=兔, 7=鼠, 8=牛, 9=猴, 10=鸡, 11=龙
|
||||
```
|
||||
071期验证: 34=鸡(34%12=10)✅, 46=鸡(46%12=10)✅, 17=虎(17%12=5)✅
|
||||
|
||||
## 冷数据 vs 热数据
|
||||
|
||||
**🧊 冷数据**(存cold_data表,不常变):
|
||||
- 歷史、生肖表、日期、常識、全年、技巧、規律、策略
|
||||
|
||||
**🔥 热数据**(每期更新):
|
||||
- 開獎、掛牌、解牌、綜掛、平碼平肖、玄机资料、论坛高手推荐等
|
||||
|
||||
## 参考资料
|
||||
|
||||
- 用户说"六合彩"、"开奖"、"彩票"、"特码"
|
||||
- 用户问"今天开什么"、"最近开奖号码"
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 页面用Vue.js动态加载,非开奖时间段(21:14-21:40外)可能无数据
|
||||
- 图库类页面(玄机图库、经典图库等)是图片,不抓取
|
||||
- 网站有大量博彩广告,解析时需过滤
|
||||
|
||||
## 参考资料
|
||||
|
||||
- `references/zodiac-table.md`: 2026年完整生肖五行波色对照表(号码→生肖→五行→波色→分类)
|
||||
- `references/draw-dates.md`: 2021-2023年搅珠日期(JS日历数据)
|
||||
- `references/common-knowledge.md`: 生肖属性文章索引、关键概念
|
||||
- `references/techniques.md`: 规律秘诀文章索引(出波、波色法等)
|
||||
- `references/patterns.md`: 固定规律文章索引(日期定波、杀肖、出尾等)
|
||||
- `references/strategies.md`: 买码建议文章索引(赢钱秘诀、七戒律等)
|
||||
|
||||
## 定时任务
|
||||
|
||||
| 任务 | 时间(UTC) | 内容 |
|
||||
|------|-----------|------|
|
||||
| lottery-hot-data | 周二/四/六 11:30 (19:30北京) | 抓热数据+频率分析,推QQ |
|
||||
| lottery-draw-result | 周二/四/六 14:00 (22:00北京) | 抓开奖结果入库,推QQ |
|
||||
|
||||
开奖时间: 21:30 北京时间 → 先抓热数据分析(19:30),开奖后抓结果(22:00)。
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **sol.0051.cc 历史API已失效**: `/e/api/api.php?get=sixlist&year=YYYY` 返回空数据(2026-07确认)。历史页面能访问但AJAX无数据返回。不要浪费时间尝试此API。当前开奖数据应从 `btc.tktk.app/data/v_xg.json` 获取。
|
||||
- **历史数据无批量API**: 目前没有可用的批量历史开奖数据API。只能逐期从 `data/v_xg.json` 获取当期数据,需要长期积累。
|
||||
- **生肖表URL会过期**: sol.0051.cc 的生肖表页面每年更新,旧URL会404。应先访问 https://sol.0051.cc/sssx/ 列表页,找到最新年份的文章链接。
|
||||
- **中文彩票站内容多为图片**: sol.0051.cc 等网站的详细资料(公式、规律、技巧)嵌在图片中,curl/sed只能抓到文章标题索引,无法提取实际内容。需要用 browser 工具查看页面截图。
|
||||
- **抓取编码**: 这些站点多为UTF-8 with BOM,curl 输出可能有 `锘` 开头(BOM标记),不影响内容但需注意。
|
||||
@@ -0,0 +1,36 @@
|
||||
# 六合彩常识 (Common Knowledge)
|
||||
|
||||
Source: https://sol.0051.cc/sssx/
|
||||
|
||||
## 生肖属性文章列表
|
||||
|
||||
- 2026年生肖.属性.知识.排位[020期启用] - 2026-02-16
|
||||
- 2025年生肖.属性.知识.排位[017期启用] - 2025-01-25
|
||||
- 2024年生肖.属性.知识.排位[017期启用] - 2024-02-10
|
||||
- 2023年生肖.属性.知识.排位[009期启用] - 2023-01-20
|
||||
- 2022年生肖.属性.知识.排位[004期启用] - 2022-01-29
|
||||
- 2021年生肖.属性.知识.排位[013期启用] - 2021-02-11
|
||||
- 2020年生肖.属性.知识.排位[008期启用] - 2020-01-23
|
||||
|
||||
## 名著目录
|
||||
- 三国演义
|
||||
- 封神榜
|
||||
- 红楼梦
|
||||
|
||||
## 历年资料
|
||||
- 十二生肖的来历
|
||||
- 2014年生肖.波色.五行.门数[014期启用]
|
||||
- 2015年生肖.属性.知识.排位[021期启用]
|
||||
- 2016年生肖.属性.知识.排位[017期启用]
|
||||
|
||||
## Key Concepts
|
||||
- 生肖 (Zodiac animals): 鼠牛虎兔龙蛇马羊猴鸡狗猪
|
||||
- 五行 (Five elements): 金木水火土
|
||||
- 波色 (Wave colors): 红蓝绿
|
||||
- 大小 (Big/small): 01-24小, 25-49大
|
||||
- 单双 (Odd/even)
|
||||
- 合数 (Sum of digits)
|
||||
- 尾数 (Last digit)
|
||||
- 门数 (Gate numbers)
|
||||
|
||||
Note: Most detailed content on this site is embedded in images. The text listings above are article titles/indices.
|
||||
@@ -0,0 +1,46 @@
|
||||
# 六合彩搅珠日期 (HK Mark Six Draw Dates)
|
||||
|
||||
Source: https://tktk.tktk4.cc/date.htm
|
||||
|
||||
## 2021年
|
||||
- 1月: 2, 5, 8, 12, 15, 19, 22, 26, 29
|
||||
- 2月: 2, 5, 9, 12, 16, 19, 23, 26
|
||||
- 3月: 2, 5, 12, 19, 23, 26, 30
|
||||
- 4月: 2, 6, 9, 13, 16, 20, 23, 27, 30
|
||||
- 5月: 4, 7, 11, 14, 18, 21, 25, 28
|
||||
- 6月: 1, 4, 8, 11, 15, 17, 19, 22, 24, 27, 29
|
||||
- 7月: 3, 6, 8, 10, 13, 15, 17, 20, 22, 24, 27
|
||||
- 8月: 1, 3, 5, 7, 10, 12, 14, 19, 21, 26, 28, 31
|
||||
- 9月: 2, 4, 7, 9, 11, 14, 16, 21, 23, 25, 28, 30
|
||||
- 10月: 2, 5, 7, 14, 16, 19, 21, 26, 28, 30
|
||||
- 11月: 2, 4, 6, 9, 11, 14, 16, 18, 20, 23, 25, 27, 30
|
||||
- 12月: 2, 4, 7, 9, 11, 14, 16, 19, 21, 23, 25, 28, 30
|
||||
|
||||
## 2022年
|
||||
- 1月: 4, 20, 27
|
||||
- 2月: 5, 10, 17, 24
|
||||
- 3月: 3, 8, 11, 15, 18, 22, 25, 29
|
||||
- 4月: 1, 5, 8, 12, 15, 19, 22, 26, 29
|
||||
- 5月: 3, 6, 10, 13, 17, 20, 24, 27, 31
|
||||
- 6月: 3, 7, 10, 14, 17, 21, 27
|
||||
- 7月: 3, 5, 8, 12, 15, 22, 26, 28, 30
|
||||
- 8月: 2, 4, 9, 11, 13, 16, 18, 20, 23, 25, 27, 30
|
||||
- 9月: 1, 3, 6, 8, 13, 15, 17, 20, 22, 24, 27, 29
|
||||
- 10月: 2, 4, 6, 8, 11, 13, 15, 18, 20, 22, 25, 29
|
||||
- 11月: 1, 3, 5, 8, 10, 13, 15, 17, 19, 22, 24, 26, 29
|
||||
- 12月: 1, 3, 6, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29
|
||||
|
||||
## 2023年
|
||||
- 1月: 3, 5, 7, 10, 12, 14, 17, 19, 26, 28
|
||||
- 2月: 2, 4, 7, 9, 11, 14, 16, 18, 21, 23, 25, 28
|
||||
- 3月: 2, 4, 7, 9, 12, 14, 16, 18, 21, 23, 25, 28, 30
|
||||
- 4月: 1, 4, 8, 11, 13, 16, 18, 20, 22, 25, 27, 29
|
||||
- 5月: 2, 4, 6, 9, 11, 14, 16, 18, 20, 23, 25, 27, 30
|
||||
- 6月: 1, 3, 6, 8, 11, 13, 15, 17, 20, 22, 24, 27, 29
|
||||
- 7月: 2, 4, 8, 11, 13, 15, 18, 20, 25, 27, 29
|
||||
- 8月: 1, 3, 5, 8, 10, 12, 15, 17, 19, 22, 24, 26, 29, 31
|
||||
|
||||
## Notes
|
||||
- Draw dates are typically Tuesday, Thursday, and Saturday (二、四、六)
|
||||
- Sometimes there are additional draws on other days
|
||||
- The pattern shows approximately 3 draws per week
|
||||
@@ -0,0 +1,64 @@
|
||||
# 六合彩数据源URL清单
|
||||
|
||||
## 核心API(经2026-07验证)
|
||||
|
||||
| 端点 | URL | 类型 | 说明 |
|
||||
|------|-----|------|------|
|
||||
| **当前开奖JSON** | `https://btc.tktk.app/data/v_xg.json` | ✅可用 | 直接返回JSON,curl可抓 |
|
||||
| 开奖渲染页 | `https://btc.tktk.app/e/api/kj.php?xg` | 渲染 | Vue.js页面,仅展示用 |
|
||||
| tktk主页 | `https://tktk.tktk4.cc/ww.htm` | 入口 | 含iframe引用kj.php |
|
||||
|
||||
### API URL模式(从public.js逆向)
|
||||
|
||||
`https://btc.tktk.app/data/v_{cod}.json?{timestamp}`
|
||||
|
||||
| cod | 彩种 |
|
||||
|-----|------|
|
||||
| xg | 香港六合彩 |
|
||||
| 48am | 天天澳门彩 |
|
||||
| am | 新澳门六合彩 |
|
||||
| tw | 台湾六合彩 |
|
||||
| xjp | 新加坡六合彩 |
|
||||
| fckl8 | 快乐8 |
|
||||
|
||||
## 已失效的端点(2026-07确认)
|
||||
|
||||
| 端点 | URL | 状态 |
|
||||
|------|-----|------|
|
||||
| sol.0051.cc 历史API | `/e/api/api.php?get=sixlist&year=YYYY` | ❌返回空数据 |
|
||||
| sol.0051.cc 全年资料 | `https://sol.0051.cc/qnzl/` | ⚠️仅文章索引,非结构化数据 |
|
||||
| 419.ccc3.cc 历史API | `/e/api/api.php?get=sixlist&year=YYYY` | ❌404 |
|
||||
| 666kj.com | `/kj/kj_history.aspx` | ❌404 |
|
||||
|
||||
## 冷数据页面(从sol.0051.cc)
|
||||
|
||||
| 页面 | URL | 说明 |
|
||||
|------|-----|------|
|
||||
| 历史记录页 | https://sol.0051.cc/history/ | 页面可访问但API无数据 |
|
||||
| 全年资料 | https://sol.0051.cc/qnzl/ | 文章链接列表(歇后语、生肖诗等) |
|
||||
| 生肖表 | https://sol.0051.cc/sssx/467578.html | 生肖号码对照 |
|
||||
| 常识 | https://sol.0051.cc/sssx/ | 六合彩基础知识 |
|
||||
| 技巧 | https://sol.0051.cc/guilvmijue/ | 分析技巧 |
|
||||
| 规律 | https://sol.0051.cc/gudingguilv/ | 号码规律 |
|
||||
| 策略 | https://sol.0051.cc/maimajianyi/ | 投注策略 |
|
||||
|
||||
## 热数据页面(从sol.0051.cc)
|
||||
|
||||
| 页面 | URL | 说明 |
|
||||
|------|-----|------|
|
||||
| 解牌 | https://sol.0051.cc/gsjg/ | 号码解读 |
|
||||
| 综合挂牌 | https://sol.0051.cc/zongheguapai/ | 综合挂牌分析 |
|
||||
| 挂牌 | https://tktk.tktk4.cc/tkgp/index.htm | 挂牌号码 |
|
||||
| 日期 | https://tktk.tktk4.cc/date.htm | 开奖日期表 |
|
||||
|
||||
## 编码注意
|
||||
|
||||
- sol.0051.cc 页面可能是 GB2312 编码,需转换为 UTF-8
|
||||
- tktk.tktk4.cc 主页是 UTF-8 with BOM(curl输出可能有`锘`开头)
|
||||
- btc.tktk.app JSON API 返回标准UTF-8
|
||||
|
||||
## 抓取频率
|
||||
|
||||
- 冷数据: 月度/季度更新
|
||||
- 热数据: 每周二、四、六 19:30 抓取(开奖前2小时)
|
||||
- 开奖结果: 开奖后立即抓取(21:30后),用 `btc.tktk.app/data/v_xg.json`
|
||||
@@ -0,0 +1,29 @@
|
||||
# 六合彩固定规律 (Patterns)
|
||||
|
||||
Source: https://sol.0051.cc/gudingguilv/
|
||||
|
||||
## 文章列表
|
||||
|
||||
- ┫公式规律┣ 【日期定准双波规律】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【开奖日排期日杀肖】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【四柱出肖日柱出行】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【双日定七肖中特区】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【★永远不变的规律】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【★全年特尾出码表】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【★全年固定杀波★】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【肖日杀码规律专用】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【精准奇门方法出尾】≡永久性≡ (2019-06-20)
|
||||
- ┫公式规律┣ 【对尾规律六尾中特】≡永久性≡ (2019-06-20)
|
||||
|
||||
## Pattern Types
|
||||
- 日期定准双波规律: Date-based dual wave prediction
|
||||
- 开奖日排期日杀肖: Draw day zodiac elimination
|
||||
- 四柱出肖日柱出行: Four pillars zodiac prediction
|
||||
- 双日定七肖中特区: Dual date seven zodiac special zone
|
||||
- 全年特尾出码表: Annual special tail number table
|
||||
- 全年固定杀波: Annual fixed wave elimination
|
||||
- 肖日杀码规律: Zodiac day number elimination
|
||||
- 精准奇门方法出尾: Precise Qimen tail prediction
|
||||
- 对尾规律六尾中特: Paired tail six-tail special
|
||||
|
||||
Note: Detailed patterns are in image format on the source site.
|
||||
@@ -0,0 +1,27 @@
|
||||
# 六合彩买码建议 (Strategies)
|
||||
|
||||
Source: https://sol.0051.cc/maimajianyi/
|
||||
|
||||
## 文章列表
|
||||
|
||||
- 赢钱秘诀(实践篇) (2019-06-20)
|
||||
- 赢钱经验和输钱原因 (2019-06-20)
|
||||
- 六合彩选号"七戒律" (2019-06-20)
|
||||
- 理性分析六合彩 (2019-06-20)
|
||||
- 六合三戒 (2019-06-20)
|
||||
- 香港六合彩的富翁定律(獨家原創資料) (2019-06-20)
|
||||
- 問題賭博的表徵 (2019-06-20)
|
||||
- 如何成为六合投资胜利者 (2019-06-20)
|
||||
- 中六合彩的五要数 (2019-06-20)
|
||||
- 一直买不中的原因 (2019-06-20)
|
||||
|
||||
## Strategy Concepts
|
||||
- 赢钱秘诀: Winning secrets (practical)
|
||||
- 七戒律: Seven commandments for number selection
|
||||
- 理性分析: Rational analysis approach
|
||||
- 三戒: Three taboos
|
||||
- 富翁定律: Millionaire's law
|
||||
- 五要数: Five key numbers
|
||||
- 投资心态: Investment mindset
|
||||
|
||||
Note: Detailed strategies are in image format on the source site.
|
||||
@@ -0,0 +1,22 @@
|
||||
# 六合彩规律秘诀 (Techniques)
|
||||
|
||||
Source: https://sol.0051.cc/guilvmijue/
|
||||
|
||||
## 文章列表
|
||||
|
||||
- 本机构建议投注人士不可沉迷赌博 (2019-06-20)
|
||||
- 驾趋六合彩博彩这个令多少人浮沉不定的王国吗? (2019-06-20)
|
||||
- 六合彩=规律+运气+概率+科学方法+良好的心态=财富 (2019-06-20)
|
||||
- 六合彩【群英会】全年固定公式规律『出波篇』 (2019-06-20)
|
||||
- 六合彩【群英会】全年固定公式规律『赢秘诀』 (2019-06-20)
|
||||
- 六合彩【群英会】全年固定公式规律『波色法』 (2019-06-20)
|
||||
- 〖全年〗【㊣固定公式规律㊣出特专区】已更新 (2019-06-20)
|
||||
|
||||
## Key Formula Concepts
|
||||
- 出波篇: Wave color prediction methods
|
||||
- 赢秘诀: Winning secrets
|
||||
- 波色法: Wave color method
|
||||
- 固定公式规律: Fixed formula patterns
|
||||
- 出特专区: Special number prediction zone
|
||||
|
||||
Note: Detailed formulas and techniques are in image format on the source site.
|
||||
@@ -0,0 +1,123 @@
|
||||
# 2026年甲辰年六合彩生肖属性 (020期启用)
|
||||
|
||||
Source: https://sol.0051.cc/sssx/359828.html
|
||||
|
||||
## 生肖对照表 (Zodiac Number Mapping)
|
||||
|
||||
| 马 | 蛇 | 龙 | 兔 | 虎 | 牛 | 鼠 | 猪 | 狗 | 鸡 | 猴 | 羊 |
|
||||
|----|----|----|----|----|----|----|----|----|----|----|-----|
|
||||
| 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
|
||||
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
|
||||
| 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
|
||||
| 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
|
||||
| 49 |
|
||||
|
||||
## 五行属性 (Five Elements)
|
||||
|
||||
| 五行 | 号码 | 相冲 |
|
||||
|------|------|------|
|
||||
| 金 | 04 05 12 13 26 27 34 35 42 43 | 鼠冲马, 牛冲羊 |
|
||||
| 木 | 08 09 16 17 24 25 38 39 46 47 | 虎冲猴, 兔冲鸡 |
|
||||
| 水 | 01 14 15 22 23 30 31 44 45 | 龙冲狗, 蛇冲猪 |
|
||||
| 火 | 02 03 10 11 18 19 32 33 40 41 48 49 | 马冲鼠, 羊冲牛 |
|
||||
| 土 | 06 07 20 21 28 29 36 37 | 猴冲虎, 鸡冲兔 |
|
||||
|
||||
## TM大小
|
||||
- TM小:01-24
|
||||
- TM大:25-49
|
||||
- 狗冲龙, 猪冲蛇
|
||||
|
||||
## 生肖分类
|
||||
|
||||
### 左右肖
|
||||
- 左肖:鼠牛龙蛇猴鸡
|
||||
- 右肖:虎兔马羊狗猪
|
||||
|
||||
### 独合肖
|
||||
- 无边肖:鼠牛虎兔马羊
|
||||
- 有边肖:龙蛇猴鸡狗猪
|
||||
|
||||
### 前后肖
|
||||
- 前肖:鼠牛虎兔龙蛇
|
||||
- 后肖:马羊猴鸡狗猪
|
||||
|
||||
### 阴阳肖
|
||||
- 阴肖:鼠龙马蛇狗猪
|
||||
- 阳肖:鸡兔牛羊虎猴
|
||||
|
||||
### 天地肖
|
||||
- 天肖:兔马猴猪牛龙
|
||||
- 地肖:蛇羊鸡狗鼠虎
|
||||
|
||||
### 家野肖
|
||||
- 野兽:猴蛇龙兔虎鼠
|
||||
- 家畜:羊马牛猪狗鸡
|
||||
|
||||
### 单双笔
|
||||
- 单笔画肖:鼠龙蛇马鸡猪
|
||||
- 双笔画肖:牛虎兔羊猴狗
|
||||
|
||||
### 美丑肖
|
||||
- 吉美:兔龙蛇马羊鸡
|
||||
- 凶丑:鼠牛虎猴狗猪
|
||||
|
||||
### 风雷云雨肖
|
||||
- 风肖:虎龙兔
|
||||
- 雷肖:蛇马羊
|
||||
- 云肖:猴鸡狗
|
||||
- 雨肖:猪鼠牛
|
||||
|
||||
### 波色生肖
|
||||
- 红生肖:马兔鼠鸡
|
||||
- 蓝生肖:蛇虎猪猴
|
||||
- 绿生肖:羊龙牛狗
|
||||
|
||||
## 四季方位
|
||||
- 春:兔虎龙;夏:马蛇羊;秋:鸡猴狗;冬:鼠猪牛
|
||||
- 东:兔虎龙;南:马蛇羊;西:鸡猴狗;北:鼠猪牛
|
||||
|
||||
## 生肖代号表
|
||||
|
||||
| 生肖 | 代号 |
|
||||
|------|------|
|
||||
| 鼠 | 梅花、宰相、神偷、逆贼、军师 |
|
||||
| 牛 | 荷花、员外、元帅、大将 |
|
||||
| 虎 | 桃花、武士、将军、都督、大王 |
|
||||
| 兔 | 兰花、小姐、皇后、东宫、玉女、玉帝 |
|
||||
| 龙 | 李花、状元、皇帝、皇上、梨花 |
|
||||
| 蛇 | 竹花、美女、宫女、太子、宫妃、才人 |
|
||||
| 马 | 杏花、秀才、太子、元帅 |
|
||||
| 羊 | 樱花、夫人、宰相、士兵、西宫 |
|
||||
| 猴 | 松树、游侠、宰相、太监、蔻王 |
|
||||
| 鸡 | 葵花、歌女、武士、西宫、奴婢、贵妃、苓花 |
|
||||
| 狗 | 菊花、管家、奴才、先锋、文官 |
|
||||
| 猪 | 桂花、商贾、太监、东宫 |
|
||||
|
||||
## 五行生肖属性
|
||||
- 金肖:猴鸡
|
||||
- 木肖:虎兔
|
||||
- 水肖:鼠猪
|
||||
- 火肖:蛇马
|
||||
- 土肖:牛羊龙狗
|
||||
|
||||
## 三合六合
|
||||
- 三合:鼠龙猴、牛蛇鸡、虎马狗、兔羊猪
|
||||
- 六合:鼠牛、龙鸡、虎猪、蛇猴、兔狗、马羊
|
||||
|
||||
## 四季生肖
|
||||
- 春:虎兔龙
|
||||
- 夏:蛇马羊
|
||||
- 秋:猴狗鸡
|
||||
- 冬:鼠牛猪
|
||||
|
||||
## 十二代号属性
|
||||
- 两大君王:龙虎
|
||||
- 两大恶人:鼠猴
|
||||
- 四大美女:兔蛇羊鸡
|
||||
- 四大家臣:牛马猪狗
|
||||
|
||||
## 波色 (Wave Colors)
|
||||
|
||||
| 红波 | 蓝波 | 绿波 |
|
||||
|------|------|------|
|
||||
| 01 02 07 08 12 13 18 19 23 24 29 30 34 35 40 45 46 | 03 04 09 10 14 15 20 25 26 31 36 37 41 42 47 48 | 05 06 11 16 17 21 22 27 28 32 33 38 39 43 44 49 |
|
||||
Binary file not shown.
@@ -0,0 +1,405 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
香港六合彩开奖抓取与分析(SQLite版)
|
||||
数据源: https://tktk.tktk4.cc/ww.htm (天空彩票)
|
||||
|
||||
用法:
|
||||
python3 lottery.py fetch # 抓取最新开奖结果
|
||||
python3 lottery.py add <期号> <号码> # 手动添加
|
||||
python3 lottery.py add_full <期号> <号码> <生肖> # 带生肖添加
|
||||
python3 lottery.py history [期数] # 查看历史记录
|
||||
python3 lottery.py analyze # 分析(频率/热号/冷号/生肖)
|
||||
python3 lottery.py zodiac # 生肖属性表
|
||||
python3 lottery.py next # 下期开奖时间
|
||||
python3 lottery.py import_json <文件> # 导入JSON历史数据
|
||||
"""
|
||||
|
||||
import json, os, sys, re, sqlite3
|
||||
from datetime import datetime
|
||||
from collections import Counter
|
||||
|
||||
DATA_DIR = os.path.expanduser("~/.hermes/trading")
|
||||
DB_FILE = os.path.join(DATA_DIR, "lottery.db")
|
||||
|
||||
# 生肖对照表(网站实际映射,从浏览器071期数据验证)
|
||||
ZODIAC_BY_MOD = {
|
||||
0: "狗", 1: "猪", 2: "蛇", 3: "马", 4: "羊", 5: "虎",
|
||||
6: "兔", 7: "鼠", 8: "牛", 9: "猴", 10: "鸡", 11: "龙"
|
||||
}
|
||||
|
||||
# 五行对照表
|
||||
ELEMENT_MAP = {
|
||||
1: "木", 2: "木", 3: "火", 4: "火", 5: "土", 6: "土",
|
||||
7: "金", 8: "金", 9: "水", 10: "水", 11: "木", 12: "木",
|
||||
13: "火", 14: "火", 15: "土", 16: "土", 17: "金", 18: "金",
|
||||
19: "水", 20: "水", 21: "木", 22: "木", 23: "火", 24: "火",
|
||||
25: "土", 26: "土", 27: "金", 28: "金", 29: "水", 30: "水",
|
||||
31: "木", 32: "木", 33: "火", 34: "火", 35: "土", 36: "土",
|
||||
37: "金", 38: "金", 39: "水", 40: "水", 41: "木", 42: "木",
|
||||
43: "火", 44: "火", 45: "土", 46: "土", 47: "金", 48: "金",
|
||||
49: "水",
|
||||
}
|
||||
|
||||
# 波色对照表
|
||||
COLOR_MAP = {
|
||||
"红波": [1, 2, 7, 8, 12, 13, 18, 19, 23, 24, 29, 30, 34, 35, 40, 45, 46],
|
||||
"蓝波": [3, 4, 9, 10, 14, 15, 20, 25, 26, 31, 36, 37, 41, 42, 47, 48],
|
||||
"绿波": [5, 6, 11, 16, 17, 21, 22, 27, 28, 32, 33, 38, 39, 43, 44, 49],
|
||||
}
|
||||
|
||||
def get_zodiac(num):
|
||||
return ZODIAC_BY_MOD.get(num % 12, "?")
|
||||
|
||||
def get_element(num):
|
||||
return ELEMENT_MAP.get(num, "?")
|
||||
|
||||
def get_color(num):
|
||||
for color, nums in COLOR_MAP.items():
|
||||
if num in nums:
|
||||
return color
|
||||
return "未知"
|
||||
|
||||
def get_db():
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
conn = sqlite3.connect(DB_FILE)
|
||||
conn.row_factory = sqlite3.Row
|
||||
conn.execute("""CREATE TABLE IF NOT EXISTS draws (
|
||||
period TEXT PRIMARY KEY,
|
||||
date TEXT,
|
||||
n1 INTEGER, n2 INTEGER, n3 INTEGER, n4 INTEGER, n5 INTEGER, n6 INTEGER,
|
||||
special INTEGER,
|
||||
z1 TEXT, z2 TEXT, z3 TEXT, z4 TEXT, z5 TEXT, z6 TEXT, z_special TEXT,
|
||||
e1 TEXT, e2 TEXT, e3 TEXT, e4 TEXT, e5 TEXT, e6 TEXT, e_special TEXT,
|
||||
c1 TEXT, c2 TEXT, c3 TEXT, c4 TEXT, c5 TEXT, c6 TEXT, c_special TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)""")
|
||||
conn.execute("""CREATE TABLE IF NOT EXISTS cold_data (
|
||||
key TEXT PRIMARY KEY,
|
||||
content TEXT,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)""")
|
||||
conn.commit()
|
||||
return conn
|
||||
|
||||
def add_draw(period, numbers, zodiacs=None):
|
||||
"""添加开奖结果"""
|
||||
conn = get_db()
|
||||
|
||||
existing = conn.execute("SELECT period FROM draws WHERE period=?", (period,)).fetchone()
|
||||
if existing:
|
||||
print(f"⚠️ 第{period}期已存在,跳过")
|
||||
conn.close()
|
||||
return
|
||||
|
||||
nums = [int(n) for n in numbers[:6]]
|
||||
special = int(numbers[6]) if len(numbers) > 6 else 0
|
||||
|
||||
zodiac_list = []
|
||||
element_list = []
|
||||
color_list = []
|
||||
all_nums = nums + [special]
|
||||
|
||||
for i, num in enumerate(all_nums):
|
||||
if zodiacs and i < len(zodiacs):
|
||||
zodiac_list.append(zodiacs[i])
|
||||
else:
|
||||
zodiac_list.append(get_zodiac(num))
|
||||
element_list.append(get_element(num))
|
||||
color_list.append(get_color(num))
|
||||
|
||||
conn.execute("""INSERT INTO draws
|
||||
(period, date, n1,n2,n3,n4,n5,n6,special, z1,z2,z3,z4,z5,z6,z_special, e1,e2,e3,e4,e5,e6,e_special, c1,c2,c3,c4,c5,c6,c_special)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(period, datetime.now().strftime("%Y-%m-%d"),
|
||||
*nums, special,
|
||||
*zodiac_list,
|
||||
*element_list,
|
||||
*color_list))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
nums_str = " ".join([f"{n:02d}" for n in nums])
|
||||
print(f"✅ 第{period}期已添加: {nums_str} + {special:02d}")
|
||||
|
||||
def show_history(limit=10):
|
||||
"""显示历史记录"""
|
||||
conn = get_db()
|
||||
rows = conn.execute("SELECT * FROM draws ORDER BY CAST(period AS INTEGER) DESC LIMIT ?", (limit,)).fetchall()
|
||||
conn.close()
|
||||
|
||||
if not rows:
|
||||
print("📭 暂无历史记录")
|
||||
return
|
||||
|
||||
print(f"📋 最近{len(rows)}期开奖记录:\n")
|
||||
for r in rows:
|
||||
nums = [r['n1'], r['n2'], r['n3'], r['n4'], r['n5'], r['n6']]
|
||||
special = r['special']
|
||||
zodiacs = [r['z1'], r['z2'], r['z3'], r['z4'], r['z5'], r['z6']]
|
||||
elements = [r['e1'], r['e2'], r['e3'], r['e4'], r['e5'], r['e6']]
|
||||
|
||||
nums_str = " ".join([f"{n:02d}" for n in nums])
|
||||
print(f"第{r['period']}期 ({r['date']}): {nums_str} + {special:02d}")
|
||||
|
||||
info = []
|
||||
for i in range(6):
|
||||
info.append(f"{nums[i]:02d}({zodiacs[i]}/{elements[i]})")
|
||||
info.append(f"+ {special:02d}({r['z_special']}/{r['e_special']})特")
|
||||
print(f" {' '.join(info)}")
|
||||
print()
|
||||
|
||||
def analyze():
|
||||
"""分析开奖数据"""
|
||||
conn = get_db()
|
||||
rows = conn.execute("SELECT * FROM draws").fetchall()
|
||||
conn.close()
|
||||
|
||||
if not rows:
|
||||
print("📭 暂无数据")
|
||||
return
|
||||
|
||||
print(f"📊 共{len(rows)}期数据分析:\n")
|
||||
|
||||
all_nums = []
|
||||
special_nums = []
|
||||
zodiac_counter = Counter()
|
||||
element_counter = Counter()
|
||||
color_counter = Counter()
|
||||
|
||||
for r in rows:
|
||||
nums = [r['n1'], r['n2'], r['n3'], r['n4'], r['n5'], r['n6']]
|
||||
all_nums.extend(nums)
|
||||
special_nums.append(r['special'])
|
||||
|
||||
for i in range(1, 7):
|
||||
zodiac_counter[r[f'z{i}']] += 1
|
||||
element_counter[r[f'e{i}']] += 1
|
||||
color_counter[r[f'c{i}']] += 1
|
||||
zodiac_counter[r['z_special']] += 1
|
||||
element_counter[r['e_special']] += 1
|
||||
color_counter[r['c_special']] += 1
|
||||
|
||||
freq = Counter(all_nums)
|
||||
special_freq = Counter(special_nums)
|
||||
|
||||
print("🔥 热号(出现最多):")
|
||||
for num, count in freq.most_common(10):
|
||||
print(f" {num:02d} ({get_zodiac(num)}): {count}次")
|
||||
|
||||
print("\n❄️ 冷号(出现最少):")
|
||||
for num, count in freq.most_common()[-10:]:
|
||||
print(f" {num:02d} ({get_zodiac(num)}): {count}次")
|
||||
|
||||
print("\n🎯 特码频率:")
|
||||
for num, count in special_freq.most_common(10):
|
||||
print(f" {num:02d} ({get_zodiac(num)}): {count}次")
|
||||
|
||||
print("\n🐉 生肖频率:")
|
||||
for zodiac, count in zodiac_counter.most_common():
|
||||
print(f" {zodiac}: {count}次")
|
||||
|
||||
print("\n🌊 五行频率:")
|
||||
for element, count in element_counter.most_common():
|
||||
print(f" {element}: {count}次")
|
||||
|
||||
print("\n🎨 波色频率:")
|
||||
for color, count in color_counter.most_common():
|
||||
print(f" {color}: {count}次")
|
||||
|
||||
big = sum(1 for n in all_nums if n >= 25)
|
||||
small = sum(1 for n in all_nums if n < 25)
|
||||
odd = sum(1 for n in all_nums if n % 2 == 1)
|
||||
even = sum(1 for n in all_nums if n % 2 == 0)
|
||||
print(f"\n📏 大小: 大{big} / 小{small}")
|
||||
print(f"📏 单双: 单{odd} / 双{even}")
|
||||
|
||||
def save_cold_data(key, content):
|
||||
"""保存冷数据到数据库"""
|
||||
conn = get_db()
|
||||
conn.execute("""INSERT OR REPLACE INTO cold_data (key, content, updated_at)
|
||||
VALUES (?, ?, ?)""", (key, content, datetime.now().isoformat()))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"✅ 冷数据已保存: {key}")
|
||||
|
||||
def get_cold_data(key):
|
||||
"""获取冷数据"""
|
||||
conn = get_db()
|
||||
row = conn.execute("SELECT content FROM cold_data WHERE key=?", (key,)).fetchone()
|
||||
conn.close()
|
||||
return row['content'] if row else None
|
||||
|
||||
def import_json(filepath):
|
||||
"""导入JSON历史数据到SQLite"""
|
||||
with open(filepath) as f:
|
||||
data = json.load(f)
|
||||
|
||||
conn = get_db()
|
||||
count = 0
|
||||
for item in data:
|
||||
period = item.get('period')
|
||||
if not period:
|
||||
continue
|
||||
|
||||
existing = conn.execute("SELECT period FROM draws WHERE period=?", (period,)).fetchone()
|
||||
if existing:
|
||||
continue
|
||||
|
||||
numbers = item.get('numbers', [])
|
||||
special = item.get('special', 0)
|
||||
zodiacs_raw = [d.get('zodiac') for d in item.get('details', [])]
|
||||
|
||||
if len(numbers) < 6:
|
||||
continue
|
||||
|
||||
all_nums = numbers[:6] + [special]
|
||||
zodiac_list = []
|
||||
element_list = []
|
||||
color_list = []
|
||||
|
||||
for i, num in enumerate(all_nums):
|
||||
if zodiacs_raw and i < len(zodiacs_raw) and zodiacs_raw[i]:
|
||||
zodiac_list.append(zodiacs_raw[i])
|
||||
else:
|
||||
zodiac_list.append(get_zodiac(num))
|
||||
element_list.append(get_element(num))
|
||||
color_list.append(get_color(num))
|
||||
|
||||
conn.execute("""INSERT INTO draws
|
||||
(period, date, n1,n2,n3,n4,n5,n6,special, z1,z2,z3,z4,z5,z6,z_special, e1,e2,e3,e4,e5,e6,e_special, c1,c2,c3,c4,c5,c6,c_special)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(period, item.get('date', ''),
|
||||
*numbers[:6], special,
|
||||
*zodiac_list, *element_list, *color_list))
|
||||
count += 1
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"✅ 导入{count}条记录")
|
||||
|
||||
def zodiac_table():
|
||||
"""显示生肖属性表"""
|
||||
print("🐉 2026年生肖号码对照表(网站实际映射):\n")
|
||||
zodiac_nums = {}
|
||||
for num in range(1, 50):
|
||||
zodiac = get_zodiac(num)
|
||||
if zodiac not in zodiac_nums:
|
||||
zodiac_nums[zodiac] = []
|
||||
zodiac_nums[zodiac].append(num)
|
||||
for zodiac in ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"]:
|
||||
nums = zodiac_nums.get(zodiac, [])
|
||||
print(f" {zodiac}: {', '.join([f'{n:02d}' for n in nums])}")
|
||||
|
||||
def next_draw():
|
||||
conn = get_db()
|
||||
r = conn.execute("SELECT * FROM draws ORDER BY CAST(period AS INTEGER) DESC LIMIT 1").fetchone()
|
||||
conn.close()
|
||||
|
||||
if r:
|
||||
nums = [r['n1'], r['n2'], r['n3'], r['n4'], r['n5'], r['n6']]
|
||||
print(f"📊 最新开奖: 第{r['period']}期")
|
||||
print(f" 号码: {' '.join([f'{n:02d}' for n in nums])} + {r['special']:02d}")
|
||||
print(f"\n⏰ 下期开奖时间: 每周二、四、六 21:30")
|
||||
print(f" 数据源: https://tktk.tktk4.cc/ww.htm")
|
||||
|
||||
def save_image_link(category, title, url, period=None):
|
||||
"""保存图片链接到数据库"""
|
||||
conn = get_db()
|
||||
conn.execute("INSERT INTO image_links (category, title, url, period) VALUES (?,?,?,?)",
|
||||
(category, title, url, period))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"✅ 图片链接已保存: {category}/{title}")
|
||||
|
||||
def list_image_links(category=None, period=None):
|
||||
"""列出图片链接"""
|
||||
conn = get_db()
|
||||
sql = "SELECT * FROM image_links WHERE 1=1"
|
||||
params = []
|
||||
if category:
|
||||
sql += " AND category=?"
|
||||
params.append(category)
|
||||
if period:
|
||||
sql += " AND period=?"
|
||||
params.append(period)
|
||||
sql += " ORDER BY created_at DESC LIMIT 50"
|
||||
rows = conn.execute(sql, params).fetchall()
|
||||
conn.close()
|
||||
if not rows:
|
||||
print("📭 暂无图片链接")
|
||||
return
|
||||
for r in rows:
|
||||
print(f"[{r['category']}] {r['title']}: {r['url']}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
|
||||
cmd = sys.argv[1]
|
||||
|
||||
if cmd == "fetch":
|
||||
print("⚠️ 页面使用Vue.js动态加载,开奖时间段外(21:14-21:40)可能无数据")
|
||||
print(" 建议开奖期间用浏览器抓取,或用 add 命令手动添加")
|
||||
|
||||
elif cmd == "history":
|
||||
limit = int(sys.argv[2]) if len(sys.argv) > 2 else 10
|
||||
show_history(limit)
|
||||
|
||||
elif cmd == "analyze":
|
||||
analyze()
|
||||
|
||||
elif cmd == "zodiac":
|
||||
zodiac_table()
|
||||
|
||||
elif cmd == "next":
|
||||
next_draw()
|
||||
|
||||
elif cmd == "add":
|
||||
if len(sys.argv) < 4:
|
||||
print("用法: python3 lottery.py add <期号> <号码,逗号分隔>")
|
||||
sys.exit(1)
|
||||
add_draw(sys.argv[2], [int(x) for x in sys.argv[3].split(",")])
|
||||
|
||||
elif cmd == "add_full":
|
||||
if len(sys.argv) < 5:
|
||||
print("用法: python3 lottery.py add_full <期号> <号码> <生肖>")
|
||||
sys.exit(1)
|
||||
add_draw(sys.argv[2], [int(x) for x in sys.argv[3].split(",")], sys.argv[4].split(","))
|
||||
|
||||
elif cmd == "import_json":
|
||||
if len(sys.argv) < 3:
|
||||
print("用法: python3 lottery.py import_json <文件路径>")
|
||||
sys.exit(1)
|
||||
import_json(sys.argv[2])
|
||||
|
||||
elif cmd == "save_cold":
|
||||
if len(sys.argv) < 4:
|
||||
print("用法: python3 lottery.py save_cold <key> <content>")
|
||||
sys.exit(1)
|
||||
save_cold_data(sys.argv[2], sys.argv[3])
|
||||
|
||||
elif cmd == "get_cold":
|
||||
if len(sys.argv) < 3:
|
||||
print("用法: python3 lottery.py get_cold <key>")
|
||||
sys.exit(1)
|
||||
content = get_cold_data(sys.argv[2])
|
||||
if content:
|
||||
print(content)
|
||||
else:
|
||||
print("📭 无数据")
|
||||
|
||||
elif cmd == "save_image":
|
||||
if len(sys.argv) < 5:
|
||||
print("用法: python3 lottery.py save_image <类别> <标题> <URL> [期号]")
|
||||
sys.exit(1)
|
||||
period = sys.argv[5] if len(sys.argv) > 5 else None
|
||||
save_image_link(sys.argv[2], sys.argv[3], sys.argv[4], period)
|
||||
|
||||
elif cmd == "list_images":
|
||||
category = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
list_image_links(category)
|
||||
|
||||
else:
|
||||
print(f"未知命令: {cmd}")
|
||||
print(__doc__)
|
||||
Reference in New Issue
Block a user