按用户名进行 Reddit OSINT。发现已索引的帖子、评论、已删除和实时内容、活动模式、暴露的标识符以及总体暴露评分。
基于用户名的 Reddit OSINT。输入一个用户名,它就会返回公共 Reddit 档案中为该账户索引的所有内容:帖子、评论、哪些已被删除、哪些仍然存在、活动模式、泄露的标识符,以及一个暴露评分。
无依赖、无 API 密钥、无需登录。只需 python3。

python3 reddit_osint.py someuser
同样的分析也可作为面向 LLM 客户端的 MCP 工具使用,参见 MCP 服务器。
pip install -r requirements.txt && python mcp_server.py
Reddit 会删除内容,但第三方数据存档保留了删除之前存在的副本(或至少是 [removed] / [deleted] 标记)。此工具查询这些存档,并重建某个账户的活动画像。
这就是 h3kz-reddint 网页工具背后的前提,也是本脚本的参考对象:它将该流程(两个数据源、合并、PII 正则表达式、图谱、评分)重新实现为一个独立的 CLI,并带有测试和可选的代理支持。
| 数据源 | 说明 | 作用 |
|---|---|---|
Arctic Shift (arctic-shift.photon-reddit.com) | 免费的 Reddit 存档,由社区运营,是 Pushshift 的继任者。覆盖 2022 年至今,并保留原始抓取数据(selftext、removed_by_category、media_metadata、flair)。无需 API 密钥,按 IP 限速。 | 主要数据源。 提供 100% 的有用数据。 |
PullPush (api.pullpush.io) | 同一批 Reddit 数据的另一个索引(历史 Pushshift 转储)。 | 备用。并行查询并按 id 合并。其 subreddit API 返回 404,因此仅提供帖子和评论。 |
reddit.com | — | 从不查询。 仅用于在报告中构建永久链接 URL。不使用官方 Reddit API(无需登录),这也是它能在被封禁账户上工作的原因。 |
在一个真实样本账户上实测:Arctic Shift 返回 39 条帖子 / 139 条评论,PullPush 返回 38 条帖子,ID 有重叠。Arctic Shift 胜出;--source arctic 更快且足够。
pip install。仅使用标准库。pip install pysocks(可选,仅在 socks5:// 时需要)pip install -r requirements.txt(FastMCP)。CLI 不需要它。python3 reddit_osint.py someuser
在 stdout 上以 JSON 格式打印报告(仅摘要,不含逐项转储),并在 stderr 上记录日志。
python3 reddit_osint.py someuser --json report.json
python3 reddit_osint.py someuser --json report.json --quiet > /dev/null
report.json 包含 all_items 键,其中记录了每一条帖子和评论(文本、subreddit、评分、日期、状态、永久链接)。屏幕上的摘要会省略该键,以免输出 200 KB 的内容。
python3 reddit_osint.py someuser --subreddit dotnet # one subreddit only
python3 reddit_osint.py someuser --keywords "kubernetes" # posts mentioning X
python3 reddit_osint.py someuser --over18 true # NSFW only
python3 reddit_osint.py someuser --source arctic # single source (faster)
python3 reddit_osint.py someuser --max-pages 10 # cap pagination (100 items/page)
python3 reddit_osint.py someuser --delay 1.0 # slower, better with proxies
python3 reddit_osint.py someuser --rate-limit-retries 0 # fail fast when throttled
python3 reddit_osint.py someuser --proxies proxies.txt
proxies.txt,每行一个代理。接受的格式:
http://1.2.3.4:8080
https://1.2.3.4:8443
socks5://1.2.3.4:1080
socks5://user:[email protected]:1080
1.2.3.4:8080 # no scheme -> assumed http://
# comments and blank lines are ignored
重复项会被移除。如果文件中没有有效的代理,脚本会以退出码 2 退出,并且 不发出任何请求。
轮换。 代理按轮询方式依次使用。当某个请求返回封禁/限流
状态码时,该代理会被置于冷却状态 --cooldown 秒,脚本则转向下一个代理:
| 标志 | 默认值 | 作用 |
|---|---|---|
--proxies FILE | — | 代理列表。不提供则:直连。 |
--cooldown SECONDS | 30 | 被封禁的代理保持停用状态的时间。 |
--max-attempts N | (proxies+1) × 3 | 每个请求在放弃前的尝试次数上限。 |
--no-direct-fallback | 关闭 | 当所有代理都在冷却时,等待而不是直接连接。 |
--shuffle-proxies | 关闭 | 启动时打乱列表顺序(从第一个请求起就分散负载)。 |
--proxy-stats | 关闭 | 在报告中添加 proxy_stats:每个代理的 ok / banned / errors。 |
--rate-limit-retries N | 2 | 当 API 返回 429/422 时,对整个页面进行指数退避重试(30 秒、60 秒,上限 120 秒)。 |
被视为封禁的代码(会轮换代理):401 403 407 422 429 500 502 503 504 520 521 522 523 524,以及任何响应体包含 slow down / rate limit /
too many requests / timeout 的 400/409。Arctic Shift 使用 422 "Timeout. Maybe slow down a bit" 来
表示限流,因此它被有意当作封禁处理。
404 不会触发轮换:那是错误请求,而不是坏代理,换到别处重试也
无济于事。
退避。 封禁之后会等待 min(cooldown, 10 s) —— 这也适用于直连,
而直连恰恰是最受影响的情况。如果某个页面用尽了尝试次数,fetch_all 会以
指数退避重试该页面(--rate-limit-retries,默认 2 次尝试 = 30 秒和 60 秒)。正是这一点
防止脚本在运行中途触发限流时悄悄丢失评论。
带轮换的示例日志:
[proxy] http://1.2.3.4:8080 blocked (429), backing off 10s -> rotating
[proxy] http://5.6.7.8:1080 failed (Connection refused), rotating
[proxy] direct connection blocked (422), backing off 10s -> rotating
[!] arctic/comments rate limited, backing off 30s (retry 1/2)
当所有代理都处于冷却状态时,脚本会等待最长的剩余冷却时间后重试
(或使用直连,除非传入了 --no-direct-fallback)。
警告: 代理是你的 IP 通往第三方的出口路径。如果你不信任 代理供应商,请不要在此工具中使用代理。
| 变量 | 用途 |
|---|---|
REDDIT_OSINT_ARCTIC | 覆盖 Arctic Shift URL(镜像、测试)。 |
REDDIT_OSINT_PULLPUSH | 覆盖 PullPush URL。 |
id、title、selftext、body、author、subreddit、created_utc、score、permalink、
url、over_18、num_comments、link_flair_text、preview.images、media_metadata、
removed_by_category。
| 状态 | 检测方式 |
|---|---|
removed | 文本 = [removed] 或 [ Removed by Reddit ],或存在 removed_by_category(帖子)→ 版主删除了它。 |
deleted | 文本 = [deleted] 或 author == "[deleted]" → 作者删除了它。 |
live | 其他所有情况。 |
这正是该工具的核心意义所在:即使内容已被删除,存档仍会记录下删除这一事实, 而仅凭这一点就是有用的元数据。
deleted_pct 的 live/removed/deleted 细分。total_karma、post_karma、comment_karma、num_posts、
num_comments、earliest_post_at、last_comment_at(来自 /api/users/search)。Americas (UTC-8..UTC-5)Europe / Middle East / Africa (UTC+0..UTC+3)Asia / Oceania (UTC+7..UTC+11)Unknown / distributed| 类型 | 模式 |
|---|---|
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(拒绝误匹配的 .png/.jpg) | |
| BTC | bc1…(bech32)、1… / 3…(legacy/base58),26–59 个字符 |
| ETH | 0x + 40 个十六进制字符 |
| XMR | 4 + 94 个字符(共 95 个) |
| Telegram | t.me/<user>、telegram: @<user> |
| Phone | 10–15 位数字,带分隔符 |
| Domains | 每个 https?://host(过滤掉 reddit.com、redd.it、redditmedia.com、imgur.com) |
| Mentions | u/<username>(目标账户本身被排除) |
这些会显示在 identifiers、external_domains 和 mentioned_users 下。
score_exposure_0_100:内容识别出账户背后之人的可能性有多大。
对电子邮件(10)、BTC(8)、ETH/XMR(6)、Telegram/电话(4)以及域名/提及(各 2)加权,
以 30 为基准缩放并上限为 100。没有 PII 暴露的个人资料得分为 0。
将其用作分诊:高分并不意味着“已被入侵”,而是意味着“对此文本要小心”。collect() -> fetch_all() for each (source, kind)
|
+-> build_search_url() GET .../search?limit=100&sort=asc&author=U&after=<ts>
+-> PoolSession.get() PoolSession -> ProxyPool.pick() -> urllib
| 429/403 -> flag proxy, rotate, wait cooldown
+-> dedup by id Set over rows from both sources
+-> cursor = min(created_utc) + 1 and page again
|
analyze() -> status, counters, histograms, regexes, score
关键细节:
after = min(created_utc) + 1 来自上一页。在分页过程中,即使有新行落入索引,也能保持稳定。errors 中,而不是中止运行,并且 PoolSession.rate_limited 会保持为 True,以便分页层知道需要使用退避重试。--max-pages 可防止代理持续返回同一页时出现无限分页。sort=asc,以保持游标一致。--delay,默认 0.35 秒),以保持在速率限制之下。python3 -m unittest test_reddit_osint -v
python3 -m unittest test_reddit_osint.ProxyEndToEndTests # a single class
74 个测试,约 15 秒,无网络。它们在本地 ThreadingHTTPServer 实例上启动伪造源站和伪造代理,因此轮换是针对真实套接字而非模拟进行验证的。所有 sleep 调用都是注入的(sleeper=),这就是测试套件不会耗时数分钟的原因。
该测试套件不包含任何真实用户名——离线测试使用一个合成的 OFFLINE_USERNAME = "target",你可以在文件顶部自由修改。
# test_reddit_osint.py
OFFLINE_USERNAME = "target"
LiveArchiveTests 是可选启用的部分:它会访问真实归档,并针对你提供的任意用户名运行,因此仓库中不会内置任何人的信息。除非两个环境变量都已设置,否则该测试会被跳过:
REDDIT_OSINT_LIVE=1 REDDIT_OSINT_TEST_USER=someuser python3 -m unittest test_reddit_osint.LiveArchiveTests -v
| 变量 | 含义 |
|---|---|
REDDIT_OSINT_LIVE | 1 / true / yes 启用网络测试。其他任何值(或未设置)则跳过这些测试。 |
REDDIT_OSINT_TEST_USER | 用于运行测试的 Reddit 用户名。 |