Python 网络爬虫获取 SYZOJ AC 代码

写本文之前,我共有两百多道题目在校内 OJ 上通过,由于某些原因,想要保存这些代码,于是想到使用 Python 实现自动爬取代码。同时考虑到效率问题,决定使用 aiohttp 编写一个高性能异步爬虫。 实现目标分析 这个爬虫需要能够爬取所有的已通过题目的列表,并继续爬取这些已通过题目的代码,随后保存到文件中。 校内 OJ 基于 SYZOJ 搭建,该 OJ 的项目地址为 https://github.com/syzoj/syzoj,故接下来的代码都是基于其实现的。 由于这个 OJ 的外网域名的带宽很小,一个网页最坏情况下需要花费 2~3 秒的时间,所以必须采用异步实现。这里就使用 aiohttp 了。 获取 Cookie 由于直接从浏览器里获取的 Cookie 无法正常使用,传给服务器无法识别,猜测是编码问题,所以使用在爬虫运行时即时获取 Cookie 的办法。 分析 SYZOJ 的登陆页面源码,找到如下代码片段: function login() { password = md5($("#password").val() + "syzoj2_xxx"); $("#login").addClass("loading"); $.ajax({ url: "/api/login", type: 'POST', data: { "username": $("#username").val(), "password": password }, async: true, success: function(data) { error_code = data.error_code; switch (error_code) { case 1001: show_error("用户不存在"); break; case 1002: show_error("密码错误"); break; case 1003: show_error("您尚未设置密码,请通过下方「找回密码」来设置您的密码。"); break; case 1: success(data....

April 18, 2022 · 4 min · oosquare

MultiGenerator 使用文档

概述 MultiGenerator 作为早年我的试验项目,已不再适合于实际使用。如果想要生成数据,请使用性能更高且更简单的 Rust 实现:data-gen-rs。 MultiGenerator 是一个为 OI 而生的多线程并行数据生成库,基于 C++ 17,使用面向对象和泛型等 Morden C++ 高级特性,只需要添加最少的额外代码,就可以获得最高的性能。以下是一个能够指定数据范围的 A + B Problem 数据生成器的示例代码: #include <random> #include <MultiGenerator.hpp> using MultiGenerator::DataConfig; using MultiGenerator::GeneratingTask; using MultiGenerator::SolutionTask; using MultiGenerator::NormalTemplate; using MultiGenerator::entry; using MultiGenerator::testcase; /** 指定数据生成器,仅需继承一个抽象类和实现一个成员函数 */ class AddGenerator : public GeneratingTask { private: void generate(std::ostream &data, const DataConfig &config) override { /** DataConfig 为配置信息,可以用于储存数据范围等元信息 */ auto minValue = std::stoi(config.get("minValue").value()); auto maxValue = std::stoi(config.get("maxValue").value()); std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> dist(minValue, maxValue); /** 像 cout 一样输出生成结果 */ data << dist(gen) << " " << dist(gen) << std::endl; } }; /** 指定数据求解器,也仅需继承一个抽象类和实现一个成员函数 */ class AddSolution : public SolutionTask { private: /** 假如你有标程,仅需要把程序用这个类包装起来,再把 main() 改为这个成员函数即可 */ void solve(std::istream &dataIn, std::ostream &dataOut, const DataConfig &) override { int a, b; /** 像 cin 一样读入数据 */ dataIn >> a >> b; /** 像 cout 一样输出答案 */ dataOut << a + b << std::endl; } }; int main() { constexpr int MAX_THREAD_COUNT = 8; constexpr int MAX_TESTCASE_COUNT = 20; constexpr char PROBLEM_NAME[] = "add"; /** 创建一个题目生成模板,指定数据文件名为 add#....

April 4, 2022 · 5 min · oosquare