当前位置: 首页 > 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.zskr.cn/news/40204.html

相关文章:

  • 在欧拉系统上安装openGauss数据库
  • 新学期每日总结(第19天)
  • 2025 年 11 月扑灭司林厂家推荐排行榜:专业杀虫剂,高效农药,卫生防疫用药,农业喷洒用药源头厂家精选!
  • Centos7安装新版本python3.10
  • 2025 年 11 月高温轴承厂家权威推荐榜:耐高温轴承,真空高温轴承,窑炉高温轴承,BOPP链夹高温轴承,高温调心球轴承,高温关节轴承,高温滚针轴承,高温角接触轴承,高温圆柱滚子轴承公司推荐
  • 2025 年 11 月清洗机厂家推荐排行榜,全自动/工业/零排放/双溶剂/碳氢/改性醇/真空/全密闭清洗机设备公司精选
  • 2025 年 11 月闭式冷却塔厂家推荐排行榜,工业闭式冷却塔,横流闭式冷却塔,逆流闭式冷却塔,复合流闭式冷却塔公司推荐
  • 2025 年 11 月清洗机厂家推荐排行榜,高压清洗机,工业清洗机,超声波清洗机,零部件清洗设备公司推荐
  • 2025 年 11 月电缆厂家推荐排行榜,国标电缆/国网南网入围电缆,铜芯/铝合金/光伏/新能源/工业/控制/拖链/橡胶/铠装电缆公司推荐
  • 2025 年 11 月电磁铁厂家推荐排行榜,直流电磁铁,微型电磁铁,小型电磁铁,防爆电磁铁,比例电磁铁,非标电磁铁定制公司推荐
  • 大文件上传公共库
  • 2025 年 11 月控制器厂家推荐排行榜,开关控制器,自动控制器,阀门控制器,智能控制器,限位开关控制器公司推荐
  • 今日学习:二分
  • Ice Breaker Games - 一个在线免费的游戏网站,无需登录,打开即玩。
  • Java获取当前时间的下一天以及30天前的时间
  • AI大语言模型从0开发
  • 第三十三篇
  • EAS_提供多个单据详情查询接口数据给第三方进行单据查看
  • 备考笔记7
  • 详细介绍:常见反爬虫策略与破解方案汇总
  • 2025 年 11 月财税合规审计报告服务商权威推荐榜:专业审计、税务合规、财务风控,企业财税合规审计报告公司精选
  • P5369 最大前缀和
  • 奋飞咨询:以专业之光,照亮企业可持续发展通途
  • cpp生成1到n生成全排列的三种方法
  • 【Redis】实操:cluster集群部署
  • 实用指南:【Nest】登录鉴权
  • 程序员修炼之道:从小工到专家-2
  • 从零实现3D Gaussian Splatting:完整渲染流程的PyTorch代码详解
  • NOIP2025模拟1
  • 文生视频时代,RustFS如何成为AI资产库的最佳底座?