1. TWSE Stock Information

url <- "https://isin.twse.com.tw/isin/C_public.jsp?strMode=2"

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))]]

twse_stocks <- raw_twse[-c(1, 2), ] |>
  transmute(
    stock_id = str_extract(X1, "\\d{4,6}"),
    name = str_trim(str_remove(X1, "^\\d{4,6}\\s*")),
    listing_date = X3,
    industry = X5
  ) |>
  filter(
    !is.na(stock_id),
    !is.na(name),
    listing_date != "",
    industry != ""
  )

# Keep valid stock IDs and remove likely non-common securities
twse_stocks <- twse_stocks |>
  dplyr::filter(
    !grepl("ETF|基金|債券|特別股|認購權證|認售權證",
           name, ignore.case = TRUE),
    !grepl("ETF|基金|債券|特別股|認購權證|認售權證",
           industry, ignore.case = TRUE)
  ) |>
  dplyr::distinct(stock_id, .keep_all = TRUE)

head(twse_stocks)
## # 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_stocks)
## [1] 1084

2. FinMind Stock Information

finmind_url <- "https://api.finmindtrade.com/api/v4/data"

finmind_response <- GET(
  finmind_url,
  query = list(dataset = "TaiwanStockInfo")
)

stop_for_status(finmind_response)

finmind_json <- content(
  finmind_response,
  as = "parsed",
  type = "application/json"
)

finmind_stocks <- bind_rows(finmind_json$data)

finmind_stocks_clean <- finmind_stocks |>
  dplyr::filter(
    type == "twse",
    grepl("^\\d{4}$", stock_id),
    !grepl(
      "ETF|基金|債券|特別股|認購權證|認售權證|指數股票型|受益證券",
      industry_category,
      ignore.case = TRUE
    ),
    !grepl(
      "ETF|基金|債券|特別股|受益證券",
      stock_name,
      ignore.case = TRUE
    )
  ) |>
  dplyr::distinct(stock_id, .keep_all = TRUE)

finmind_final <- finmind_stocks_clean |>
  dplyr::select(
    stock_id,
    name = stock_name,
    industry = industry_category,
    type
  ) |>
  dplyr::left_join(
    twse_stocks |>
      dplyr::select(stock_id, listing_date),
    by = "stock_id"
  ) |>
  dplyr::select(stock_id, name, listing_date, industry)

head(finmind_final)
## # A tibble: 6 × 4
##   stock_id name   listing_date industry    
##   <chr>    <chr>  <chr>        <chr>       
## 1 2241     艾姆勒 2020/08/26   電機機械    
## 2 1443     立益   1990/04/21   紡織纖維    
## 3 3669     圓展   2011/08/25   光電業      
## 4 6165     浪凡   2003/08/04   電子工業    
## 5 2429     銘旺科 2000/09/11   電子零組件業
## 6 2459     敦吉   2001/09/17   電子通路業
nrow(finmind_final)
## [1] 1225