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

news/2025/11/4 23:17:26/文章来源:https://www.cnblogs.com/soarowl/p/19191665
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.mzph.cn/news/956141.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

出海 AI 公司招 Java 大佬|北京

公司介绍 Thunderbit 是一款一站式 AI Data Agent。我们专注于为商业用户提供高效的网页数据抓取能力。接下来半年,我们将发布重要升级,推出基于智能 Agent 的新功能,使数据抓取与处理更加精准高效 ~ 公司融资 510…

在欧拉系统上安装openGauss数据库

在欧拉系统上安装openGauss数据库:完整指南与问题解决 前言 在欧拉系统(openEuler)上安装openGauss数据库是一个常见的需求,但过程中可能会遇到各种问题。本文记录了完整的安装过程、遇到的问题及解决方案,希望能…

2025.11.4总结

今天继续软考的学习 今天学复习了几个排序的基本概念。还有软件工程的软件过程模型和敏捷方法。 除此之外在昨天发现一个微信小程序:软考达人 。里面有历年真题联系,章节练习,知识点学习等很多功能,是个免费的程序…

医疗非结构化数据价值挖掘:文档抽取技术的工作原理与场景实践

在当今的医疗健康领域,数据正以前所未有的速度增长。这其中,绝大部分是非结构化数据,如临床病历、出院小结、病理报告、医学影像报告、科研文献和保险单据等。这些文档中蕴含着宝贵的患者信息、诊疗经验和医学洞见,…

18、Flink CDC监听MySQL-Binlog实现数据监听

18、Flink CDC监听MySQL-Binlog实现数据监听一、CDC简介: CDC(Change Data Capture)是变更数据捕获的简称,其核心思想是监测并捕获数据库的变动(包括数据或数据表的插入、更新、删除等),将这些变更按发生的顺序…

Ai元人文构想并不神秘—系统化流程图

系统化流程图 这是一个根据“AI元人文”思想体系生成的系统化流程图。该图旨在直观地展现整个理论框架的核心结构、动态过程与内在逻辑。 flowchart TDsubgraph A [哲学基础]direction LRA1[“核心命题<br>价值对…

2025 年 11 月 EVA 厂家推荐排行榜,eva塑料,eva板材,eva卷材,eva发泡材料,eva橡塑制品公司推荐

2025 年 11 月 EVA 厂家推荐排行榜:深度解析行业优质供应商 随着环保要求的提升和材料技术的进步,乙烯-醋酸乙烯酯共聚物(EVA)作为一种多功能高分子材料,在包装、鞋材、体育用品、汽车内饰等领域的应用持续扩大。…

2025 年 11 月防爆电磁阀厂家推荐排行榜,气动防爆电磁阀,先导式防爆电磁阀,直动式防爆电磁阀,不锈钢防爆电磁阀,禁铜禁锌防爆电磁阀公司推荐

2025 年 11 月防爆电磁阀厂家推荐排行榜:气动防爆电磁阀、先导式防爆电磁阀、直动式防爆电磁阀、不锈钢防爆电磁阀、禁铜禁锌防爆电磁阀公司推荐 行业背景介绍 防爆电磁阀作为工业自动化控制系统的关键执行元件,在化…

11.04

11.04这么多的故事 这么多的人Look at yourself in the mirror钱永远是主旋律不怕输才是第一步心态 心态 好好说话 好好说话

20251104 正睿

正睿 NOIP 二十连测 C\(n, q, a_i \le 300\)。 这种题一般都要发现一些性质(不变量)才能做。这个题的是将 \(a\) 分成两组 \(S1, S2\) 的总和。 首先如果可以分成两组使得 \(s1 = s2\),那么后手必胜。\(s1 = s2 = 0…

【做题记录】多校-dp

A. Multitest Generator 考虑一个长为 \(m(m\ge 2)\) 的序列 \(b\),我们显然可以令 \(b_1=1,b_2=m-2\) 来使它变成 multitest。于是我们只需要判断能否使用 \(0\) 次或 \(1\) 次操作使其变成 multitest。 首先考虑 \(…

CSP-S 题解反思考场游记

贪心+(最小生成树,归并)+(ACAM,主席树,哈希)+ DP(贡献延后类)前言:今后可能会考虑在noip时写个游记,csp实在太烂而且没啥好写的。T1 签到小贪心 T2 第一眼这啥啊。后来想到枚举集合,然后写搜索,调半天发现…

新学期每日总结(第19天)

今日 相较昨日 学习如何连接数据库

2025 年 11 月扑灭司林厂家推荐排行榜:专业杀虫剂,高效农药,卫生防疫用药,农业喷洒用药源头厂家精选!

2025 年 11 月扑灭司林厂家推荐排行榜:专业杀虫剂,高效农药,卫生防疫用药,农业喷洒用药源头厂家精选! 随着全球公共卫生和农业害虫防治需求的持续增长,扑灭司林作为一种高效、低毒的拟除虫菊酯类杀虫剂,在卫生防…

2025 年 11 月高压清洗机厂家推荐排行榜,超高压清洗机组,超高压水清洗设备,超高压清洗装置,工业超高压清洗设备公司精选

2025 年 11 月高压清洗机厂家推荐排行榜,超高压清洗机组,超高压水清洗设备,超高压清洗装置,工业超高压清洗设备公司精选 随着工业清洗技术的不断进步,高压清洗机、超高压清洗机组、超高压水清洗设备、超高压水清洗…

Centos7安装新版本python3.10

简单说明Python2.7.5是CentOS 7默认安装的版本; Python3.6.8是CentOS 7中可以通过默认repo安装到的最新版本;如果简单使用,默认的python可能已经够用,但是如果使用python3版本的话,还是推荐升级到3的较高版本3.7/3…

2025 年 11 月高温轴承厂家权威推荐榜:耐高温轴承,真空高温轴承,窑炉高温轴承,BOPP链夹高温轴承,高温调心球轴承,高温关节轴承,高温滚针轴承,高温角接触轴承,高温圆柱滚子轴承公司推荐

2025 年 11 月高温轴承厂家权威推荐榜:耐高温轴承,真空高温轴承,窑炉高温轴承,BOPP链夹高温轴承,高温调心球轴承,高温关节轴承,高温滚针轴承,高温角接触轴承,高温圆柱滚子轴承公司推荐 在工业制造领域,高温轴…

2025 年 11 月不干胶轮转机厂家推荐排行榜,商标不干胶轮转机,高速轮转印刷设备,高效稳定生产解决方案

2025年11月不干胶轮转机厂家推荐排行榜:商标不干胶轮转机的高效稳定生产解决方案 在当今快速发展的包装印刷行业,不干胶轮转机作为商标标签生产的关键设备,其技术水平和性能稳定性直接影响着企业的生产效率和产品质…

swagger-typescript-api

最近用了一套第三方的若依框架做产品,技术栈是vue3+vite+TS,前端团队3个人,时间紧任务重,大家开发肯定不会太注重代码风格及质量,为了统一api的使用和类型的定义,引入了swagger-typescript-api来统一api和类型定…

HAL库DMA框架

介绍HAL库中外设如何与DMA建立连接外设句柄结构体中包含有DMA句柄,如ADC typedef struct __ADC_HandleTypeDef {// ADC_TypeDef *Instance; /*!< Register base address */// ADC_InitTy…