go语言访问新浪股票(hq.sinajs.cn)
config.go:
package main
// 股票代码配置 - 简化版本,只包含主要指数和热门股票
var stockSymbols = []string{
// 主要指数
"sh000001", // 上证指数
"sh000016", // 上证50
"sh000300", // 沪深300
"sz399001", // 深证成指
"sz399006", // 创业板指
// 热门股票
"sh600519", // 贵州茅台
"sz000858", // 五粮液
"sh601318", // 中国平安
"sz000001", // 平安银行
"sh600036", // 招商银行
"sz000002", // 万科A
"sh601166", // 兴业银行
"sz000651", // 格力电器
"sh600887", // 伊利股份
"sz000333", // 美的集团
"sh601888", // 中国国旅
"sz000063", // 中兴通讯
"sh600276", // 恒瑞医药
"sz000538", // 云南白药
"sh600104", // 上汽集团
"sz000625", // 长安汽车
"sh601398", // 工商银行
"sz000776", // 广发证券
"sh601288", // 农业银行
"sz000725", // 京东方A
"sh601328", // 交通银行
"sz000100", // TCL科技
"sh601988", // 中国银行
"sz000977", // 浪潮信息
"sh601939", // 建设银行
"sz000066", // 中国长城
"sh601668", // 中国建筑
"sh601766", // 中国中车
"sz000898", // 鞍钢股份
"sh601800", // 中国交建
"sh601186", // 中国铁建
"sh601390", // 中国中铁
"sh601618", // 中国中冶
"sh601857", // 中国石油
"sh601088", // 中国神华
"sh601899", // 紫金矿业
"sh601600", // 中国铝业
"sh601168", // 西部矿业
"sh601958", // 金钼股份
"sh601699", // 潞安环能
"sh601101", // 昊华能源
"sh601666", // 平煤股份
"sh601001", // 大同煤业
"sh601898", // 中煤能源
"sh601918", // 新集能源
}
main.go:
package main
import (
"encoding/csv"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
// StockData 股票数据结构
type StockData struct {
Symbol string // 股票代码
Name string // 股票名称
Open float64 // 开盘价
PrevClose float64 // 昨收价
Price float64 // 当前价
High float64 // 最高价
Low float64 // 最低价
Volume int64 // 成交量
Amount float64 // 成交额
Change float64 // 涨跌额
ChangeRate float64 // 涨跌幅
Time string // 时间
}
func main() {
fmt.Println("开始获取股票数据...")
// 获取所有股票数据
stocks, err := fetchAllStockData()
if err != nil {
fmt.Printf("获取股票数据失败: %v\n", err)
return
}
// 显示股票数据
displayStockData(stocks)
// 保存到CSV文件
err = saveToCSV(stocks, "stock_data.csv")
if err != nil {
fmt.Printf("保存CSV文件失败: %v\n", err)
} else {
fmt.Println("数据已保存到 stock_data.csv")
}
}
// fetchAllStockData 获取所有股票数据
func fetchAllStockData() ([]StockData, error) {
var stocks []StockData
// 分批获取,避免URL过长
batchSize := 20 // 减少批次大小,避免URL过长
for i := 0; i < len(stockSymbols); i += batchSize {
end := i + batchSize
if end > len(stockSymbols) {
end = len(stockSymbols)
}
batchSymbols := stockSymbols[i:end]
batchStocks, err := fetchBatchStockData(batchSymbols)
if err != nil {
fmt.Printf("批次 %d 获取失败: %v\n", i/batchSize+1, err)
continue // 继续处理下一批,而不是直接返回错误
}
stocks = append(stocks, batchStocks...)
// 添加延迟避免请求过快
time.Sleep(500 * time.Millisecond)
}
return stocks, nil
}
// fetchBatchStockData 批量获取股票数据
func fetchBatchStockData(symbols []string) ([]StockData, error) {
// 构建URL
url := "https://hq.sinajs.cn/list=" + strings.Join(symbols, ",")
// 创建HTTP客户端
client := &http.Client{
Timeout: 10 * time.Second,
}
// 创建请求
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err)
}
// 设置请求头(模拟浏览器请求)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
req.Header.Set("Referer", "https://finance.sina.com.cn/")
// 发送请求
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
// 检查响应状态
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP错误: %s", resp.Status)
}
// 读取响应内容
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
// 解析响应数据
return parseResponseData(string(body), symbols)
}
// parseResponseData 解析新浪财经返回的数据
func parseResponseData(data string, symbols []string) ([]StockData, error) {
var stocks []StockData
// 按行分割数据
lines := strings.Split(data, "\n")
for i, line := range lines {
if line == "" || !strings.Contains(line, "=\"") {
continue
}
// 提取股票数据部分
parts := strings.SplitN(line, "=\"", 2)
if len(parts) < 2 {
continue
}
// 获取股票代码
symbol := ""
if i < len(symbols) {
symbol = symbols[i]
}
// 解析数据字段
stock, err := parseStockFields(parts[1], symbol)
if err != nil {
fmt.Printf("解析股票数据失败 (代码: %s): %v\n", symbol, err)
continue
}
stocks = append(stocks, stock)
}
return stocks, nil
}
// parseStockFields 解析股票数据字段
func parseStockFields(data string, symbol string) (StockData, error) {
// 去除末尾的引号
data = strings.TrimSuffix(data, "\";")
// 分割字段
fields := strings.Split(data, ",")
if len(fields) < 30 {
return StockData{}, fmt.Errorf("数据字段不足")
}
// 解码股票名称(GB2312到UTF-8)
decodedName, decodeErr := decodeGB2312(fields[0])
if decodeErr != nil {
// 如果解码失败,使用原始名称
decodedName = fields[0]
}
stock := StockData{
Symbol: symbol,
Name: decodedName,
}
// 解析数值字段
var err error
stock.Open, err = parseFloat(fields[1])
if err != nil {
return StockData{}, err
}
stock.PrevClose, err = parseFloat(fields[2])
if err != nil {
return StockData{}, err
}
stock.Price, err = parseFloat(fields[3])
if err != nil {
return StockData{}, err
}
stock.High, err = parseFloat(fields[4])
if err != nil {
return StockData{}, err
}
stock.Low, err = parseFloat(fields[5])
if err != nil {
return StockData{}, err
}
stock.Volume, err = parseInt(fields[8])
if err != nil {
return StockData{}, err
}
stock.Amount, err = parseFloat(fields[9])
if err != nil {
return StockData{}, err
}
// 计算涨跌额和涨跌幅
if stock.PrevClose > 0 {
stock.Change = stock.Price - stock.PrevClose
stock.ChangeRate = (stock.Change / stock.PrevClose) * 100
}
// 时间字段
if len(fields) > 30 {
stock.Time = fields[30] + " " + fields[31]
}
return stock, nil
}
// parseFloat 解析浮点数
func parseFloat(s string) (float64, error) {
if s == "" {
return 0, nil
}
return parseFloatWithDefault(s, 0)
}
// parseFloatWithDefault 解析浮点数,带默认值
func parseFloatWithDefault(s string, def float64) (float64, error) {
if s == "" {
return def, nil
}
var result float64
_, err := fmt.Sscanf(s, "%f", &result)
if err != nil {
return def, err
}
return result, nil
}
// parseInt 解析整数
func parseInt(s string) (int64, error) {
if s == "" {
return 0, nil
}
var result int64
_, err := fmt.Sscanf(s, "%d", &result)
if err != nil {
return 0, err
}
return result, nil
}
// displayStockData 显示股票数据
func displayStockData(stocks []StockData) {
fmt.Printf("\n=== 股票数据 (%d只股票) ===\n", len(stocks))
fmt.Printf("%-10s %-12s %-8s %-8s %-8s %-8s %-8s %-12s\n",
"代码", "名称", "当前价", "涨跌额", "涨跌幅", "最高", "最低", "时间")
fmt.Println(strings.Repeat("-", 90))
for _, stock := range stocks {
changeSign := ""
if stock.Change > 0 {
changeSign = "+"
}
// 限制名称长度,避免显示问题
name := stock.Name
if len(name) > 10 {
name = name[:10] + "..."
}
fmt.Printf("%-10s %-12s %-8.2f %s%-7.2f %-7.2f%% %-8.2f %-8.2f %-12s\n",
stock.Symbol, name, stock.Price, changeSign, stock.Change,
stock.ChangeRate, stock.High, stock.Low, stock.Time)
}
}
// saveToCSV 保存数据到CSV文件
func saveToCSV(stocks []StockData, filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
// 写入表头
header := []string{"股票代码", "股票名称", "开盘价", "昨收价", "当前价", "最高价",
"最低价", "成交量", "成交额", "涨跌额", "涨跌幅", "时间"}
if err := writer.Write(header); err != nil {
return err
}
// 写入数据
for _, stock := range stocks {
row := []string{
stock.Symbol,
stock.Name,
fmt.Sprintf("%.2f", stock.Open),
fmt.Sprintf("%.2f", stock.PrevClose),
fmt.Sprintf("%.2f", stock.Price),
fmt.Sprintf("%.2f", stock.High),
fmt.Sprintf("%.2f", stock.Low),
fmt.Sprintf("%d", stock.Volume),
fmt.Sprintf("%.2f", stock.Amount),
fmt.Sprintf("%.2f", stock.Change),
fmt.Sprintf("%.2f", stock.ChangeRate),
stock.Time,
}
if err := writer.Write(row); err != nil {
return err
}
}
return nil
}
// decodeGB2312 将GB2312编码的字符串转换为UTF-8
func decodeGB2312(s string) (string, error) {
// 创建GB2312到UTF-8的转换器
transformer := simplifiedchinese.GBK.NewDecoder()
// 将字符串转换为字节切片
gbkBytes := []byte(s)
// 进行编码转换
utf8Bytes, _, err := transform.Bytes(transformer, gbkBytes)
if err != nil {
return s, fmt.Errorf("GB2312解码失败: %v", err)
}
return string(utf8Bytes), nil
}

浙公网安备 33010602011771号