CoolFace
Apppublic

RanAI01/CHMA-API

sourceHugging Faceupdated 11mo agoView on Hugging Face
0likes
index.js77 linesDownload Raw Back to root
1const express = require('express');2const puppeteer = require('puppeteer');3 4const app = express();5const PORT = process.env.PORT || 7860;6 7app.get('/crawl', async (req, res) => {8    const url = 'https://goodinfo.tw/tw/StockList.asp?MARKET_CAT=%E6%99%BA%E6%85%A7%E9%81%B8%E8%82%A1&INDUSTRY_CAT=%E4%B8%89%E5%A4%A7%E6%B3%95%E4%BA%BA%E9%80%A3%E8%B2%B7+%E2%80%93+%E6%97%A5%40%40%E4%B8%89%E5%A4%A7%E6%B3%95%E4%BA%BA%E9%80%A3%E7%BA%8C%E8%B2%B7%E8%B6%85%40%40%E4%B8%89%E5%A4%A7%E6%B3%95%E4%BA%BA%E9%80%A3%E7%BA%8C%E8%B2%B7%E8%B6%85+%E2%80%93+%E6%97%A5&表=%E6%B3%95%E4%BA%BA%E8%B2%B7%E8%B3%A3%5F%E4%B8%89%E5%A4%A7&SHEET2=%E6%B3%95%E4%BA%BA%E9%80%A3%E8%B2%B7%E9%80%A3%E8%B3%A3%E7%B5%B1%E8%A8%88%28%E6%97%A5%29';9 10    let browser;11    try {12        browser = await puppeteer.launch({13            args: ['--no-sandbox', '--disable-setuid-sandbox'],14            headless: true,15        });16 17        const page = await browser.newPage();18        await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36');19        await page.goto(url, { waitUntil: 'load' });20 21        // 等待表格容器出現,延長等待時間至 30 秒22        await page.waitForSelector('#tblStockList', { timeout: 30000 });23 24        // 確保表格內有資料25        await page.waitForFunction(() => {26            const table = document.querySelector('#tblStockList');27            return table && table.querySelectorAll('tbody tr').length > 0;28        }, { timeout: 30000 });29 30        const data = await page.evaluate(() => {31            const rows = Array.from(document.querySelectorAll('#tblStockList tbody tr'));32            return rows.map(row => {33                const cells = row.querySelectorAll('td');34                const aTag = cells[0]?.querySelector('a');35                // 股票編號36                const stockId = aTag?.getAttribute('href')?.match(/STOCK_ID=(\w+)/)?.[1] || '';37                // 名稱(從標題屬性解析,取出「個股詳細資料」和「日線圖」)38                const title = aTag?.getAttribute('title') || '';39                const name = title40                    .replace(/^查看\s*\(\d+\)/, '')41                    .replace(/個股詳細資料$/, '')42                    .replace(/日線圖$/, '')43                    .trim();44 45                return {46                    stockId,47                    name,48                    date: cells[3]?.innerText.trim(),49                    foreignDays: cells[4]?.innerText.trim(),50                    foreignShares: cells[5]?.innerText.trim(),51                    trustDays: cells[8]?.innerText.trim(),52                    trustShares: cells[9]?.innerText.trim(),53                    dealerDays: cells[12]?.innerText.trim(),54                    dealerShares: cells[13]?.innerText.trim(),55                    totalDays: cells[16]?.innerText.trim(),56                    totalShares: cells[17]?.innerText.trim(),57                };58            });59        });60 61        await browser.close();62        res.json(data);63    } catch (err) {64        if (browser) {65            await browser.close();66        }67        res.status(500).json({ error: err.message, stack: err.stack });68    }69});70 71app.get('/', (req, res) => {72    res.send('爬蟲API相容中,請用 /crawl 取得資料');73});74 75app.listen(PORT, () => {76    console.log(`伺服器正在連接埠 ${PORT} 上執行`);77});