Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions docs/adapters/browser/sinafinance.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
| `opencli sinafinance news` | 新浪财经 7×24 小时实时快讯 | 🌐 Public |
| `opencli sinafinance rolling-news` | 新浪财经滚动新闻 | 🔐 Browser |
| `opencli sinafinance stock` | 新浪财经行情(A股/港股/美股) | 🌐 Public |
| `opencli sinafinance stock-rank` | 新浪财经热搜榜 | 🔐 Browser |

## Usage Examples

Expand Down Expand Up @@ -56,6 +57,28 @@ opencli sinafinance stock 招商证券
opencli sinafinance stock 贵州茅台 -f json
```

### stock-rank - 热搜榜

```bash
# Default A股热搜榜
opencli sinafinance stock-rank

# 港股热搜榜
opencli sinafinance stock-rank --market hk

# 美股热搜榜
opencli sinafinance stock-rank --market us

# 外汇热搜榜
opencli sinafinance stock-rank --market ft

# 期货热搜榜
opencli sinafinance stock-rank --market wh

# JSON output
opencli sinafinance stock-rank -f json
```

## Options

### news
Expand All @@ -71,15 +94,22 @@ opencli sinafinance stock 贵州茅台 -f json
|--------|-------------|
| `--market` | Market: `cn`, `hk`, `us`, `auto` (default: auto). When `auto`, searches in cn, hk, us order |

### stock-rank

| Option | Description |
|--------|-------------|
| `--market` | Market: `cn` (A股, 默认), `ft` (期货), `us` (美股), `wh` (外汇), `hk` (港股) |

## Prerequisites

- `news` & `stock`: No browser required — uses public API
- `rolling-news`: Chrome running and **logged into** `finance.sina.com.cn`
- For `rolling-news`: [Browser Bridge extension](/guide/browser-bridge) installed
- `rolling-news` & `stock-rank`: Chrome running and **logged into** `finance.sina.com.cn`
- For `rolling-news` & `stock-rank`: [Browser Bridge extension](/guide/browser-bridge) installed

## Notes

- `news` and `stock` use public APIs — no browser or login needed
- `stock` supports Chinese names, Chinese codes, and ticker symbols; auto-detects market
- Market priority for auto-detection: cn (A股) → hk (港股) → us (美股)
- US stock `High`/`Low` columns show 52-week range; A股/港股 show today's range
- `stock-rank` scrapes the hot search list from the Sina Finance homepage; requires browser login
68 changes: 68 additions & 0 deletions src/clis/sinafinance/stock-rank.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Sinafinance stock rank
*/

import { cli, Strategy } from '../../registry.js';

cli({
site: 'sinafinance',
name: 'stock-rank',
description: '新浪财经热搜榜',
domain: 'finance.sina.cn',
strategy: Strategy.COOKIE,
navigateBefore: false,
args: [
{ name: 'market', type: 'string', default: 'cn', choices: ['cn', 'hk', 'us', 'wh', 'ft'], help: 'Market: cn (A股), hk (港股), us (美股), wh (外汇), ft (期货)' },
],
columns: ['rank', 'name', 'symbol', 'market', 'price', 'change', 'url'],
func: async (page, _args) => {
const market = _args.market || 'cn';

await page.goto('https://finance.sina.cn/');
await page.wait({ selector: '#actionSearch', timeout: 10000 });

const payload = await page.evaluate(`
(async () => {
const wait = (ms) => new Promise(r => setTimeout(r, ms));
const cleanText = (value) => (value || '').replace(/\\s+/g, ' ').trim();
const marketType = ${JSON.stringify(market)};

const searchBtn = document.querySelector('#actionSearch');
if (searchBtn) {
searchBtn.dispatchEvent(new Event('tap', { bubbles: true }));
await wait(3000);
}

const tabEl = document.querySelector('[data-type="' + marketType + '"]');
const marketName = tabEl?.textContent || marketType;
if (marketType !== 'cn' && tabEl) {
tabEl.click();
await wait(2000);
}

const results = [];
document.querySelectorAll('#stock-list .j-stock-row').forEach(el => {
const rankEl = el.querySelector('.rank');
const nameEl = el.querySelector('.j-sname');
const codeEl = el.querySelector('.stock-code');
const priceEl = el.querySelector('.j-price');
const changeEl = el.querySelector('.j-change');
const openUrl = el.getAttribute('open-url') || '';
const fullUrl = openUrl ? 'https:' + openUrl : '';
results.push({
rank: cleanText(rankEl?.textContent || ''),
name: cleanText(nameEl?.textContent || ''),
symbol: cleanText(codeEl?.textContent || ''),
market: cleanText(marketName),
price: cleanText(priceEl?.textContent || ''),
change: cleanText(changeEl?.textContent || ''),
url: fullUrl,
});
});
return results;
})()
`);
if (!Array.isArray(payload)) return [];
return payload;
},
});