[Day 3] 取得台股资料(基本篇)

一、前言

想要进行资料分析,要做的第一件事当然是收集资料,所幸现在是2021,我们不需要为了股票资料请一堆工读生帮忙手动输入资料,这部分已经有一堆公司/政府部门替我们做了,我们只要利用网路爬虫,就可以把所有资料储存下来,至於合法性的问题可以参考这篇

抓取资料的部分估计会分成两天说明

毕竟我打这行时已经只剩40分就Day 4了

前半部分会着重於简单的基本资料,包括清单、成交纪录等基础资料,第二天会着重於三大法人、筹码等分析会用到的进阶资料,所有程序码皆存放於Colab上,建议阅读时实际测试一次,学习效果会更好。

二、股票代码

如果想要一支股票的相关资讯,我们会需要知道一个股票的股票代码,这个股票代码就像人类的身分证字号,一个股票只会有一个特定的代码,只要知道股票代码,我们就可以依此来查阅该股票的相关资料,完整的股票清单请点这
2330即是台积电专有代码

2330即是台积电专有代码

由於台股清单是一个很常用的资料,很多股市的资料分析都会包括在内,因此我们暂时还不需要使用爬虫,只需要从别人的资料撷取我们需要的部分就行了。

import pandas as pd
import requests

link = "https://quality.data.gov.tw/dq_download_json.php?nid=11549&md5_url=bb878d47ffbe7b83bfc1b41d0b24946e"

# Slice to Stock list
df = pd.DataFrame(requests.get(link).json())[["证券代号", "证券名称"]]
df = df.rename(columns={"证券代号": "STOCK_ID", "证券名称": "STOCK_NAME"})

df.to_csv("data/stock_id.csv", index=False, header=True)
df.head()

三、元大台湾50 (ETF50)

台股清单抓下来後,你可能会发现台湾的上市股票有上千种,如果真的要全部分析的话,复杂程度和麻烦程度都会直线的上升,因此我们先聚焦在足够好的股票,而直接参考元大台湾50这种指数型股票也省下我们初步筛选的时间,然而ETF股票构成是会变动的,手动输入资料不是不行,但如果要扩展到美股ETF这种动辄500种的股票,容易打错不说,每个月都要手动更新500支股票怎麽想都很反人类。

根据Google结果,我们找到了近期的ETF50成份股,由於他没提供CSV或API提供给我们下载/串接,所以我们必须使用爬虫程序把网页的图表,手动转换成Python的图表格式。

通过Chrome 开发者工具 F12可以清楚地得知我们要的区块在哪

通过Chrome 开发者工具 F12可以清楚地得知我们要的区块在哪

# Request html
retry_strategy = Retry(total=3)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)
http.mount("http://", adapter)
response = http.get("https://www.moneydj.com/ETF/X/Basic/Basic0007a.xdjhtm?etfid=0050.TW")

# Parser html
soup = BeautifulSoup(response.content, "html.parser")

df = pd.DataFrame()
row_index = 0

# Locate the table by find the sibling html tag which have id attribute
first_table = soup.find(id="ctl00_ctl00_MainContent_MainContent_sdate3").find_next_sibling()
stock_tag = first_table.find_all("td")
for i in range(0, len(stock_tag), 4):
    stock_name = stock_tag[i].text.strip()

    df.loc[row_index, "STOCK_NAME"] = stock_name
    df.loc[row_index, "持股(千股)"] = stock_tag[i + 1].text.strip()
    df.loc[row_index, "比例"] = stock_tag[i + 2].text.strip()
    df.loc[row_index, "增减"] = stock_tag[i + 3].text.strip()
    row_index += 1

stock_tag = first_table.find_next_sibling().find_all("td")

for i in range(0, len(stock_tag), 4):
    stock_name = stock_tag[i].text.strip()

    df.loc[row_index, "STOCK_NAME"] = stock_name
    df.loc[row_index, "持股(千股)"] = stock_tag[i + 1].text.strip()
    df.loc[row_index, "比例"] = stock_tag[i + 2].text.strip()
    df.loc[row_index, "增减"] = stock_tag[i + 3].text.strip()
    row_index += 1


# Combine with Stock ID
stock_df = pd.read_csv("data/stock_id.csv")
result_df = pd.merge(df, stock_df, how="left", on=["STOCK_NAME"])
result_df = result_df[["STOCK_ID", "STOCK_NAME", "持股(千股)", "比例", "增减"]]

四、成交纪录

拿到了ETF50的成份股清单,现在我们可以用它来提取个股的成交纪录,理论上政府是有提供历年的日/周成交纪录,然而由於未知原因,你可以发现该资料从6月开始就没再更新了。

还好现在我们有另一个选择,Yahoo股市有维护自己的股市资料库,甚至有专门的API提供串接,我们只需要提供股票代码即可查询历年成交纪录、报表、历年股利等资料。

import pandas as pd
import yfinance as yf

etf50_df = pd.read_csv("data/ETF50.csv")
etf50_id = etf50_df.loc[:, "STOCK_ID"].astype(str) + ".TW"
etf50_id = etf50_id.str.cat(sep=" ")

# Download etf50 recent 1 years data
df = yf.download(etf50_id, group_by="Ticker", period="1y", interval="1d")

# rotate Ticker axis and convert to (Date,Ticker) index
df = df.stack(level=0).rename_axis(["Date", "Ticker"]).reset_index(level=1)

# drop index
df = df.reset_index(level=0)
df = df.rename(columns={"Ticker": "STOCK_ID"})
df = df[["STOCK_ID", "Date", "Adj Close", "Close", "High", "Low", "Open", "Volume"]]

df.to_csv("data/ETF50_10years.csv", index=False, header=True)

六、预告

由於明天的爬虫程序会需要大量网路存取动作,建议使用Google Colab来测试以避免存取过多,而被服务器端封锁IP,如果对Google Colab还不熟的话麻烦参考这篇

我绝对不会说我一个晚上就被Ban了两次


<<:  #8 - Reading & Writing Files (fs)

>>:  Day08:资料结构 - 堆积(Heap)

【Day21】 Transformer 新手包 (一)

为什麽 Transformer ? 回顾 LSTM 与 CNN ,在一开始处理 time-seque...

D14 - 彭彭的课程# Python 函式参数详解:参数预设值、名称对应、任意长度参数(1)

今天也是一个爆炸累 天气颇好早上出门没那麽热了 秋天感觉终於要来了~~~ 今天就是来一个函式参数说明...

放弃实作 AES CBC 加密/解密

是的,如题 因为网路上找到的范例,几乎都是具备密码学知识基础才看得懂的 … 我完全无法使用 pyth...

方法的输入处理,其实不简单!

输入处理,功能实现,输出处理,异常处理。 铁人赛第三天, 今天在进入对於方法的『 输入处理 』前:...

Day30 - this&Object Prototypes Ch3 Objects - Review

Iteration forEach()、every()、some() 三者的差异在於:他们会对我们...