幕客网课程抓取工具(导出CSV本地下载)

抓取课程信息并导出CSV,无需发送后端

// ==UserScript==
// @name         幕客网课程抓取工具(导出CSV本地下载)
// @namespace    http://tampermonkey.net/
// @version      2.51
// @description  抓取课程信息并导出CSV,无需发送后端
// @match        https://coding.imooc.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const allCourses = [];

    function showLoading() {
        const loading = document.createElement('div');
        loading.id = 'imooc-loading-overlay';
        loading.style.cssText = `
            position: fixed;
            top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0, 0, 0, 0.4);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9998;
            font-size: 20px;
            color: white;
            font-weight: bold;
        `;
        loading.innerHTML = '⏳ 正在抓取课程数据,请稍候...';
        document.body.appendChild(loading);
    }

    function hideLoading() {
        const loading = document.getElementById('imooc-loading-overlay');
        if (loading) document.body.removeChild(loading);
    }

    function getTotalPages() {
        const pageLinks = document.querySelectorAll('.page a[href*="page="]');
        const pages = Array.from(pageLinks)
            .map(a => {
                const match = a.href.match(/page=(\d+)/);
                return match ? parseInt(match[1]) : null;
            })
            .filter(n => n !== null);
        return pages.length ? Math.max(...pages) : 1;
    }

    function getBaseUrlAndParam() {
        const url = new URL(window.location.href);
        const base = url.origin + url.pathname;
        const searchParams = new URLSearchParams(url.search);
        searchParams.delete('page');
        const paramStr = searchParams.toString();
        return {
            baseUrl: base,
            paramPrefix: paramStr ? `${paramStr}&` : ''
        };
    }

    function parseHTML(htmlText) {
        const parser = new DOMParser();
        const doc = parser.parseFromString(htmlText, 'text/html');
        const cards = doc.querySelectorAll('li.course-card');
        const pageCourses = [];

        cards.forEach(card => {
            const name = card.getAttribute('data-name');
            const price = card.getAttribute('data-price') + '元';

            const numbersText = card.querySelector('p.one .numbers')?.innerText || '';
            const [level, applyRaw] = numbersText.split('·').map(s => s.trim());
            const apply = applyRaw?.replace('人报名', '') || '';

            const imgDiv = card.querySelector('.img');
            let image = '';
            if (imgDiv) {
                const style = imgDiv.getAttribute('style') || '';
                const match = style.match(/url\((.*?)\)/);
                if (match && match[1]) {
                    image = match[1].startsWith('//') ? 'https:' + match[1] : match[1];
                }
            }

            pageCourses.push({
                name,
                level,
                apply,
                price,
                image
            });
        });

        return pageCourses;
    }

    function exportCSV(dataArray, filename = 'imooc_courses.csv') {
        const headers = ['课程名称', '等级', '报名人数', '价格', '图片地址'];
        const rows = dataArray.map(d => [d.name, d.level, d.apply, d.price, d.image]);

        const csvContent = [headers, ...rows]
            .map(row => row.map(item => `"${String(item).replace(/"/g, '""')}"`).join(','))
            .join('\n');

        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = filename;
        link.click();
    }

    async function fetchAllPages() {
        allCourses.length = 0;
        showLoading();

        const totalPages = getTotalPages();
        const { baseUrl, paramPrefix } = getBaseUrlAndParam();

        for (let page = 1; page <= totalPages; page++) {
            const url = `${baseUrl}?${paramPrefix}page=${page}`;
            console.log(`📄 抓取第 ${page} 页:${url}`);

            try {
                const res = await fetch(url, { credentials: 'include' });
                const html = await res.text();
                const courses = parseHTML(html);
                allCourses.push(...courses);
            } catch (err) {
                console.error(`❌ 第 ${page} 页请求失败:`, err);
                break;
            }
        }

        console.log('✅ 抓取完成,共', allCourses.length, '条课程');
        hideLoading();

        if (allCourses.length > 0) {
            exportCSV(allCourses);
        } else {
            alert('未抓取到任何课程信息。');
        }
    }

    function addGrabButton() {
        const btn = document.createElement('button');
        btn.textContent = '📦 导出课程CSV';
        btn.style.cssText = `
            position: fixed;
            top: 100px;
            right: 20px;
            z-index: 9999;
            background-color: #28a745;
            color: white;
            padding: 10px 16px;
            font-size: 14px;
            border: none;
            border-radius: 6px;
            box-shadow: 0 0 6px rgba(0,0,0,0.2);
            cursor: pointer;
        `;
        btn.onclick = fetchAllPages;
        document.body.appendChild(btn);
    }

    window.addEventListener('load', () => {
        setTimeout(addGrabButton, 1000);
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址