node-twstock
Version:
A client library for scraping Taiwan stock market data
83 lines (82 loc) • 3.88 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TdccScraper = void 0;
const _ = require("lodash");
const cheerio = require("cheerio");
const numeral = require("numeral");
const csvtojson = require("csvtojson");
const luxon_1 = require("luxon");
const scraper_1 = require("./scraper");
class TdccScraper extends scraper_1.Scraper {
async fetchStocksShareholders(options) {
const { date, symbol } = options;
const url = 'https://www.tdcc.com.tw/portal/zh/smWeb/qryStock';
const request = await this.httpService.get(url);
const $request = cheerio.load(request.data);
const token = $request('#SYNCHRONIZER_TOKEN').attr('value');
const uri = $request('#SYNCHRONIZER_URI').attr('value');
const method = $request('#method').attr('value');
const firDate = $request('#firDate').attr('value');
const scaDate = $request('#scaDate').find('option').map((_, el) => {
return $request(el).attr('value');
}).toArray();
const cookie = request.headers['set-cookie'];
const form = new URLSearchParams({
SYNCHRONIZER_TOKEN: token,
SYNCHRONIZER_URI: uri,
method,
firDate,
scaDate: luxon_1.DateTime.fromISO(date).toFormat('yyyyMMdd'),
sqlMethod: 'StockNo',
stockNo: symbol,
stockName: '',
});
const response = await this.httpService.post(url, form, { headers: { 'Cookie': cookie } });
const $response = cheerio.load(response.data);
const message = $response('.table tr').find('td').eq(0).text();
if (message === '查無此資料')
return null;
const data = {};
data.date = date;
data.symbol = symbol;
data.shareholders = $response('.table tr').slice(1).map((_, el) => {
const td = $response(el).find('td');
return {
level: numeral(td.eq(0).text()).value(),
holders: numeral(td.eq(2).text()).value(),
shares: numeral(td.eq(3).text()).value(),
proportion: numeral(td.eq(4).text()).value(),
};
}).toArray();
if (data.shareholders.length === 16) {
data.shareholders = [
...data.shareholders.slice(0, -1),
{ level: 16, holders: null, shares: 0, proportion: 0 },
Object.assign(Object.assign({}, data.shareholders[data.shareholders.length - 1]), { level: 17 })
];
}
return data;
}
async fetchStocksShareholdersRecentWeek(options) {
const url = 'https://smart.tdcc.com.tw/opendata/getOD.ashx?id=1-5';
const response = await this.httpService.get(url);
const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(response.data);
const [fields, ...rows] = json;
const distributions = rows.map(row => ({
date: luxon_1.DateTime.fromFormat(row[0], 'yyyyMMdd').toISODate(),
symbol: row[1],
level: numeral(row[2]).value(),
holders: row[2] === '16' ? null : numeral(row[3]).value(),
shares: row[2] === '16' ? numeral(row[4]).multiply(-1).value() : numeral(row[4]).value(),
proportion: row[2] === '16' ? numeral(row[5]).multiply(-1).value() : numeral(row[5]).value(),
}));
const data = _(distributions).groupBy('symbol')
.map(rows => {
const { date, symbol } = rows[0];
const shareholders = rows.map(row => _.omit(row, ['date', 'symbol']));
return { date, symbol, shareholders };
}).value();
return (options === null || options === void 0 ? void 0 : options.symbol) ? data.find(data => data.symbol === options.symbol) : data;
}
}
exports.TdccScraper = TdccScraper;