把 Netflix 账号里导出的“观看记录”CSV 变成一份个人观影小结:每个月看了多少条、看得最多的剧,以及哪天一口气连看了 4 集以上。日期格式会自动识别;遇到无法判断的情况(比如分不清 3/4 是 3 月 4 日还是 4 月 3 日),脚本会直接报错,而不是悄悄算错。
先看清成品
pytest 跑了 3 个测试,全部通过;用 9 行示例数据运行,输出了月份分布、排行和“2026-10-12《示例剧》4 集”。示例 CSV 是我按帮助中心的描述自己编写的(两列:Title、Date),没有拿真实账号的导出文件验证过。特别是日期写法,会随账号的语言和地区设置而不同,所以脚本做了自动识别和歧义检查。
注意:Netflix 没有面向公众的开放 API,本系列只用官方提供的功能和公开数据:账号里的观看记录导出、Tudum 的 Top 10 数据文件,以及 “What We Watched” 报告。不要爬取网站,不要绕过 DRM 或录屏,不要用 VPN 或代理切换地区,不要违反官方的家庭共享规则去共享账号(见“项目资源”里的帮助页面)。套餐价格以官方页面为准,本文不列数字。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
viewing_stats.py |
识别日期格式、按月统计、剧集排行、找出连看的日子 |
| 歧义保护 | 日/月顺序无法判断时直接报错,可以用 --format 指定 |
| 本地处理 | 数据不上传到任何地方 |
需要准备哪些环境?
| 项目 | 要求 | 说明 |
|---|---|---|
| Netflix 账号 | 你自己的账号和档案 | 只分析你有权查看的档案 |
| 浏览器 | 网页版 | 导出功能在网页版的账号页面里 |
| Python | 3.8 以上 | 只用标准库 |
文件结构
33-netflix-A-src/
├── viewing_stats.py
├── sample_history.csv # 自己编写的示例
└── test_stats.py
第 1 步:导出(官方帮助中心的步骤)
- 用网页浏览器打开账号页面(Account)。
- 选择 Profiles,再选一个档案。
- 打开 Viewing activity;如果列表不完整,点 Show More。
- 在页面底部点 Download all,下载 CSV 文件。
如果账号已经不再有效,帮助中心说明要通过“下载你的个人信息”来获取观看记录(链接见文末)。
第 2 步:脚本
viewing_stats.py:
"""分析 Netflix “观看记录”(Account → Profiles → 某个档案 → Viewing activity → Download all)导出的 CSV。
文件有两列:Title、Date。日期写法随账号的语言和地区设置而不同,脚本会自动判断,判断不了就报错。
用法:python viewing_stats.py NetflixViewingHistory.csv"""
import csv, sys
from collections import Counter, defaultdict
from datetime import datetime
FORMATS = ["%Y-%m-%d", "%m/%d/%y", "%d/%m/%y", "%m/%d/%Y", "%d/%m/%Y", "%d.%m.%y", "%d.%m.%Y", "%Y/%m/%d"]
def detect_format(dates):
ok = [f for f in FORMATS if all(_try(d, f) for d in dates)]
if not ok: raise ValueError(f"认不出日期格式,例如 {dates[0]!r}")
families = {f.replace("y", "Y") for f in ok} # 两位/四位年份算一类
if len(families) > 1:
raise ValueError(f"日期有歧义({', '.join(sorted(ok))} 都能解析),请用 --format 指定")
return ok[0]
def _try(s, f):
try: datetime.strptime(s, f); return True
except ValueError: return False
def split_title(t):
"""'Show: Season 1: Episode' → ('Show', 'Season 1', 'Episode');电影只有一段"""
parts = [p.strip() for p in t.split(": ")]
return (parts[0], parts[1] if len(parts) > 2 else "", ": ".join(parts[2:]) if len(parts) > 2 else (parts[1] if len(parts) == 2 else ""))
def stats(rows, fmt=None):
dates = [r["Date"] for r in rows]
fmt = fmt or detect_format(dates)
per_month, per_show, per_day = Counter(), Counter(), defaultdict(int)
for r in rows:
d = datetime.strptime(r["Date"], fmt).date()
per_month[d.strftime("%Y-%m")] += 1
show, season, ep = split_title(r["Title"])
per_show[show] += 1
per_day[(d, show)] += 1
binges = sorted(((n, d, s) for (d, s), n in per_day.items() if n >= 4), reverse=True)
return {"format": fmt, "total": len(rows), "months": dict(sorted(per_month.items())),
"top_shows": per_show.most_common(5), "binges": binges[:5]}
if __name__ == "__main__":
args = sys.argv[1:]
fmt = args[args.index("--format") + 1] if "--format" in args else None
rows = list(csv.DictReader(open(args[0], encoding="utf-8-sig")))
s = stats(rows, fmt)
print(f"共 {s['total']} 条记录(日期格式 {s['format']})")
print("每月条数:", " ".join(f"{m}:{n}" for m, n in s["months"].items()))
print("看得最多:", ";".join(f"{t}({n})" for t, n in s["top_shows"]))
for n, d, show in s["binges"]: print(f"连看:{d} 《{show}》{n} 集")
剧集标题一般写成“剧名: 季: 集名”,脚本按 ": " 拆开,取第一段作为剧名。如果剧名本身带冒号,统计时会被拆开,这种情况需要手动合并。
第 3 步:测试和运行
sample_history.csv:
Title,Date
"示例剧: 第 1 季: 第 6 集",10/12/26
"示例剧: 第 1 季: 第 5 集",10/12/26
"示例剧: 第 1 季: 第 4 集",10/12/26
"示例剧: 第 1 季: 第 3 集",10/12/26
"示例剧: 第 1 季: 第 2 集",10/5/26
"示例剧: 第 1 季: 第 1 集",10/5/26
"示例电影",9/28/26
"另一部剧: 第 2 季: 归来",9/14/26
"另一部剧: 第 2 季: 出发",9/13/26
$ python -m pytest -q
3 passed
$ python viewing_stats.py sample_history.csv
共 9 条记录(日期格式 %m/%d/%y)
每月条数: 2026-09:3 2026-10:6
看得最多: 示例剧(6);另一部剧(2);示例电影(1)
连看:2026-10-12 《示例剧》4 集
调试示例
只有十月的数据时,报日期有歧义
只取示例数据的前 6 行(全是十月,日期是 10/12 和 10/5):
$ python viewing_stats.py oct.csv
ValueError: 日期有歧义(%d/%m/%y, %m/%d/%y 都能解析),请用 --format 指定
$ python viewing_stats.py oct.csv --format %m/%d/%y
共 6 条记录(日期格式 %m/%d/%y)
完整的示例数据里有一行 9/28/26:28 不可能是月份,所以格式能确定下来。只有十月的数据时,10/12 既可以是 10 月 12 日,也可以是 12 月 10 日。如果脚本随便选一种,统计结果会悄悄错掉一半,所以这里选择报错。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
认不出日期格式 |
你的地区用了别的写法 | 看一行原始数据,用 --format 指定 |
| 有乱码 | 用 Excel 打开后又另存了一次 | 直接用下载的原文件 |
| 同一部剧被统计成两部 | 剧名本身带冒号 | 手动合并 |
| 有档案看不到 | 你没有那个档案的权限 | 只统计自己的档案 |
模板规则
- 只用官方导出的数据,不要爬取网页。
- 日期无法判断时报错,不要猜。
- 个人数据只在本地处理。
项目资源
| 用途 | 地址 |
|---|---|
| 查看和下载观看记录 | https://help.netflix.com/en/node/101917 |
| 获取你的个人信息副本 | https://help.netflix.com/en/node/100624 |
| Python csv 模块 | https://docs.python.org/3/library/csv.html |
| 分享账号(官方规则) | https://help.netflix.com/en/node/123277 |
| Extra Members | https://help.netflix.com/en/node/123279 |
| 使用条款 | https://help.netflix.com/legal/termsofuse |
没有 API,也能用官方的导出功能做出有用的个人统计。关键在于老老实实处理好格式,日期有歧义的时候宁可报错,也不要猜。