需求:评论栏/作品栏要能分清是哪个博主。修好分组字段之后名字仍带星号,因为上游作为 教学版默认对昵称做中间脱敏(首尾各留 1 字,中间星号)。这个脱敏是**有损**的: 「张三」和「张四」都变成「张*」 「小明老师」和「小刚老师」都变成「小***师」 而本仓库的用途是监控一批公开创作者账号,分清谁是谁正是这一层要干的事。所以关掉它。 * config/base_config.py 新增 MASK_NICKNAME = False(和 INJECT_ALL_COOKIES 一样是个 开关,不是删代码 —— 改回 True 就恢复上游行为)。 * tools/user_hash.py 的 mask_nickname 读这个开关,关闭时原样返回。读的是模块属性而 不是导入值,测试才能 monkeypatch。脱敏实现本身一字未动。 * 顺带修一个数据陈旧问题:评论是去重后直接 continue 的,昵称只在首次入库时写一次, 于是开关一改(或评论者改名)老评论永远停在旧值 —— 而重采是唯一能拿到新值的途径。 现在已存在的评论会跟着刷新昵称(作品那边的 creator_name 早就是这么做的)。 * anonymous 的 creator_hash 保持不变:那是分组用的稳定键,不是显示名。 测试: * 三个隐私套件 + weibo 的 autouse fixture 强制把开关打开 —— 它们验的是**脱敏机制 本身**,机制仍然必须正确,所以显式打开来测,而不是让它们随部署配置漂。 * test_mask_and_hash_tools 改成两个方向都覆盖(开着脱敏 / 关着脱敏)。 * test_tieba_extractor.py 里 8 处字面量的脱敏期望值换成真实昵称 —— 提取器现在就是 返回原文的,期望值理应跟着改(这一条是行为变更的直接后果,不是测试放宽)。 * 新增一条:已入库的评论昵称会随重采刷新(且不会因刷新而重复插入)。
45 lines
1.6 KiB
Python
45 lines
1.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright (c) 2025 [email protected]
|
|
#
|
|
# 本文件为 MediaCrawler 教学版的一部分。
|
|
# 出于教学与防骚扰定位,爬取结果中不保留任何可定位到真人的用户个人信息
|
|
# (用户 ID、IP 归属地、头像、主页链接、签名、性别等一律不采集;
|
|
# 昵称保留但做中间脱敏)。本模块提供匿名化与脱敏工具。
|
|
import hashlib
|
|
|
|
import config
|
|
|
|
|
|
def anonymize_user_id(user_id) -> str:
|
|
"""把原始用户 ID 转成匿名哈希,用于内容/评论记录的创作者分组,
|
|
不暴露真实身份。返回 sha256 截断 16 位的十六进制串。"""
|
|
if user_id is None:
|
|
return ""
|
|
s = str(user_id).strip()
|
|
if not s:
|
|
return ""
|
|
return hashlib.sha256(s.encode("utf-8")).hexdigest()[:16]
|
|
|
|
|
|
def mask_nickname(name) -> str:
|
|
"""昵称中间脱敏:首尾各保留 1 字,中间替换为星号。
|
|
- 长度 <= 1:返回 "*"
|
|
- 长度 == 2:首字 + "*"
|
|
- 长度 >= 3:首字 + "***" + 尾字
|
|
这样既保留教学分析所需的内容归属语义,又无法据昵称定位到真人。
|
|
|
|
**是否启用由 config.MASK_NICKNAME 决定,本仓库默认关闭**(原样返回)。
|
|
脱敏是有损的,撞名很常见 —— 详见 base_config 里那一段的说明。
|
|
开关读的是模块属性而不是导入时的值,这样测试可以 monkeypatch 它。
|
|
"""
|
|
if name is None:
|
|
return ""
|
|
s = str(name)
|
|
if not getattr(config, "MASK_NICKNAME", False):
|
|
return s
|
|
if len(s) <= 1:
|
|
return "*"
|
|
if len(s) == 2:
|
|
return s[0] + "*"
|
|
return s[0] + "***" + s[-1]
|