OnlineJudge 离线题库采集

过段时间要把以前的OJ换掉,我负责VirtualJudge的部分。需要用C与PHP写一个Linux下的VJudge。

在此之前,将以前写给自己学弟学妹用的OJ离线题库的采集程序改进了一下。支持国内一些知名高校的OJ,为之后VJudge的开发练练手,熟悉下各个OJ的结构,免去以后再在LINUX上进行一些繁琐的测试。

题目的采集没有使用任何OJ的API,直接采取从HTML页面采集数据并处理的方式。下载HTTP文件使用的是WinINet函数集,用起来比CURL还方便。正则表达式使用的ATL库里的regex。题目的储存使用的文件进行储存。别看一个OJ就有几千道题,文件结构访问的速度却是相当地快。因为抓取出来的题目有大量的HTML标签,先要全部去掉非常地困难(一些题目有不同的格式与链接标签,甚至有一些题目是关于标记语言的题目)。因为我现在做的是离线的题库,于是将这些标签保留写来,利用HTML的方式显示我采集的题目。

目前做了六个OJ:

1、bnu(北京师范大学OJ):这个OJ我觉得是国内用户体验做得最好的一个OJ,自己OJ的题目很丰富(含有较多不错的中文),VOJ的功能也完美地融合在自己的OJ中。

2、hdu(杭州电子科技大学OJ):拥有国内最强判题机群,国内各大算法竞赛都选在杭电OJ举行。

3、neu(成都东软学院OJ):很多人可能都不知道这OJ,也是国内少数登录需要验证码的奇葩OJ,以至于VOJ想支持它都困难,简单题爆多,欢迎来撸。我母校嘛,必须有,虽然我亚洲区牌都没拿到个,学校最高也就个铜。补习班性质的比赛嘛,二本学校的学生都不想把耍的时间花在觉得苦逼的事情上,我能拿着塑料坚持那么久也是NB了。该OJ即将在今年更换新的系统,判题内核、数据管理、界面、以及VOJ都由我们的老成员完成。希望以后学校的骚年能取得好成绩--wchrt。

4、poj(北京大学OJ):搞ICPC的骚年都知道,国内最为知名的OJ之一。

5、vijos(高效信息学在线评测系OJ):里面全是中文题哦,小心有大量的小学生,分分钟虐爆你的小学生。

6、zoj(浙江大学OJ):国内起步最早的OJ之一,但我基本没怎么用过这个OJ。

下面根据各oj分别列出了获取题目内容与标题的正则表达式:

        ojnum=0;
	cbox->AddString("bnu");
	ojurl[ojnum]="http://www.bnuoj.com/bnuoj/problem_show.php?pid=";
	ojgetstr[ojnum].allcontent="{<div id=\"showproblem\">(.|\n)*?<div id=\"one_content_base\">}";
	ojgetstr[ojnum].title="<div id=\"showproblem\.*?<h1.*?>{.*?}</h1>";
	ojgetstr[ojnum].iscode=true;
	ojgetstr[ojnum].getnum=-1;
	ojgetstr[ojnum].pnostart=1000;
	ojnum++;

	cbox->AddString("hdu");
	ojurl[ojnum]="http://acm.hdu.edu.cn/showproblem.php?pid=";
	ojgetstr[ojnum].allcontent="{<h1(.|\n)*?Note</a>}";
	ojgetstr[ojnum].title="<h1.*?>{.*?}</h1>";
	ojgetstr[ojnum].iscode=false;
	ojgetstr[ojnum].getnum=-1;
	ojgetstr[ojnum].pnostart=1000;
	ojnum++;

	cbox->AddString("neu");
	ojurl[ojnum]="http://acm.nsu.edu.cn/JudgeOnline/problem.php?id=";
	ojgetstr[ojnum].allcontent="{<div id=main(.|\n)*?Sample Output(.|\n)*?BBS(.|\n)*?</a>}";
	ojgetstr[ojnum].title="<title.*?>{.*?}</title>";
	//目前不需要把内容信息分开,就不正则详细的题目内容
	/*ojgetstr[ojnum].tim="Time Limit:{.*?}&nbsp";
	ojgetstr[ojnum].mem="Memory Limit:{.*?}<br>";
	ojgetstr[ojnum].des=">Description</h2>.*?<div.*?>{.*?}</div>";
	ojgetstr[ojnum].input=">Input</h2>.*?<div.*?>{.*?</div>.*?}</div>";
	ojgetstr[ojnum].output=">Output</h2>.*?<div.*?>{.*?}</div>";
	ojgetstr[ojnum].sinput=">Sample Input</h2>.*?{<pre>(.|\n)*?</pre>}";
	ojgetstr[ojnum].soutput=">Sample Output</h2>.*?{<pre>(.|\n)*?</pre>}";*/
	ojgetstr[ojnum].iscode=true;
	ojgetstr[ojnum].getnum=-1;
	ojgetstr[ojnum].pnostart=1000;
	ojnum++;

	cbox->AddString("poj");
	ojurl[ojnum]="http://poj.org/problem?id=";
	ojgetstr[ojnum].allcontent="{<div class=\"ptt\"(.|\n)*?Discuss</a>}";
	ojgetstr[ojnum].title="<div class=\"ptt\".*?>{.*?}</div>";
	ojgetstr[ojnum].iscode=true;
	ojgetstr[ojnum].getnum=49;//poj最多只允许短时间访问49道题
	ojgetstr[ojnum].pnostart=1000;
	ojnum++;

	cbox->AddString("vijos");
	ojurl[ojnum]="https://vijos.org/p/";
	ojgetstr[ojnum].allcontent="{<div class=\"pcontent\">(.|\n)*}<h4";
	ojgetstr[ojnum].title="<div class=\"content\">.*?</span>{.*?}<div";
	ojgetstr[ojnum].iscode=true;
	ojgetstr[ojnum].getnum=-1;
	ojgetstr[ojnum].pnostart=1000;
	ojnum++;

	cbox->AddString("zoj");
	ojurl[ojnum]="http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=";
	ojgetstr[ojnum].allcontent="{<div id=\"content_body\">(.|\n)*</div>}";
	ojgetstr[ojnum].title="<div id=\"content_body\">.*?>.*?>{.*?}</span>";
	ojgetstr[ojnum].iscode=false;
	ojgetstr[ojnum].getnum=-1;
	ojgetstr[ojnum].pnostart=1000;
	ojnum++;

因为不同的OJ网站所使用的编码格式不同,我使用的是ansi,所以要把一些使用utf-8的网页转换为ansi,下面是转换的代码:

static void  UTF8toANSI(CString &strUTF8)
{
	UINT nLen = MultiByteToWideChar(CP_UTF8,NULL,strUTF8,-1,NULL,NULL);
	WCHAR *wszBuffer = new WCHAR[nLen+1];
	nLen = MultiByteToWideChar(CP_UTF8,NULL,strUTF8,-1,wszBuffer,nLen);
	wszBuffer[nLen] = 0;
	nLen = WideCharToMultiByte(936,NULL,wszBuffer,-1,NULL,NULL,NULL,NULL);
	CHAR *szBuffer = new CHAR[nLen+1];
	nLen = WideCharToMultiByte(936,NULL,wszBuffer,-1,szBuffer,nLen,NULL,NULL);
	szBuffer[nLen] = 0;

	strUTF8 = szBuffer;
	delete []szBuffer;
	delete []wszBuffer;
}

使用WinINet函数集的CInternetSession与CHttpFile下载HTTP文件,那hdu作为例子

hdu的1001题的地址是:"http://acm.hdu.edu.cn/showproblem.php?pid=1001"

这里的http://acm.hdu.edu.cn/showproblem.php是hdu的题目PHP文件,我们需要以GET的方式请求pid=1001的数据。对于hdu的其他题目,我们只需把pid的值设置成其他的值即可。

        //获取GET方式获取题目的HTML页面
        CInternetSession intsess;
	CHttpFile *phtfile = NULL;
	phtfile = (CHttpFile *)intsess.OpenURL(url);
	UINT nfilelen = (UINT) phtfile->GetLength();
	CString strhtml;
	CString buffer;
	UINT dw=0;
	while(dw<nfilelen)//将数据转入strhtml处理
	{
		dw+=phtfile->ReadString(buffer);
		strhtml+=buffer;
		dw++;
	}

	//使用正则表达式提取数需要的内容
	data->all=strhtml;
	getcontent(ojgetstr->allcontent,strhtml,data->allcontent);//去除题目页面的多与信息
	getcontent(ojgetstr->title,strhtml,data->title);
	/*getcontent(ojgetstr->tim,strhtml,data->tim);
	getcontent(ojgetstr->mem,strhtml,data->mem);
	getcontent(ojgetstr->des,strhtml,data->des);
	getcontent(ojgetstr->input,strhtml,data->input);
	getcontent(ojgetstr->output,strhtml,data->output);
	getcontent(ojgetstr->sinput,strhtml,data->sinput);
	getcontent(ojgetstr->soutput,strhtml,data->soutput);*/

	if(ojgetstr->iscode)
	{
		UTF8toANSI(data->allcontent);
		UTF8toANSI(data->title);
		/*UTF8toANSI(data->tim);
		UTF8toANSI(data->mem);
		UTF8toANSI(data->des);
		UTF8toANSI(data->input);
		UTF8toANSI(data->output);
		UTF8toANSI(data->sinput);
		UTF8toANSI(data->soutput);*/
	}

	if(data->allcontent.GetLength()<5||data->title.GetLength()<1)
	{
		return 0;
	}
	//将处理后的数据写入到文件中
	CFile file;
	if(!file.Open("c:\\ojdata\\"+data->oj+"\\"+data->id,CFile::modeWrite))
	{
		if(!file.Open("c:\\ojdata\\"+data->oj+"\\"+data->id,CFile::modeCreate|CFile::modeWrite))
		{
			return 0;
		}
	}
	CArchive ar(&file,CArchive::store);
	ar<<data;
	ar.Close();
	file.Close();

因为每个OJ的题目都有几千道,不可能每次打开软件都去遍历那么几千个文件,效率太低而且容易出错。因此我将每个题目的ID和标题都提取出来,放到一个文件中记录,用于题目的引索。

引索的结构是:题目数量、id1、标题1、id2、标题2、id3...的格式,每个OJ一个表。

下面是储存记录的代码,使用序列化储存:

        CFile file;
	if(!file.Open("c:\\ojdata\\"+oj+"\\pinfo",CFile::modeWrite))
	{
		if(!file.Open("c:\\ojdata\\"+oj+"\\pinfo",CFile::modeCreate|CFile::modeWrite))
		{
			AfxMessageBox("写入出错");
			return 1;
		}
	}
	CArchive ar(&file,CArchive::store);
	ar<<that->infonum;
	for(int i=0;i<that->infonum;i++)
	{
		ar<<(&that->problemarr[i]);
	}
	ar.Close();
	file.Close();

	//统计题目下载成功与失败
	str.Format("SECC:%d  FAIL:%d",that->infonum,totalnum-50-that->infonum);

hdu的:

全是中文题目的OJ,不错不错:

水水更健康:

基本上题目的采集工作到此结束。以后做VOJ的时候再将各个部分正则出来。

因为网络环境的因素,OJ题目的访问速度不一定很快,这里可以用开多个线程进行下载,以及优化题目的下载和处理,因为我可以直接挂在我的服务器上下载,所以就一条线程下载处理完了。我用的电信50M的宽带(坑爆,说是50M,上行只有不到2M,电信太煎饼了,一个月169还不包含短信费,80端口封完喊还推荐我开3000一个月的商务宽带)大概下载一个OJ的所有题目10多分钟,我直接6个OJ一起下载的。不想等待的朋友可以直接在附件下载我采集好的题目包,把它解压到到C盘即可。

采集到了题目,怎么看呢?肯定不能直接给人看,一堆标记语言烦死了。WINDOWS自带了浏览器控件,直接使用它即可,我使用了两种方式,一种是对话框上的HTML控件,可能是我的原因但是这个控件不稳定, 在一些电脑上老加载出错。

于是我另外用了CDHtmlDialog。这直接是个HTML的对话框,每次把题目的本地地址给他后让其Navigate即可。由于我题目是储存的一个problemdata结构体,含有一些其他的信息。打开题目前需要把要显示的题目提取出来,重新生成一个HTML文件,然后再让HTML对话框打开它。

题目的显示过程:

bool ProblemList::opensafeproblem(CString oj,CString pid)
{
	CFile file;
	if(!file.Open("c:\\ojdata\\"+oj+"\\"+pid,CFile::modeRead))
	{
		return false;
	}
	CArchive ar(&file,CArchive::load);
	problemdata *data;
	ar>>data;
	ar.Close();
	file.Close();

	HtmlContent *contentdlg=new HtmlContent;
	if(!contentdlg->setdata(*data))
	{
		AfxMessageBox("文件打开失败");
		return false;
	}
	contentdlg->Create(IDD_DIALOG_HTML);
	contentdlg->ShowWindow(SW_SHOW);

	contentdlg->SetWindowTextA(pid);

	return true;
}
BOOL HtmlContent::OnInitDialog()
{
	CDHtmlDialog::OnInitDialog();

	this->SetHostFlags(DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_FLAT_SCROLLBAR);

	//将题目转化为HTML文件并在本地打开显示
	CFile *f=new CFile;
	if(!f->Open("c:\\ojdata\\"+data->oj+"\\"+data->id+".html",CFile::modeCreate|CFile::modeWrite))
	{
		AfxMessageBox("打开失败");
		this->CloseWindow();
	}
	f->Write(data->allcontent,data->allcontent.GetLength());
	f->Close();

	this->Navigate("c:\\ojdata\\"+data->oj+"\\"+data->id+".html");
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

整个离线题库就是这样,需要支持其他OJ只要改改正则和题号即可,对于一些没法直接获取到题目的OJ要做另外的处理,uestc的新OJ就需要另外的方式获取题目内容。

采集程序源码

采集程序

OJ题库下载

时间: 2024-11-07 03:17:15

OnlineJudge 离线题库采集的相关文章

《算法竞赛入门经典——训练指南》第二章题库

UVa特别题库 UVa网站专门为本书设立的分类题库配合,方便读者提交: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=442 注意,下面注有"extra"的习题并没有在书中出现,但在上面的特别题库中有,属于附加习题. 基础练习 (Basic Problems) UVa11388 GCD LCM UVa11889 Benefit UVa10943 How do y

OnlineJudge判题平台 ——后台流程

判题分两部分 1.其中judged为服务进程,d即daemon.负责轮询数据库,提取判题队列.当发现新任务时产生judge_client进程. 2.judge_client进程为实际判题程序,负责准备运行环境.数据,运行并监控目标程序的系统调用,采集运行指标,判断运行结果. Judged流程 初始化: 1.创建子进程pid_judged,并设置为会话的领头进程(umask(0),close(0~2)) 2.改变当前工作目录为"/home/judge" 3.将pid_judged写入文件

北大POJ题库使用指南

原文地址:北大POJ题库使用指南 北大ACM题分类主流算法: 1.搜索 //回溯 2.DP(动态规划)//记忆化搜索 3.贪心 4.图论 //最短路径.最小生成树.网络流 5.数论 //组合数学(排列组合).递推关系.质因数法 6.计算几何 //凸壳.同等安置矩形的并的面积与周长.凸包计算问题 8.模拟 9.数据结构 //并查集.堆.树形结构 10.博弈论 11.CD有正气法题目分类: 1. 排序 1423, 1694, 1723, 1727, 1763, 1788, 1828, 1838, 1

菁优网(jyeoo.com)题库数据(题目数据超102万组题数据超2100万含图片)

本文原创作者:数据超市(http://www.data-shop.net) 本文原始链接:http://www.data-shop.net/2016/03/jyeoo_com_20160321/ 数据说明:菁优网(http://www.jyeoo.com/)的题库数据.是按网站上的学科.教材组题来采集的,数据总数21,125,850条.数据包括以下学科:小学 – 数学初中 – 数学,物理,化学,生物,地理高中 – 数学,物理,化学,生物 特别说明:本次采集内容包括菁优网上初中.高中.小学的所有的

猿题库 iOS 客户端架构设计(原文地址:http://gracelancy.com/blog/2016/01/06/ape-ios-arch-design/)

猿题库 iOS 客户端架构设计 序 猿题库是一个拥有数千万用户的创业公司,从2013年题库项目起步到2015年,团队保持了极高的生产效率,使我们的产品完成了五个大版本和数十个小版本的高速迭代.在如此快速的开发过程中,如何保证代码的质量,降低后期维护的成本,以及为项目越来越快的版本迭代速度提供支持,成为了我们关注的重要问题.这篇文章将阐明我们在猿题库 iOS 客户端的架构设计. MVC MVC,Model-View-Controller,我们从这个古老而经典的设计模式入手.采用 MVC 这个架构的

北大ACM题库习题分类与简介(转载)

在百度文库上找到的,不知是哪位大牛整理的,真的很不错! zz题 目分类 Posted by fishhead at 2007-01-13 12:44:58.0 -------------------------------------------------------------------------------- acm.pku.edu.cn 1. 排序 1423, 1694, 1723, 1727, 1763, 1788, 1828, 1838, 1840, 2201, 2376, 23

题库类产品如何计算题目的难度值

一.引言 题库类产品(如猿题库.易题库等)的一个标配功能是预测用户未来要进行的某项考试得分,我们称之为目标考试预测分.以猿题库高考为例,即将参加高考的学生通过在题库上做大量练习,练习的效果会以学生的高考预测分呈现出来,这是学生最关注的指标,也是整个题库产品中最关键的数据.为了让“预测分”数据更加准确,我们引入了能力评估模型,通过测算用户在所有知识点上的能力水平,并将其量化成为一个数值.能力评估模型中有两个重要参数:题目难度值.用户答题的正确率.简化为:A=f(an,d)其中A表示能力值,an表示

OCP读书笔记(27) - 题库(ExamG)

601.You need to perform a block media recovery on the tools01.dbf data file in the SALES database byusing Recovery Manager (RMAN).Which two are the prerequisites to perform this operation? (Choose two)A. You must configure block change tracking fileB

OCP读书笔记(25) - 题库(ExamE)

401.Which of the following are correct about block media recovery? (Choose all that apply.)A. Physical and logical block corruption is recorded automatically in V$DATABASE_BLOCK_CORRUPTION.B. Logical corruptions are repairable by BMR.C. Physical corr