简介:面向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 | 提交记录 | 必要 | 只保留 Accepted |
| solution | 提交记录里的代码块 | 必要 | 核心答案 |
| 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 带上你已经登录过的 Cookie:
import 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;q=0.9", }) if COOKIE_FILE.exists(): for c in json.loads(COOKIE_FILE.read_text(encoding="utf-8")): s.cookies.set(c["name"], c["value"], domain=c.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 接口,别一上来就啃 HTML
python123.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, timeout=10) resp.raise_for_status() data = resp.json() return data["problems"] def fetch_submission_code(session, problem_url): resp = session.get(problem_url, timeout=10) 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 请求的是我示例里的接口路径,实际项目里要以浏览器抓到的为准,不要照抄任何博客里的旧路径,因为平台改版是常态。timeout=10 是给单次请求的超时时间,太短容易在慢网络下误报,太长会让整个导出流程卡死。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(parents=True, exist_ok=True) (problem_dir / "meta.json").write_text( json.dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8", ) (problem_dir / "solution.py").write_text(code, encoding="utf-8")参数说明:ensure_ascii=False 让 meta.json 里保留中文标签而不是 \uXXXX 转义,直接打开文件可读性更好;indent=2 是给 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, key=score)逻辑说明: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(encoding="utf-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_ascii=False, indent=2), encoding="utf-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", compression=ZIP_DEFLATED, compresslevel=compresslevel) as zf: for fp in sorted(root.rglob("*")): if fp.is_file(): arcname = fp.relative_to(root).as_posix() zf.write(fp, arcname=arcname)参数说明:compression=ZIP_DEFLATED 是兼容性最好的压缩算法,几乎所有解压工具都支持;compresslevel 是压缩等级,范围 0-9,6 是体积与耗时最均衡的档位,个人归档用 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 互相不识别。
解决:所有写文件的地方统一固定encoding="utf-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"], input=stdin, capture_output=True, text=True, encoding="utf-8", timeout=5, ) assert result.stdout.strip() == expected逻辑说明:subprocess.run 用来执行目标代码,input 参数传入样例输入,capture_output=True 捕获 stdout,text=True 让输出以文本形式返回,encoding="utf-8" 保证中文输出不会解析错。timeout=5 是防死循环的关键参数,python123 的入门题里偶尔有人写while True不跳出,没有超时控制回测脚本会一直卡住。
如果你有几十道题,可以进一步把题目目录作为参数,用例文件统一放在每道题的 testcase.json 里,用 pytest 的 fixture 动态生成测试项。但不管你用多复杂的框架,核心目标只有一个:让 zip 里的每个 solution.py 都能复现平台上的 Accepted 结果。我自己在这步翻过车,曾把一份带 BOM 的文件直接存进压缩包,回测时编译失败,别人拿去用更是完全跑不起来。从那以后我养成的习惯是:先回测,再打包,绝对不让未经测试的代码进 zip。
这个方向做到后面,还能把整理题库的脚本本身做成一个小型 python 后端项目:数据层是题目目录加 index.json,业务层是导出和打包函数,表现层是命令行参数。想进一步练手的,可以再加一个 Flask 接口,把题库按知识点检索出来,这就是一个前后端分离项目的雏形了。希望这些整理思路能帮你少走几步弯路,把自己的做题记录变成真正能复用的资产。
本文还有配套的精品资源,点击获取