当前位置: 首页 > news >正文

go语言实现腾讯股票获取示例(并发)

package mainimport ("encoding/csv""flag""fmt""io""log""net/http""os""strconv""strings""sync""time""golang.org/x/text/encoding/simplifiedchinese"
)// StockData 股票数据结构
type StockData struct {Code          string  // 股票代码Name          string  // 股票名称Price         float64 // 当前价Change        float64 // 涨跌额ChangePercent float64 // 涨跌幅Volume        int64   // 成交量Amount        float64 // 成交额High          float64 // 最高价Low           float64 // 最低价Open          float64 // 开盘价PreClose      float64 // 前收盘价
}// 获取股票代码列表
func getStockCodes() ([]string, error) {// 模拟股票代码列表var codes []string// 添加一些上海市场的股票代码for i := 0; i < 10000; i++ {code := fmt.Sprintf("sh%06d", 600000+i)codes = append(codes, code)}// 添加一些深圳市场的股票代码for i := 0; i < 999; i++ {code := fmt.Sprintf("sz%06d", 1+i)codes = append(codes, code)}return codes, nil
}// 获取单个股票数据
func fetchStockData(code string) (*StockData, error) {url := fmt.Sprintf("http://qt.gtimg.cn/q=%s", code)// 创建HTTP客户端client := &http.Client{Timeout: 5 * time.Second,}req, err := http.NewRequest("GET", url, nil)if err != nil {return nil, err}// 设置适当的请求头req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)")req.Header.Set("Accept", "*/*")resp, err := client.Do(req)if err != nil {return nil, err}defer resp.Body.Close()// 读取响应体body, err := io.ReadAll(resp.Body)if err != nil {return nil, err}// 将GB2312/GBK编码的响应内容转换为UTF-8var content stringdataUTF8, err := simplifiedchinese.GBK.NewDecoder().Bytes(body)if err != nil {// 如果转换失败,使用原始内容content = string(body)} else {content = string(dataUTF8)}// 检查数据有效性if !strings.Contains(content, "~") {return nil, fmt.Errorf("数据格式异常")}// 提取数据部分 - 优化提取逻辑start := strings.Index(content, "=\"")end := strings.LastIndex(content, "\"")if start == -1 || end == -1 {return nil, fmt.Errorf("无法提取有效数据")}// 提取数据部分data := content[start+2 : end]return parseStockData(code, data)
}// 解析股票数据
func parseStockData(code, data string) (*StockData, error) {fields := strings.Split(data, "~")if len(fields) < 40 {return nil, fmt.Errorf("数据字段不足")}stockName := fields[1]stock := &StockData{Code: code,Name: stockName,}// 解析数值字段if price, err := strconv.ParseFloat(fields[3], 64); err == nil {stock.Price = price}if change, err := strconv.ParseFloat(fields[4], 64); err == nil {stock.Change = change}if changePercent, err := strconv.ParseFloat(strings.TrimSuffix(fields[5], "%"), 64); err == nil {stock.ChangePercent = changePercent}if volume, err := strconv.ParseInt(fields[6], 10, 64); err == nil {stock.Volume = volume}if amount, err := strconv.ParseFloat(fields[7], 64); err == nil {stock.Amount = amount}if high, err := strconv.ParseFloat(fields[33], 64); err == nil {stock.High = high}if low, err := strconv.ParseFloat(fields[34], 64); err == nil {stock.Low = low}if open, err := strconv.ParseFloat(fields[35], 64); err == nil {stock.Open = open}if preClose, err := strconv.ParseFloat(fields[36], 64); err == nil {stock.PreClose = preClose}return stock, nil
}// 处理股票数据(带重试)
func processStockData(code string, maxRetries int, retryDelay time.Duration) (*StockData, error) {for i := 0; i < maxRetries; i++ {// 尝试从API获取数据stock, err := fetchStockData(code)if err == nil && stock != nil && stock.Name != "" && len(stock.Name) > 1 {return stock, nil}if i < maxRetries-1 {time.Sleep(retryDelay)}}return nil, fmt.Errorf("获取股票 %s 数据失败", code)
}// 保存数据到CSV文件
func saveToCSV(stocks []*StockData, filename string) error {file, err := os.Create(filename)if err != nil {return err}defer file.Close()// 写入UTF-8 BOM,确保Excel能正确识别编码_, err = file.WriteString("\xEF\xBB\xBF")if err != nil {return err}// 创建CSV写入器writer := csv.NewWriter(file)writer.Comma = ','writer.UseCRLF = true// 写入表头headers := []string{"代码", "名称", "当前价", "涨跌额", "涨跌幅", "成交量", "成交额", "最高价", "最低价", "开盘价", "前收盘价"}if err := writer.Write(headers); err != nil {return err}// 写入数据行for _, stock := range stocks {row := []string{stock.Code,stock.Name, // 已修复的股票名称fmt.Sprintf("%.2f", stock.Price),fmt.Sprintf("%.2f", stock.Change),fmt.Sprintf("%.2f", stock.ChangePercent),fmt.Sprintf("%d", stock.Volume),fmt.Sprintf("%.2f", stock.Amount),fmt.Sprintf("%.2f", stock.High),fmt.Sprintf("%.2f", stock.Low),fmt.Sprintf("%.2f", stock.Open),fmt.Sprintf("%.2f", stock.PreClose),}if err := writer.Write(row); err != nil {return err}}writer.Flush()return writer.Error()
}// 显示股票数据
func displayStocks(stocks []*StockData) {fmt.Println("\n=== 股票数据统计 ===")fmt.Printf("获取到 %d 只股票数据\n\n", len(stocks))// 优化列宽,确保中文显示正常fmt.Printf("%-10s %-12s %-10s %-10s %-8s %-10s\n","代码", "名称", "当前价", "涨跌额", "涨跌幅", "成交额")fmt.Println("----------------------------------------------------------------------")// 只显示前5只股票limit := 5if len(stocks) < limit {limit = len(stocks)}for i := 0; i < limit; i++ {stock := stocks[i]// 确保股票名称不会溢出列宽displayName := stock.Nameif len([]rune(displayName)) > 6 {displayName = string([]rune(displayName)[:6]) + "..."}// 修复涨跌幅显示格式,确保百分号直接跟在数字后面,没有空格fmt.Printf("%-10s %-12s %-10.2f %-10.2f %5.2f %-10.2f\n",stock.Code, displayName, stock.Price, stock.Change, stock.ChangePercent, stock.Amount)}fmt.Println()
}func main() {// 定义命令行参数maxCount := flag.Int("n", 10, "获取股票数量")concurrency := flag.Int("c", 5, "并发数")flag.Parse()// 参数验证if *concurrency <= 0 || *concurrency > 20 {*concurrency = 5}if *maxCount <= 0 || *maxCount > 100 {*maxCount = 10}fmt.Println("开始获取A股数据...")startTime := time.Now()// 获取股票代码codes, err := getStockCodes()if err != nil {log.Fatalf("获取股票代码失败: %v", err)}fmt.Printf("过滤后共有 %d 个股票代码\n", len(codes))fmt.Printf("限制获取 %d 只股票数据\n", *maxCount)// 限制数量if len(codes) > *maxCount {codes = codes[:*maxCount]}fmt.Printf("开始批量获取数据(并发数: %d)...\n", *concurrency)// 并发获取数据resultChan := make(chan *StockData, len(codes))errChan := make(chan error, len(codes))var wg sync.WaitGroupsemaphore := make(chan struct{}, *concurrency)for _, code := range codes {wg.Add(1)semaphore <- struct{}{}go func(code string) {defer wg.Done()defer func() { <-semaphore }()stock, err := processStockData(code, 3, 300*time.Millisecond)if err != nil {errChan <- fmt.Errorf("股票 %s: %v", code, err)return}resultChan <- stock}(code)}// 等待所有协程完成go func() {wg.Wait()close(resultChan)close(errChan)}()// 收集数据var validStocks []*StockDatafor stock := range resultChan {validStocks = append(validStocks, stock)}// 输出错误for err := range errChan {log.Printf("错误: %v", err)}duration := time.Since(startTime)fmt.Printf("数据获取完成,耗时: %v\n", duration)// 显示数据displayStocks(validStocks)// 保存到CSV文件filename := fmt.Sprintf("stock_data_%s.csv", time.Now().Format("20060102_150405"))if err := saveToCSV(validStocks, filename); err != nil {log.Fatalf("保存CSV文件失败: %v", err)}fmt.Printf("数据已保存到: %s\n", filename)fmt.Println("提示:CSV文件已使用UTF-8编码(带BOM)保存,可以直接在Excel中打开查看中文内容。")
}
http://www.jsqmd.com/news/31617/

相关文章:

  • 出海 AI 公司招 Java 大佬|北京
  • 在欧拉系统上安装openGauss数据库
  • 2025.11.4总结
  • 医疗非结构化数据价值挖掘:文档抽取技术的工作原理与场景实践
  • 18、Flink CDC监听MySQL-Binlog实现数据监听
  • Ai元人文构想并不神秘—系统化流程图
  • 2025 年 11 月 EVA 厂家推荐排行榜,eva塑料,eva板材,eva卷材,eva发泡材料,eva橡塑制品公司推荐
  • 2025 年 11 月防爆电磁阀厂家推荐排行榜,气动防爆电磁阀,先导式防爆电磁阀,直动式防爆电磁阀,不锈钢防爆电磁阀,禁铜禁锌防爆电磁阀公司推荐
  • 11.04
  • 20251104 正睿
  • 【做题记录】多校-dp
  • CSP-S 题解反思考场游记
  • 新学期每日总结(第19天)
  • 2025 年 11 月扑灭司林厂家推荐排行榜:专业杀虫剂,高效农药,卫生防疫用药,农业喷洒用药源头厂家精选!
  • 2025 年 11 月高压清洗机厂家推荐排行榜,超高压清洗机组,超高压水清洗设备,超高压清洗装置,工业超高压清洗设备公司精选
  • Centos7安装新版本python3.10
  • 2025 年 11 月高温轴承厂家权威推荐榜:耐高温轴承,真空高温轴承,窑炉高温轴承,BOPP链夹高温轴承,高温调心球轴承,高温关节轴承,高温滚针轴承,高温角接触轴承,高温圆柱滚子轴承公司推荐
  • 2025 年 11 月不干胶轮转机厂家推荐排行榜,商标不干胶轮转机,高速轮转印刷设备,高效稳定生产解决方案
  • swagger-typescript-api
  • HAL库DMA框架
  • 2025 年 11 月电线电缆厂家推荐排行榜,国标电线电缆,中缆电线电缆,工程电线电缆,环保电线电缆,家用电线电缆,工业电线电缆,光伏电线电缆,耐火电线电缆公司推荐
  • 2025 年 11 月清洗机厂家推荐排行榜,全自动/工业/零排放/双溶剂/碳氢/改性醇/真空/全密闭清洗机设备公司精选
  • 2025 年 11 月电线电缆厂家推荐排行榜,电力电缆,控制电缆,通信电缆,阻燃电缆,高压电缆公司推荐
  • 2025 年 11 月电磁阀线圈厂家推荐排行榜,电磁线圈,电磁铁线圈,小型电磁线圈,微型线圈,汽车电磁线圈,车用感应线圈,防爆线圈,防爆电磁线圈,直流电磁线圈,电磁线圈定制公司推荐
  • 2025 年 11 月潜水泵厂家推荐排行榜,新型潜水泵,节能潜水泵,低噪声潜水泵,超低压潜水泵,防爆潜水泵,高压潜水泵,防腐潜水泵公司推荐
  • 2025 年 11 月消杀药剂厂家推荐排行榜,扑灭司林/5%扑灭司林,苯甲酸苄酯/25%苯甲酸苄酯,15%胺氯菊百灭宁,疥螨,阴虱,科灭达公司推荐
  • 2025 年 11 月回信器厂家推荐排行榜,隔爆回信器,阀门回信器,防爆回信器,限位开关回信器,气动阀回信器,气动回信器公司推荐
  • 数据分析流程
  • 2025 年 11 月闭式冷却塔厂家推荐排行榜,工业闭式冷却塔,横流闭式冷却塔,逆流闭式冷却塔,复合流闭式冷却塔公司推荐
  • 2025 年 11 月锅炉厂家推荐排行榜,有机热载体锅炉,导热油锅炉,生物质锅炉,蒸汽锅炉,燃天然气锅炉,热水锅炉公司推荐