承上一篇,公開申購公告的綱頁頁面如下
依照我們會需要的欄位,我們的 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 識別。
已經知道 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 出來。
結果,是完全沒有反應,所以你遇到了開發者的日常。「有 bug!」
分析:
是否為網址的問題? →貼到 chrome 上會跳出下載 csv 的視窗,所以網址正確
是否 alamofire 呼叫有問題? → 在 error 那邊沒有跳出,所以沒發生 error
第 31 行是否有 Data? → 使用 console print data,有 26651 bytes,而且和下載的 csv 檔比對 file size,不太可能是這邊有錯
第 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
成功了,如果把下載來源換成英文,就可以下載了。但和中文版資料相比,英文版資料只有 code,沒有名稱,所以還需要有一個 stock code vs. stock name 的表來查。如果是上市/上櫃增資,已經在市場上可以交易,所以在前面所提到的 上市/上櫃 公司基本資料表裡面有,如果真的想使用英文資料的話,可以使用 stock code 去前面的資料庫裡面反查 stock name。
但是,Swift String 真的只能吃 utf8 嗎? iPhone 是被設計出來賣到全世界的產品,如果不能轉換成每個地區的語言/曆法/習慣,那是賣不出去的。
而 String 的 init 被設計為可以輸入 encoding,所以,一定是有方法可以將 Big5 的 Data 轉換成 String。
下一篇會說,怎樣實作操作 Big5 的 String