YAOTU INSIGHTS

Python后端脚本:导出python123题库并打包zip

Python后端脚本:导出python123题库并打包zip
简介面向Python初学者的python123.io平台后端相关题目答案整理包适合正在刷题或完成在线作业时需要参考思路的同学使用压缩包内共32个py文件整体仅14KB均为可直接阅读的Python源码覆盖基础语法、条件判断、循环累加、函数定义、分段计算与数学运算等典型练习场景。已有1247人学习下载。源码针对具体题目给出简洁实现例如两个数之间的数学运算、计算1~n的和、判断奇偶数与闰年、阶乘函数、自幂数、百钱买百鸡、鸡兔同笼等经典习题可帮助读者快速对照自己的解题逻辑发现易错点并巩固语法细节。这些题目大多围绕Python入门与基础算法设计展开答案代码风格直接、便于阅读适合逐行分析学习。作为题目答案资源重点在于配合python123平台进行验证与复盘既能夯实入门基础也为后续理解后端开发中的Web框架、HTTP协议、数据库操作等概念提供编程手感。1. python123.io 题库资源整理一个 zip 到底在解决什么问题在 python123.io 上刷题的人基本都动过同一个念头把做过的题、通过的代码、题目里的输入输出样例按题号整理成一份 zip 存到本地。这个标题里的“后端 - python.zip”表面看是一个资源包命名实际上指两种东西一份能离线复习和二次练习的题目答案归档以及生成这份归档的后端整理脚本。用 python 写脚本把 python123.io 上自己提交通过的代码、题目原文和样例输入输出拉下来按固定目录结构落盘再打包成 zip这就是全部需求。它适合三类人还在学校跟着 python123.io 做作业的初学者想把自己刷题记录变成复习资料的备考者以及刚接触 python 后端、想拿真实数据练手的数据整理爱好者。注意一点这份资源整理的是“你自己已经提交通过的答案”不是把平台题库整体搬走也不是找别人的答案包来抄。下面按我实际做过的路径拆开讲先定字段再写导出脚本然后规范目录、打包最后用 pytest 验证这些答案还能跑通。2. 先定义资源边界题目、提交代码和运行结果怎么变成结构化字段2.1 只整理自己已提交通过的代码别把平台题库整个拖下来很多人拿到这种标题第一反应是“把 python123.io 所有题目和答案爬下来”。我劝你不要这么干原因有两个。一是合规和版权边界这些题目和样例属于平台和出题方整站搬运很容易惹麻烦而你自己提交过的代码是你自己的劳动成果整理它没有任何争议。二是实用性一份没有上下文、没有题目描述的答案列表过两周你自己都看不懂更别说拿来复习。所以我定义的资源边界很窄只处理“我在 python123.io 上做过的题目”每个题目保留一份最优提交代码再配上题目原文、样例输入输出、提交状态这些元信息。这也正好对应标题里的“部分题目”——不是全题库而是做过的那部分。“部分”反而是这个方案最合理的约束导出快、文件小、内容可控。配合这套边界我建议把运行时环境也固定下来脚本用 python3.8 写依赖只装 requests 和 beautifulsoup4打包用标准库 zipfile。不要引入 scrapy、selenium 这种重工具杀鸡不用牛刀维护成本还高。2.2 字段清单答案不只是 .py 文件而是带元信息的记录整理答案前先想清楚每一道题要留哪些字段。只存 solution.py 是最懒的做法等于把黑匣子原封不动搬回家以后根本没法检索。我一般按下面这张表设计每道题的数据结构字段来源是否必要用途problem_id题目 URL 或页面元素必要目录命名、去重title题目列表页必要人眼识别difficulty题目详情页可选复习时分级status提交记录必要只保留 Acceptedsolution提交记录里的代码块必要核心答案sample_input题目详情页必要回测输入sample_output题目详情页必要回测预期输出submitted_at提交记录可选判断新旧版本tags题目详情页可选按知识点归组这个字段表看起来简单却是整套后端方案的地基。如果你做过前后端分离项目会发现它和设计一张 submissions 表没什么区别只是把数据库换成了 JSON 文件。我建议每道题先写成一份 meta.json再让 solution.py 和它同级存放这样后续不管是做复习索引还是做自动化回测都只需要遍历目录不用解析 HTML。下面给一个 meta.json 的样子字段不复杂但能让你一眼看明白这道题当初是怎么过的{ problem_id: 1001, title: 温度转换, difficulty: 简单, status: Accepted, best_solution: solution.py, sample_input: 32C, sample_output: 89.6F, submitted_at: 2025-06-01 10:24:00, tags: [python基础, 顺序结构] }字段确定后就不要随意改名尤其是 problem_id 和 best_solution。我早期整理资源时中途改过一次字段名结果所有题目的索引路径全部断掉等于整个题库作废。字段的命名习惯是全部小写多个单词用下划线时间统一成 YYYY-MM-DD HH:mm:ss代码文件名固定为 solution.py。这样后续的打包脚本、回测脚本都只需要一套约定不用为每道题写特例。说句实在话这步的价值往往是被低估的。很多人拿到别人整理好的答案 zip打开一看全是散装 .py 文件没有题目、没有样例、没有难度分级这种资源你根本没法拿来刷第二遍。字段结构就是答案资源的后悔药宁可导出慢一点也要把元信息带全。3. 用 requests 导出自己的提交记录最小脚本与登录态处理3.1 python123 登录后的会话保持手动导一次 Cookie比自动登录更省心python123.io 的题目列表和提交记录都需要登录才能访问所以第一步是处理登录态。我不建议在脚本里直接写账号密码做自动登录因为平台登录页随时可能加验证码你写再多重试逻辑都白搭。最稳的做法是在浏览器里登录一次把 Cookie 手动导出成 cookies.json脚本启动时读入这个文件。缺点是 Cookie 会过期但做题的人基本是一两天内集中导出完全够用。下面是一段会话初始化代码核心是让 requests 的 Session 带上你已经登录过的 Cookieimport json from pathlib import Path import requests BASE_URL https://www.python123.io COOKIE_FILE Path(cookies.json) def load_session(): s requests.Session() s.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Accept-Language: zh-CN,zh;q0.9, }) if COOKIE_FILE.exists(): for c in json.loads(COOKIE_FILE.read_text(encodingutf-8)): s.cookies.set(c[name], c[value], domainc.get(domain, .python123.io)) return s逻辑说明先创建 Session 对象统一设置 User-Agent 和 Accept-Language避免被服务端识别成异常脚本然后从 cookies.json 里逐个恢复 Cookie。参数 domain 要格外注意浏览器导出的 Cookie 有些带域名有些是 host-only手动补一个默认值.python123.io能避免部分请求带不上 Cookie 导致 302。cookie 过期后脚本的报错会很直接——请求返回的是登录页 HTML而不是题目列表看到这种情况重新导一次 Cookie 即可。获取 Cookie 这一步有现成浏览器扩展可以用直接在浏览器“开发者工具-网络”里找到 python123.io 的任意请求复制请求头里的 Cookie 字段转成 JSON 数组写进 cookies.json 也行。注意不要让爬虫脚本去模拟验证码那是纯浪费时间。登录态的事情解决了后面的页面请求才谈得上稳定。3.2 解析提交列表并保存代码先找 XHR 接口别一上来就啃 HTMLpython123.io 的页面可能有两种渲染方式服务端直接输出 HTML或者前端异步加载 JSON。我刚开始写的时候就吃过亏拿着 requests 请求题目列表页用 BeautifulSoup 解析了一堆空标签后来才发现数据是从一个 XHR 接口返回的。所以在写解析之前先打开浏览器开发者工具切到 Network 面板刷新题目列表看数据到底是从哪个 URL 回来的。如果响应内容是 JSON直接解析 JSON 比解析 HTML 省事十倍。下面是我常用的一套导入流程。先请求题目列表接口拿到题号列表再逐个请求题目详情和提交记录import json import time import requests from bs4 import BeautifulSoup def fetch_problem_list(session): # 这里换成你在开发者工具 Network 里看到的实际接口 url f{BASE_URL}/api/my/problems resp session.get(url, timeout10) resp.raise_for_status() data resp.json() return data[problems] def fetch_submission_code(session, problem_url): resp session.get(problem_url, timeout10) resp.raise_for_status() soup BeautifulSoup(resp.text, html.parser) code_tag soup.select_one(pre.code) # 选择器以实际页面为准 return code_tag.get_text() if code_tag else 逻辑说明fetch_problem_list 请求的是我示例里的接口路径实际项目里要以浏览器抓到的为准不要照抄任何博客里的旧路径因为平台改版是常态。timeout10 是给单次请求的超时时间太短容易在慢网络下误报太长会让整个导出流程卡死。resp.raise_for_status() 的作用是快速失败状态码不是 200 就直接抛异常避免把错误页面当成正常数据写进题库。fetch_submission_code 里我用了 BeautifulSoup 解析代码块这里的pre.code只是示意选择器真实页面要右键检查元素后复制实际 class。如果你在 Network 里看到代码数据也是 JSON 返回的那就直接 resp.json() 取代码字段。选择器失效是这类导出脚本最常见的翻车点我后面专门有一章讲这个坑。每道题抓完之后落盘的代码要注意编码。写文件时务必指定 utf-8否则 Windows 下默认编码可能把中文注释写成乱码from pathlib import Path def save_problem(problem_dir, meta, code): problem_dir.mkdir(parentsTrue, exist_okTrue) (problem_dir / meta.json).write_text( json.dumps(meta, ensure_asciiFalse, indent2), encodingutf-8, ) (problem_dir / solution.py).write_text(code, encodingutf-8)参数说明ensure_asciiFalse 让 meta.json 里保留中文标签而不是 \uXXXX 转义直接打开文件可读性更好indent2 是给 JSON 做格式化方便 diff。写文件统一用 Path.write_text 而不是 open().write()能少写一行 close也更难忘记指定编码。到这里一份题目答案资源的基本形态就出来了每个题一个文件夹里面放着 meta.json 和 solution.py。接下来要解决的是“同一道题提交了好几次到底该保留哪份”的问题。3.3 同一道题多个提交记录怎么选最优答案python123.io 上做一道题提交三五次很正常第一次可能是 Wrong Answer最后一次才 Accepted但最后一次通过的代码不一定是最短的。直接拿最新提交当答案可能把一份又臭又长的代码存进你的题库。我写了一个简单的排序函数用元组优先级选出最优提交def pick_best(submissions): status_weight {Accepted: 0, Wrong Answer: 1, Runtime Error: 2} def score(sub): return ( status_weight.get(sub[status], 9), sub.get(cost_time, 9999), len(sub.get(code, )), ) return min(submissions, keyscore)逻辑说明Python 元组比较是从左到右逐项比较的所以这里先按状态排序Accepted 排最前同一状态下运行时间短的排前面再同时间下代码长度短的排前面。min() 返回元组最小的那一条也就是优先 Accepted、其次时间短、再次代码短。这个评分规则不复杂但已经能挡住大部分低级问题。如果你对答案质量要求更高可以把代码长度改成“代码行数”或“圈复杂度”不过对 python123.io 的入门题来说运行时间和代码长度足够用。注意这个函数处理的是内存里的提交列表是你在 3.2 节请求详情时顺带拿到的不要在落盘之后再排序否则来回改文件容易出错。4. 目录规范与 zip 打包把导出的答案变成可复用离线题库4.1 一个题目一个文件夹模板目录结构与命名规则导出的资源要让人拿回家就能用目录结构必须一眼能看懂。我最常用的结构是这样python123_export/ ├── 1001_温度转换/ │ ├── meta.json │ └── solution.py ├── 1002_整数求和/ │ ├── meta.json │ └── solution.py └── index.json命名规则是题号加下划线加题目中文名。题号放在最前面这样按字母序排列时所有题自动按题号排好队不会乱。index.json 是目录索引内容放每道题的 problem_id、title、difficulty、submitted_at相当于一个超轻量数据库表后续想按难度筛选题目就只读这一个文件不用遍历所有目录。为什么不用纯数字目录因为纯数字目录打开后根本分不清是哪道题每次都要点进 meta.json 看为什么不用纯中文目录因为部分老版本解压工具对中文目录名的兼容性一般容易乱码。题号加中文的折中最稳。这里我强烈建议在导出时就顺手生成 index.json不要等全部导出完再补因为后补意味着要把所有 meta.json 重读一遍多一次遍历就多一次编码和路径出错的机会。index.json 的生成逻辑很简单遍历根目录读取每个子目录里的 meta.json抽出关键字段汇总import json from pathlib import Path def build_index(root: Path, output_file: Path): items [] for problem_dir in sorted(p for p in root.iterdir() if p.is_dir()): meta_file problem_dir / meta.json if not meta_file.exists(): continue meta json.loads(meta_file.read_text(encodingutf-8)) items.append({ problem_id: meta[problem_id], title: meta[title], difficulty: meta.get(difficulty, ), submitted_at: meta.get(submitted_at, ), }) output_file.write_text( json.dumps(items, ensure_asciiFalse, indent2), encodingutf-8, )这里打了两个防守一个是sorted()保证目录顺序稳定另一个是.exists()跳过不完整的题目目录。因为整理过程中可能出现抓取到一半程序崩溃的情况留一个不完整的目录比让整个 index 生成失败要好。4.2 用 zipfile 打包压缩算法、压缩级别和中文文件名的选择目录整理完最后一步是把整个 python123_export 打成一个 zip。打包用标准库即可不装第三方压缩库from pathlib import Path from zipfile import ZIP_DEFLATED, ZipFile def pack(root: Path, zip_name: str, compresslevel: int 6): with ZipFile(zip_name, w, compressionZIP_DEFLATED, compresslevelcompresslevel) as zf: for fp in sorted(root.rglob(*)): if fp.is_file(): arcname fp.relative_to(root).as_posix() zf.write(fp, arcnamearcname)参数说明compressionZIP_DEFLATED 是兼容性最好的压缩算法几乎所有解压工具都支持compresslevel 是压缩等级范围 0-96 是体积与耗时最均衡的档位个人归档用 9 也行但题库这种以文本为主的资源压缩率差异不大9 反而明显拖慢打包速度。arcname 用了as_posix()这是关键Windows 下 Path 的字符串会用反斜杠 \ 分隔路径写进 zip 后很多工具解压会出错转成 Posix 风格的正斜杠能彻底规避。另外提醒一下zipfile 写入时不要直接写绝对路径否则解压后所有文件会带着你的电脑用户名目录层级我见过有人压缩包里套了三层C:/Users/xxx/Desktop/...解压出来乱七八糟。上面的fp.relative_to(root)就是用来裁掉根目录前缀的。打完包后还要做一次完整性校验至少用 ZipFile 重读一遍所有条目看有没有文件损坏with ZipFile(zip_name, r) as zf: bad zf.testzip() print(所有文件正常 if bad is None else f损坏文件: {bad})这一步看着多余但真的能拦住“压缩包拷到 U 盘后解压失败”这类问题。我自己的习惯是打包后顺手执行一次校验文件多时才几十秒比事后发现资源损坏再重新导出划算得多。5. 整理题库最容易踩的五个坑从登录失效到中文乱码5.1 跑了三分之一突然开始返回登录页现象脚本前十几道题正常然后所有请求都返回登录页 HTML解析出来的内容全是空或者乱码。原因python123 的登录状态不是永久有效的Cookie 有有效期。Session 长时间被脚本占用服务器判定会话过期后续请求被重定向到登录页。脚本里如果没做响应内容判断会把登录页当成正常响应继续解析后面的数据全是黑匣子。解决每次请求前先判断响应 URL 或响应内容里是否包含“登录”关键字检测到就中断导出并提示“Cookie 已失效请重新登录”。更省事的做法是给整个导出流程加断点续传每成功导出一题就落盘一次下次运行时跳过 meta.json 已存在的题目。这样即使过期也不需要从零开始。5.2 页面结构改版解析结果整片空白现象昨天还能正常解析今天运行脚本题目列表和提交代码全部为空也没有报错。原因平台前端改版你把 CSS 选择器写死在脚本里class 名一变BeautifulSoup 就找不到任何节点。这类问题最坑的一点是程序不会报错因为空列表也是正常返回值你必须肉眼检查输出黑匣子效应特别强。解决优先找 XHR 接口而不是解析页面。在浏览器 Network 里看数据源如果是 JSON 接口直接用 json 解析字段不依赖页面结构。如果平台没有接口只能用 HTML 解析就把选择器集中放到一个常量配置里预留修改位置不要散落在代码各个角落。另外每次导出前先打印一道题的解析结果抽查别等全部跑完才发现第一批就抓空了。5.3 中文注释全部变成乱码代码直接没法看现象solution.py 保存到本地后文件里的中文注释变成䏿–‡一类的乱码打开代码像天书。原因python123.io 返回的代码是 UTF-8 编码而你用文本编辑器或脚本默认的 GBK 编码去写文件或者写文件时没指定 encoding。Windows 简体中文环境下的默认编码是 GBK和 UTF-8 互相不识别。解决所有写文件的地方统一固定encodingutf-8绝不依赖系统默认编码。读取 meta.json 和写入 solution.py 都要显式传 encoding。同时把代码存成 .py 时尽量保留原题目的编码信息不要在脚本里做任何隐式转码。如果你拿到一份已经乱码的 zip用 VS Code 打开时选择“通过编码重新打开”选 UTF-8大部分情况能救回来但最好还是源头治理。5.4 同一道题提交多次答案被后一次覆盖现象整理完的题库里某道题的 solution.py 是错的或者和 meta.json 里的状态对不上。原因处理提交记录时没有做排序就覆盖写文件。假设你循环里先解析到 Accepted 的旧提交后解析到 Runtime Error 的新提交如果后者最后被写入磁盘你的正确答案就被错误代码覆盖了。解决先在内存里用第 3 章的 pick_best 函数选定最优提交确认选中的记录状态是 Accepted 后再写 solution.py。写文件前再加一道防线如果已有 solution.py 且新代码会被标记为非最优直接跳过不写。这道防线看起来多余但当题目数量超过五十道后人工检查是看不过来的只有代码才能保证覆盖逻辑严格统一。5.5 zip 解压后中文目录乱码文件夹层级错乱现象把 zip 发给别人或在另一台 Windows 电脑上解压文件夹名字变成乱码有时候整个目录树散掉。原因zipfile 写入中文文件名时使用的是 UTF-8 标记但部分 Windows 自带解压工具按 GBK 解析文件名两边对不上就乱码。另外如果打包时写入的是绝对路径或没有统一斜杠解压工具会按照它识别的方式重新组织目录路径一乱就全乱了。解决打包时 arcname 统一用正斜杠且只写相对路径这部分我在 4.2 节已经说明。想彻底避免中文乱码争议可以在目录命名时直接用题号加拼音比如1001_wendu_zhuanhuan牺牲一点可读性换兼容性。或者要求接收方用 7-Zip 解压7-Zip 对 UTF-8 文件名支持得比系统自带工具好。6. 用 pytest 回测答案整理出来的 zip 能跑通才叫资源答案资源整理完最后一个环节是回测验证。很多人在这一步偷懒理由是“这些都是 Accepted 的代码怎么可能跑不通”。实际上你把代码从平台复制到本地文件再从本地文件打包成 zip中间任何一次转码或截断都可能让代码失效。所以我自己整理题库时固定动作是用 pytest 写一个批量回测脚本把每道题的样例输入喂给 solution.py比对预期输出。针对 python123.io 这类以标准输入输出判题的平台直接 import solution 模块是行不通的因为代码主体可能是input()加print()的结构。最贴近在线评测的做法是使用 subprocess 启动子进程喂入样例输入抓取标准输出import subprocess import pytest CASES [ (32C, 89.6F), (100F, 37.8C), ] pytest.mark.parametrize(stdin,expected, CASES) def test_temperature(stdin, expected): result subprocess.run( [python, python123_export/1001_温度转换/solution.py], inputstdin, capture_outputTrue, textTrue, encodingutf-8, timeout5, ) assert result.stdout.strip() expected逻辑说明subprocess.run 用来执行目标代码input 参数传入样例输入capture_outputTrue 捕获 stdouttextTrue 让输出以文本形式返回encodingutf-8 保证中文输出不会解析错。timeout5 是防死循环的关键参数python123 的入门题里偶尔有人写while True不跳出没有超时控制回测脚本会一直卡住。如果你有几十道题可以进一步把题目目录作为参数用例文件统一放在每道题的 testcase.json 里用 pytest 的 fixture 动态生成测试项。但不管你用多复杂的框架核心目标只有一个让 zip 里的每个 solution.py 都能复现平台上的 Accepted 结果。我自己在这步翻过车曾把一份带 BOM 的文件直接存进压缩包回测时编译失败别人拿去用更是完全跑不起来。从那以后我养成的习惯是先回测再打包绝对不让未经测试的代码进 zip。这个方向做到后面还能把整理题库的脚本本身做成一个小型 python 后端项目数据层是题目目录加 index.json业务层是导出和打包函数表现层是命令行参数。想进一步练手的可以再加一个 Flask 接口把题库按知识点检索出来这就是一个前后端分离项目的雏形了。希望这些整理思路能帮你少走几步弯路把自己的做题记录变成真正能复用的资产。本文还有配套的精品资源点击获取