D8 - 用 Swift 和公开资讯,打造投资理财的 Apps { 台股申购资讯实作.1 - 取得公开申购公告csv档 }

承上一篇,公开申购公告的纲页页面如下

https://ithelp.ithome.com.tw/upload/images/20210916/20140622u3rX6SsvoV.png

依照我们会需要的栏位,我们的 model 如下

//
//  StockSubscriptionInfo.swift
//  ITIronMan
//
//  Created by Marvin on 2021/9/4.
//

import Foundation

/// 股票申购最小单位的 data model
struct StockSubscriptionInfo {
    
    let stockCode: String
    let stockName: String
    let subscriptionStartString: String
    let subscriptionEndString: String
    let subscriptionOccurString: String
    
    /// 承销价
    let price: String
    
    /// 实际承销价,不确定为什麽会有这个栏位,但看起来价格和承销价一样
    let actualPrice: String
    
    let stockDeliveringDateString: String
    
    let stockCountString: String
    
    /// 总合格件数,所有被扣款成功的数量
    let totalApplyCountString: String
    
    /// 中签率
    let subscriptionRateString: String
}

如果你用的是 chrome,当滑鼠移到下载 csv 的位置的时候,你会看到下载的网址呈现出来,这个位置就是放 csv 档案的地方。而後面的 yy=2021,就可以知道,他是依照年份为单位,来进行查询的。

如果你换成 2020,那就会是 2020 年的所有资料。

csv 下载位置: https://www.twse.com.tw/announcement/publicForm?response=csv&yy=2021

依照前面的方式,再写一个 StockSubscriptionManager 专门处理申购的资料。

//
//  StockSubscriptionManager.swift
//  ITIronMan
//
//  Created by Marvin on 2021/9/4.
//

import Foundation

/// 这个类别专门处理股票申购资讯
class StockSubscriptionManager {
    
    private lazy var alamofireAdapter: AlamofireAdapter = {
        return AlamofireAdapter()
    }()
    
    func requestStockSubscriptionInfo(year: Int, completion: @escaping (([StockSubscriptionInfo], Error?) -> Void)) {
        
        // https://www.twse.com.tw/announcement/publicForm?response=csv&yy=2021
        let urlString = "https://www.twse.com.tw/announcement/publicForm?response=csv&yy=\(year)"
        
        alamofireAdapter.requestForString(urlString, method: .get) { result in
            
            switch result {
            case .success(let string):
                print("you got string: \(string)")
            case .failure(let error):
                print("error: \(error.localizedDescription)")
            }
        }

    }
}

在测试的时候,我们先随便找一个按钮来发动 StockSubscriptionManager 的下载,看会不会有 header 或是资料跑出来

不过,照着这一份程序码,这一个 func 发动的时候,console 上面会印出一堆,你好像看得懂,又好像看不懂的东西。

在印出来的结果上,你看得懂的是那些数字,所以先看这些可被辨识的文字代表什麽。110/09/14 应该是日期,3533 是股票代号,110/09/08 也是日期,後面有个 432。比对这些数字,他应该就是前述截图中的 [嘉泽],只是编码和预设的不同,所以无法被 String 识别。

https://ithelp.ithome.com.tw/upload/images/20210917/20140622lqCOCx9elZ.png

已经知道 requestForString 会出编码的问题,至少确定 data 的 request 是有的,所以要改用 AlamofireAdapter 中的 request,并传出 (Data?, URLResponse?, Error?) 让这个 manager 进行 String parsing。

// https://www.twse.com.tw/announcement/publicForm?response=csv&yy=2021
        let urlString = "https://www.twse.com.tw/announcement/publicForm?response=csv&yy=\(year)"
        
        alamofireAdapter.request(urlString, method: .get) { data, response, error in
            
            if let error = error {
                completion([], error)
                return
            }
            
            var subscriptionList = [StockSubscriptionInfo]()
            
            if let data = data,
               let string = String(data: data, encoding: .utf8),
               let csv = try? CSVAdapter(rawString: string) {
                
                print(csv.header)
                print(csv.namedRows[10])
                
            }
            
            completion(subscriptionList, error)
        }

然後再试着找个测试按钮,看一下 csv 能不能被 print 出来。

https://ithelp.ithome.com.tw/upload/images/20210918/20140622mwdJCjt62C.png

结果,是完全没有反应,所以你遇到了开发者的日常。「有 bug!」

分析:

  • 是否为网址的问题? →贴到 chrome 上会跳出下载 csv 的视窗,所以网址正确

  • 是否 alamofire 呼叫有问题? → 在 error 那边没有跳出,所以没发生 error

  • 第 31 行是否有 Data? → 使用 console print data,有 26651 bytes,而且和下载的 csv 档比对 file size,不太可能是这边有错
    https://ithelp.ithome.com.tw/upload/images/20210918/20140622VjO8qoC3sg.png

  • 第 32 行後,就跳离 if let,没有往 33 行走,可以确定的是 String(data: data, encoding: .utf8) 这边, string 是 optional,所以程序跳出。

也就是…这个 String 不是 UTF-8 的编码…

那他还可能是什麽编码呢? 如果档案是繁中的话,那应该就是 Big5

Big5 简介如下

https://zh.wikipedia.org/wiki/大五码

先找找看,有没有其他 utf-8 编码的来源?

在公开申购的右上方,有个 [English] 标签,点下去後,页面就变成全部英文了。然後滑鼠移到 csv 那一栏,连结变成下方这样,可以看到,中间多了个 /en/,下载後的档案,里面的内容全部是英文。来试试看换成英文版的 csv 档,能不能拿到我们要的资讯。

https://www.twse.com.tw/en/announcement/publicForm?response=csv&yy=2021

https://ithelp.ithome.com.tw/upload/images/20210918/20140622FWkV2ViPlp.png

成功了,如果把下载来源换成英文,就可以下载了。但和中文版资料相比,英文版资料只有 code,没有名称,所以还需要有一个 stock code vs. stock name 的表来查。如果是上市/上柜增资,已经在市场上可以交易,所以在前面所提到的 上市/上柜 公司基本资料表里面有,如果真的想使用英文资料的话,可以使用 stock code 去前面的资料库里面反查 stock name。

https://ithelp.ithome.com.tw/upload/images/20210918/20140622lcOVpwMrMn.png

但是,Swift String 真的只能吃 utf8 吗? iPhone 是被设计出来卖到全世界的产品,如果不能转换成每个地区的语言/历法/习惯,那是卖不出去的。

而 String 的 init 被设计为可以输入 encoding,所以,一定是有方法可以将 Big5 的 Data 转换成 String。

下一篇会说,怎样实作操作 Big5 的 String


<<:  2. 工程师不只是工程师

>>:  Day 04. Zabbix 可监控的服务、设备、应用

[ Day 27 ] - 样板字面值(Template literals)

说明 在先前的版本中被称为样板字串(template strings) 早期在组字串资料时会用大量的...

跨网域传值的神队友——window.postMessage

最近公司的EIP专案有个需求。主管在签核一览表里会点击要签核的单子另开一个视窗,需求单位希望主管签完...

使用 Breeze 建立基础专案框架

Breeze 是官方推荐的起手套装,内建有登入、注册、忘记密码等常用的用户功能,令外可以选择使用 V...

第15章:管理与设定网路介绍(二)

前言 本章节,是要讲述如何查看网路设定与设定在主机上的网路资讯。 识别与取得网路介面资讯 在一台主机...

入门魔法 - 常用阵列方法(二)find、findIndex

前情提要 艾草:「不知不觉也累积了不少魔力总量了,我们今天透过魔法阵列来找出你适合的属性值吧!」 「...