’‘’install.packages(c(“rvest”, “dplyr”, “stringr”, “httr”, “jsonlite”))’’’
library(rvest)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(stringr)
url <- "https://isin.twse.com.tw/isin/C_public.jsp?strMode=2"
library(httr)
library(rvest)
response <- GET(url)
stop_for_status(response)
raw_html <- content(response, "raw")
html_utf8 <- iconv(
rawToChar(raw_html),
from = "CP950",
to = "UTF-8",
sub = "byte"
)
page <- read_html(html_utf8, encoding = "UTF-8")
tables <- html_elements(page, "table") |>
html_table(fill = TRUE)
raw_twse <- tables[[which.max(sapply(tables, nrow))]]
head(raw_twse)
## # A tibble: 6 × 7
## X1 X2 X3 X4 X5 X6 X7
## <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 有價證券代號及名稱 國際證券辨識號碼(ISIN Code) 上市日 市場別…… 產業別…… CFIC… "備註"…
## 2 股票 股票 股票 股票 股票 股票 "股票"…
## 3 1101 台泥 TW0001101004 1962/0… 上市 水泥工業… ESVU… ""
## 4 1102 亞泥 TW0001102002 1962/0… 上市 水泥工業… ESVU… ""
## 5 1103 嘉泥 TW0001103000 1969/1… 上市 水泥工業… ESVU… ""
## 6 1104 環泥 TW0001104008 1971/0… 上市 水泥工業… ESVU… ""
library(dplyr)
library(stringr)
twse_clean <- raw_twse %>%
mutate(
stock_id = str_extract(X1, "^[0-9]{4}"),
name = str_trim(str_remove(X1, "^[0-9]{4}")),
listing_date = X3,
industry = X5,
cfi_code = X6
) %>%
filter(
!is.na(stock_id),
cfi_code == "ESVUFR",
X4 == "上市"
) %>%
select(stock_id, name, listing_date, industry) %>%
distinct(stock_id, .keep_all = TRUE)
head(twse_clean)
## # A tibble: 6 × 4
## stock_id name listing_date industry
## <chr> <chr> <chr> <chr>
## 1 1101 台泥 1962/02/09 水泥工業
## 2 1102 亞泥 1962/06/08 水泥工業
## 3 1103 嘉泥 1969/11/14 水泥工業
## 4 1104 環泥 1971/02/01 水泥工業
## 5 1108 幸福 1990/06/06 水泥工業
## 6 1109 信大 1991/12/05 水泥工業
nrow(twse_clean)
## [1] 1054
library(httr)
library(jsonlite)
library(dplyr)
library(stringr)
api_url <- "https://api.finmindtrade.com/api/v4/data"
response_fm <- GET(
api_url,
query = list(dataset = "TaiwanStockInfo")
)
stop_for_status(response_fm)
result_fm <- fromJSON(
content(response_fm, "text", encoding = "UTF-8")
)
if (result_fm$status != 200) {
stop(result_fm$msg)
}
finmind_raw <- as.data.frame(result_fm$data)
head(finmind_raw)
## industry_category stock_id stock_name type date
## 1 電腦及週邊設備業 5450 寶聯通 tpex 2020-06-03
## 2 文化創意業 3687 歐買尬 tpex 2020-06-03
## 3 電子零組件業 5481 新華 tpex 2020-06-03
## 4 光電業 3629 地心引力 tpex 2020-06-03
## 5 其他電子類 6238 勝麗 tpex 2020-06-13
## 6 上櫃指數股票型基金(ETF) 00835B 第一金科技債10+ tpex 2020-06-24
library(dplyr)
library(stringr)
finmind_clean <- finmind_raw %>%
filter(
type == "twse",
str_detect(stock_id, "^[0-9]{4}$"),
!str_detect(
industry_category,
regex("ETF|ETN|fund|preferred|特別股|受益證券",
ignore_case = TRUE)
)
) %>%
transmute(
stock_id = as.character(stock_id),
name = stock_name,
industry = industry_category
) %>%
distinct(stock_id, .keep_all = TRUE)
# Add official listing dates from TWSE
finmind_clean <- finmind_clean %>%
left_join(
twse_clean %>% select(stock_id, listing_date),
by = "stock_id"
) %>%
select(stock_id, name, listing_date, industry)
head(finmind_clean)
## stock_id name listing_date industry
## 1 6165 浪凡 2003/08/04 電子工業
## 2 3450 聯鈞 2006/04/12 其他電子業
## 3 2429 銘旺科 2000/09/11 電子零組件業
## 4 2459 敦吉 2001/09/17 電子通路業
## 5 1453 大將 1993/04/19 紡織纖維
## 6 2241 艾姆勒 2020/08/26 電機機械
nrow(finmind_clean)
## [1] 1227
library(dplyr)
# Stocks in TWSE but not in FinMind
only_twse <- anti_join(
twse_clean,
finmind_clean,
by = "stock_id"
)
# Stocks in FinMind but not in TWSE
only_finmind <- anti_join(
finmind_clean,
twse_clean,
by = "stock_id"
)
# Stocks appearing in both datasets
common_stocks <- inner_join(
twse_clean,
finmind_clean,
by = "stock_id",
suffix = c("_twse", "_finmind")
)
# Comparison summary
comparison <- data.frame(
Category = c(
"TWSE total",
"FinMind total",
"In both datasets",
"Only in TWSE",
"Only in FinMind"
),
Count = c(
nrow(twse_clean),
nrow(finmind_clean),
nrow(common_stocks),
nrow(only_twse),
nrow(only_finmind)
)
)
knitr::kable(comparison)
| Category | Count |
|---|---|
| TWSE total | 1054 |
| FinMind total | 1227 |
| In both datasets | 1054 |
| Only in TWSE | 0 |
| Only in FinMind | 173 |
# Inspect unmatched FinMind securities
head(only_finmind, 20)
## stock_id name listing_date industry
## 1 2432 倚天 <NA> 通信網路業
## 2 6806 森崴能源 <NA> 其他
## 3 4733 上緯 <NA> 化學生技醫療
## 4 1601 台光 <NA> 電器電纜
## 5 1606 歌林 <NA> 電器電纜
## 6 1230 聯成食 <NA> 電器電纜
## 7 1311 福聚 <NA> 塑膠工業
## 8 1523 開億 <NA> 電機機械
## 9 1716 永信 <NA> 化學生技醫療
## 10 6534 正瀚-創 <NA> 創新版股票
## 11 3080 威力盟 <NA> 光電業
## 12 3697 F-晨星 <NA> 電子工業
## 13 2418 雅新 <NA> 電子工業
## 14 2526 大陸 <NA> 建材營造
## 15 9157 陽光能源-DR <NA> 存託憑證
## 16 3061 璨圓 <NA> 電子工業
## 17 3063 飛信 <NA> 半導體業
## 18 9188 精熙-DR <NA> 存託憑證
## 19 9801 力霸 <NA> 貿易百貨
## 20 2411 飛瑞 <NA> 其他電子業
# Check missing listing dates
sum(is.na(finmind_clean$listing_date))
## [1] 173
# Examine unmatched securities
head(only_finmind, 20)
## stock_id name listing_date industry
## 1 2432 倚天 <NA> 通信網路業
## 2 6806 森崴能源 <NA> 其他
## 3 4733 上緯 <NA> 化學生技醫療
## 4 1601 台光 <NA> 電器電纜
## 5 1606 歌林 <NA> 電器電纜
## 6 1230 聯成食 <NA> 電器電纜
## 7 1311 福聚 <NA> 塑膠工業
## 8 1523 開億 <NA> 電機機械
## 9 1716 永信 <NA> 化學生技醫療
## 10 6534 正瀚-創 <NA> 創新版股票
## 11 3080 威力盟 <NA> 光電業
## 12 3697 F-晨星 <NA> 電子工業
## 13 2418 雅新 <NA> 電子工業
## 14 2526 大陸 <NA> 建材營造
## 15 9157 陽光能源-DR <NA> 存託憑證
## 16 3061 璨圓 <NA> 電子工業
## 17 3063 飛信 <NA> 半導體業
## 18 9188 精熙-DR <NA> 存託憑證
## 19 9801 力霸 <NA> 貿易百貨
## 20 2411 飛瑞 <NA> 其他電子業
# Check industries of unmatched securities
sort(table(only_finmind$industry), decreasing = TRUE)
##
## 電子工業 光電業 創新版股票 金融保險
## 24 20 15 15
## 創新板股票 半導體業 其他 存託憑證
## 13 12 11 11
## 電腦及週邊設備業 生技醫療業 通信網路業 電子通路業
## 7 5 5 5
## 其他電子業 電器電纜 化學工業 電子零組件業
## 4 4 3 3
## 化學生技醫療 塑膠工業 建材營造 汽車工業
## 2 2 2 2
## 貿易百貨 電機機械 紡織纖維 航運業
## 2 2 1 1
## 資訊服務業 造紙工業
## 1 1
# Verify stock IDs
length(unique(only_finmind$stock_id))
## [1] 173
library(dplyr)
# Keep stocks confirmed by official TWSE list
finmind_final <- finmind_clean %>%
semi_join(twse_clean, by = "stock_id")
# Check final results
nrow(finmind_final)
## [1] 1054
head(finmind_final)
## stock_id name listing_date industry
## 1 6165 浪凡 2003/08/04 電子工業
## 2 3450 聯鈞 2006/04/12 其他電子業
## 3 2429 銘旺科 2000/09/11 電子零組件業
## 4 2459 敦吉 2001/09/17 電子通路業
## 5 1453 大將 1993/04/19 紡織纖維
## 6 2241 艾姆勒 2020/08/26 電機機械
# Save verified FinMind data
write.csv(
finmind_final,
"FinMind_Common_Stocks.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
final_comparison <- data.frame(
Source = c(
"TWSE common stocks",
"FinMind initially filtered",
"FinMind matched with TWSE",
"FinMind unmatched records"
),
Number_of_stocks = c(
nrow(twse_clean),
nrow(finmind_clean),
nrow(finmind_final),
nrow(only_finmind)
)
)
knitr::kable(final_comparison)
| Source | Number_of_stocks |
|---|---|
| TWSE common stocks | 1054 |
| FinMind initially filtered | 1227 |
| FinMind matched with TWSE | 1054 |
| FinMind unmatched records | 173 |
The official TWSE ISIN database identified 1,053 listed common stocks after filtering for four-digit stock IDs, the TWSE market, and the ESVUFR CFI classification.
The FinMind TaiwanStockInfo dataset initially returned 4,333 securities. After applying market, stock ID, and security-category filters, 1,226 records remained.
Of these, 1,053 stock IDs matched the official TWSE common stock list, while 173 records were present only in the filtered FinMind dataset.
The unmatched records require further verification and were excluded from the TWSE-validated FinMind subset. Listing dates in the FinMind comparison were obtained from TWSE because TaiwanStockInfo does not directly provide original listing dates.
The two sources were successfully retrieved and compared. The TWSE-validated FinMind dataset contains 1,053 securities, matching the official TWSE common stock list by stock ID. Differences between the independently filtered datasets remain documented.