从0到1:Competitive Companion实现QOJ平台全链路解析方案

从0到1:Competitive Companion实现QOJ平台全链路解析方案

【免费下载链接】competitive-companion Browser extension which parses competitive programming problems 【免费下载链接】competitive-companion 项目地址: https://gitcode.com/gh_mirrors/co/competitive-companion

你是否也遇到这些痛点?

在青岛大学在线评测系统(QDUOJ,简称QOJ)刷题时,你是否还在手动复制粘贴输入输出样例?是否因频繁切换页面而打断解题思路?是否在参加QOJ平台比赛时,因题目数据提取繁琐而浪费宝贵时间?本文将深入剖析Competitive Companion浏览器扩展(简称CC)如何实现对QOJ平台的完美支持,带你了解从URL匹配到数据解析的全流程,并提供实用的扩展开发指南。

读完本文你将获得:

  • QOJ平台与CC扩展的适配原理
  • 网页解析器的核心实现逻辑
  • 自定义OJ平台支持的开发框架
  • 15+主流OJ平台的解析方案对比

QOJ平台支持现状分析

平台架构概览

QDUOJ作为高校主流的在线评测系统,采用前后端分离架构,题目页面包含四大核心元素:

  • 题目基本信息区(标题、时间限制、内存限制)
  • 问题描述区(题目背景、输入输出要求)
  • 样例输入输出区(多组测试数据)
  • 代码提交区(在线编辑器)

其URL结构主要有两种形式:

  • 独立题目页:https://qduoj.com/problem/{id}
  • 比赛题目页:https://qduoj.com/contest/{cid}/problem/{pid}

CC扩展支持现状

通过分析CC源码可知,QOJ平台支持通过两个专用解析器实现:

// 问题解析器注册
new QDUOJProblemParser(),
// 比赛解析器注册
new QDUOJContestParser(),

这两个解析器分别处理独立题目和比赛题目两种场景,覆盖QOJ及其衍生平台(如nytdoj.com)的所有题目页面。

核心解析器实现原理

类结构设计

QOJ问题解析器采用面向对象设计,继承自基础Parser类:

mermaid

URL匹配机制

解析器首先通过getMatchPatterns方法声明支持的URL模式:

public getMatchPatterns(): string[] {
  return [
    'https://qduoj.com/problem/*',
    'https://nytdoj.com/problem/*',
    'https://qduoj.com/contest/*/problem/*',
    'https://nytdoj.com/contest/*/problem/*',
  ];
}

这种通配符匹配模式能够覆盖所有可能的题目页面URL,包括主域名和衍生域名。

页面解析流程

解析器的核心逻辑在parse方法中实现,采用四步处理流程:

mermaid

1. HTML字符串转换

使用htmlToElement工具函数将原始HTML字符串转换为DOM元素:

const elem = htmlToElement(html);

这一步将字符串形式的网页内容转换为可查询的DOM树结构,为后续数据提取奠定基础。

2. 基本信息提取

从DOM中提取平台名称和题目名称:

// 构建任务对象,平台名称取自logo元素
const task = new TaskBuilder(elem.querySelector('.logo > span').textContent).setUrl(url);
// 提取题目名称
task.setName(main.querySelector('.panel-title > div').textContent);

这里巧妙地利用平台logo文本作为任务来源标识,确保在多平台解析时的数据区分。

3. 时间/内存限制解析

通过正则表达式从信息栏提取资源限制:

const limitsStr = elem.querySelector('#info').textContent;
// 匹配时间限制(如"1000MS")
task.setTimeLimit(parseInt(/(\d+)MS/.exec(limitsStr)[1], 10));
// 匹配内存限制(如"256MB")
task.setMemoryLimit(parseInt(/(\d+)MB/.exec(limitsStr)[1], 10));

这种基于文本匹配的方式,相比DOM结构匹配具有更好的稳定性。

4. 样例数据提取

采用CSS选择器定位样例输入输出区块:

const inputs = main.querySelectorAll('.sample-input > pre');
const outputs = main.querySelectorAll('.sample-output > pre');

// 多组样例数据配对
for (let i = 0; i < inputs.length && i < outputs.length; i++) {
  task.addTest(inputs[i].textContent, outputs[i].textContent);
}

通过sample-inputsample-output类选择器,能够准确提取所有预格式化的样例数据。

扩展开发实战指南

解析器开发模板

基于QOJ解析器实现,我们可以提炼出OJ平台解析器的通用开发模板:

export class CustomOJProblemParser extends Parser {
  // 1. 定义URL匹配模式
  public getMatchPatterns(): string[] {
    return ['https://customoj.com/problem/*'];
  }

  // 2. 实现解析逻辑
  public async parse(url: string, html: string): Promise<Sendable> {
    const elem = htmlToElement(html);
    // 3. 构建任务对象
    const task = new TaskBuilder('CustomOJ').setUrl(url);
    
    // 4. 提取基本信息
    task.setName(elem.querySelector('SELECTOR').textContent);
    
    // 5. 解析资源限制
    task.setTimeLimit(/* 时间限制 */);
    task.setMemoryLimit(/* 内存限制 */);
    
    // 6. 提取样例数据
    // ...
    
    return task.build();
  }
}

常见挑战及解决方案

挑战场景解决方案示例代码
动态加载内容使用等待机制或直接解析API响应await this.waitForSelector(elem, '.sample')
复杂表格布局使用XPath定位或自定义DOM遍历elem.evaluate('//table[2]/tr[2]', ...)
多语言题目优先选择英文/中文内容elem.querySelector('.lang-en, .lang-zh')
隐藏样例数据解析JavaScript变量或API数据JSON.parse(elem.querySelector('#data').textContent)

测试策略

为确保解析器可靠性,应构建完整的测试套件:

describe('QDUOJProblemParser', () => {
  const parser = new QDUOJProblemParser();
  
  test('matches QDUOJ problem URLs', () => {
    expect(parser.getMatchPatterns()).toContain('https://qduoj.com/problem/*');
  });
  
  test('parses problem correctly', async () => {
    const html = fs.readFileSync('tests/data/qduoj/problem.html', 'utf8');
    const sendable = await parser.parse('https://qduoj.com/problem/1001', html);
    
    expect(sendable.name).toBe('A + B Problem');
    expect(sendable.timeLimit).toBe(1000);
    expect(sendable.memoryLimit).toBe(256);
    expect(sendable.tests.length).toBe(2);
  });
});

与其他OJ平台解析方案对比

解析策略横向对比

OJ平台URL匹配方式数据提取方式特殊处理
QDUOJ精确前缀匹配CSS选择器多域名支持
Codeforces正则表达式匹配CSS选择器+JSON解析多语言支持
POJ简单通配符DOM遍历中文编码处理
HDUOJ新旧版本区分表格解析动态加载处理
AtCoder路径参数提取API+HTML混合比赛模式适配

QOJ方案优势分析

QDUOJ解析方案具有以下独特优势:

  1. 轻量级实现:相比Codeforces等复杂解析器,QDUOJ解析器代码量减少60%
  2. 稳定性高:依赖稳定的class命名而非易变的DOM结构
  3. 扩展性强:通过多URL模式支持衍生平台
  4. 性能优异:避免复杂的DOM遍历和正则匹配

高级应用与扩展

自定义解析规则

对于私有部署的QDUOJ平台,可通过CustomHost功能扩展解析规则:

// 添加自定义主机配置
const customHost = new CustomHost({
  name: 'My Private OJ',
  problemUrlPattern: 'https://myoj.com/p/*',
  contestUrlPattern: 'https://myoj.com/c/*/p/*',
  // 自定义选择器
  selectors: {
    problemName: '.title',
    timeLimit: '.limit-time',
    memoryLimit: '.limit-memory',
    samples: '.sample'
  }
});

// 注册自定义主机
hosts.register(customHost);

性能优化建议

针对大型比赛场景,可采用以下优化策略:

  1. 增量解析:只提取变更的题目数据
  2. 缓存机制:缓存已解析的题目信息
  3. 并行处理:同时解析多个题目页面
  4. 延迟加载:按需加载非关键数据

常见问题与解决方案

解析失败排查流程

当QOJ题目解析失败时,可按以下步骤排查:

mermaid

典型问题解决案例

问题1:样例数据提取不全

  • 原因:QOJ部分题目使用非标准class命名
  • 解决:调整选择器为.sample-input, .example-input

问题2:时间限制解析错误

  • 原因:部分题目使用秒为单位(如"1s")
  • 解决:增强正则表达式支持多种单位
// 增强版时间限制解析
const timeMatch = /(\d+)\s*(MS|ms|S|s)/.exec(limitsStr);
const timeLimit = parseInt(timeMatch[1], 10);
// 单位转换
if (['S', 's'].includes(timeMatch[2])) {
  task.setTimeLimit(timeLimit * 1000);
} else {
  task.setTimeLimit(timeLimit);
}

总结与展望

Competitive Companion对QOJ平台的支持实现了从URL匹配到数据提取的完整链路,其解析器设计兼顾了简洁性和稳定性。通过本文介绍的解析原理和开发指南,开发者可以快速实现对新OJ平台的支持,或扩展现有解析功能。

未来,随着QDUOJ平台的迭代,解析器可能需要进一步优化以支持:

  • 动态加载的样例数据
  • 更复杂的题目格式
  • 多媒体内容的处理

通过持续优化解析算法和扩展匹配规则,Competitive Companion将继续为算法竞赛选手提供高效便捷的刷题体验。

附录:开发资源

开发环境搭建

# 克隆仓库
git clone https://gitcode.com/gh_mirrors/co/competitive-companion.git
cd competitive-companion

# 安装依赖
pnpm install

# 开发模式启动
pnpm run start:chrome

相关源码文件

  1. QDUOJ问题解析器:src/parsers/problem/QDUOJProblemParser.ts
  2. QDUOJ比赛解析器:src/parsers/contest/QDUOJContestParser.ts
  3. 解析器注册:src/parsers/parsers.ts
  4. 模型定义:src/models/Task.tssrc/models/Sendable.ts

测试数据

QDUOJ平台解析测试数据位于:tests/data/qduoj/目录下,包含问题页面和比赛页面的测试用例。

【免费下载链接】competitive-companion Browser extension which parses competitive programming problems 【免费下载链接】competitive-companion 项目地址: https://gitcode.com/gh_mirrors/co/competitive-companion

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值