批量爬取 vjudge 题单的远程提交记录

// ==UserScript==
// @name         VJudge 题单批量归档
// @namespace    https://vjudge.net/
// @version      1.0
// @description  自动点击“提交-归档”,将题单题目加入归档队列(fetch实现)
// @match        https://vjudge.net/contest/*
// @match        https://vjudge.net/article/*
// @grant        none
// ==/UserScript==

(function(){
    'use strict';

    const INTERVAL = 5000; // 每题间隔(ms),别小于 3s
    const archived = new Set();

    function sleep(ms){
        return new Promise(r=>setTimeout(r,ms));
    }

    // 获取题号
    function getProblemIds(){
        const rows = document.querySelectorAll('table tbody tr');
        const ids = [];
        rows.forEach(r=>{
            const a = r.querySelector('a[href*="/problem/"]');
            if(!a)return;
            // 提取完整的 problemId 部分
            const href = a.getAttribute('href');
            const m = href.match(/problem\/(.+)$/);
            if(m) ids.push(m[1]);
        });
        return ids;
    }


    // 归档请求(用 fetch)
    async function archiveProblem(pid){
        try {
            const res = await fetch(`https://vjudge.net/problem/submit/${encodeURIComponent(pid)}`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                },
                body: 'method=2&language=&open=1&source=',
                credentials: 'include' // 自动带上 Cookie
            });
            const text = await res.text();
            console.log('已归档:', pid, text);
        } catch (err) {
            console.error('归档失败:', pid, err);
            throw err;
        }
    }

    async function run(){
        const pids = getProblemIds();
        console.log('题目数:', pids.length);

        for(let i=0;i<pids.length;i++){
            const pid = pids[i];
            if(archived.has(pid)) continue;

            console.log(`归档 ${i+1}/${pids.length}:`, pid);
            try{
                await archiveProblem(pid);
                archived.add(pid);
            }catch(e){
                console.warn('跳过:', pid);
            }
            await sleep(INTERVAL);
        }

        console.log('题单归档触发完成');
    }

    // 插入按钮
    const btn = document.createElement('button');
    btn.innerText = '批量归档题单';
    btn.style.position = 'fixed';
    btn.style.bottom = '10px';
    btn.style.right = '10px';
    btn.style.zIndex = 9999;
    btn.style.padding = '8px 12px';
    btn.style.backgroundColor = '#4CAF50';
    btn.style.color = '#fff';
    btn.style.border = 'none';
    btn.style.borderRadius = '4px';
    btn.style.cursor = 'pointer';

    btn.onclick = run;
    document.body.appendChild(btn);
})();
posted @ 2025-12-19 11:34  见合  阅读(9)  评论(0)    收藏  举报