一个月前我写过一篇采集方法论,然后我把它忘了

4200 字
16 分钟
一个月前我写过一篇采集方法论,然后我把它忘了

先说一件不太体面的事。

2026 年 8 月 27 日,我写过一篇文章,讲怎么从零搭一套多源采集管线。那篇东西不差——21 个渠道逐条实测过,去重、并发、追溯都写了,是我和另一个 AI 双线跑完一个真实项目之后的复盘。后来 9 月 16 日站上做减法,把它和一批东西一起下架了。

一个月后,我为了收一组 Instagram 参考图,让一个 AI 去干同样性质的活。它跑了两个小时,交回来的东西我没法用:数字失控、判据说谎、该保留的被当成垃圾。我当时的原话是「我无法接受你不按照我的诉求去办事」。

那篇被我自己下架的文章里,写着它正在踩的每一个坑。

所以这篇文章分两部分:前面是那份方法论,我把它从仓库历史里捞回来重新整理了;后面是这次实战补上的东西——有些是旧文没讲到的,有些是讲到了但我没当回事的。

如果你也打算做一个自己的素材库,这篇可以当操作手册用。工具在文末,方法不受版权保护,拿走就是。

LIVE DEMO · 不会真的请求外站

把链接丢上来,图会铺到一张桌上。

帖子 / Reels / 分享口令

网页链接、短链,或 App 分享口令

点「粘贴示例」→「识别并提取」,看图是怎么摊开的。

打开真正的取图页 ↗

一、先分清「产地」和「工具」#

我犯过的第一个错,是一搜到某个站就盯着它猛抓。结果是收回来一堆重复、低质的图,还白白触发风控。

复盘之后发现,问题不在「不会下」,在于没先分清原料产地和收割工具。这是两件独立的事:

  1. 选对产地——哪些站才是这类素材的高质量大本营;
  2. 掌握工具——能不能批量、能不能去重、能不能追溯。

然后是个很关键的醒悟:那些看起来很精致的参考站,自己大多也是从少数几个上游源拿图。所以与其扒一个站,不如直接掌握它的上游源。

这条到现在都是整套方法论的地基。扒站得到的是别人的加工结果,端上游源得到的是原料。

二、可批量源才是真主力#

把渠道逐个实测之后,结论其实很集中——真正「开箱即用、能批量、能去重」的没几个。

渠道能拿什么关键条件
Pixiv二次元插画ajax 接口无需登录,下载带 Referer
Wallhaven壁纸公开 API,可按分辨率/纯度过滤
CivitaiAI 生图返回自带内容 hash,天然好去重
IG(gallery-dl + cookie)Kpop 官方原图一次拉到过 3.5 万张
LOL DataDragon游戏官方原画官方渠道,干净

而那些「看着美好」的站,实测大多是坑:

  • Pexels / Pixabay / Unsplash:质量顶级,但要 API key(注册即给,不难,但多一道手续);
  • DeviantArt / 图搜类:403 / 401 / 要 OAuth;
  • B 站专栏:有优质大图,但会撞 412 风控;
  • 某些聚合站:成分太杂,不适合当主路。

一句话:少而精的可批量源,好过一堆看着好但只能硬刚反爬的站。

三、管线:并发 + 去重 + 追溯#

真正把量堆起来的骨架就三段:

研究方法 → 评估可行性 → 并发采集 → 去重归档 → 上线管理 → 规模化

其中三件事最关键。

① 并发#

单线程抓太慢。多线程(每主题 10 个 worker)比单线程快一个量级不止。

from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=10) as ex:
for i in ids:
ex.submit(download_one, i, dirp, existing)

② 去重——这条我这次又栽了#

重复下载的根因,是用 URL 去重。 同一张图换个 URL、翻一页、换一种命名,就识别不出来了。

def url_dedup(url): return sha256(url).hexdigest() # ❌ 同图不同 URL 就漏
def content_dedup(buf): return sha256(buf).hexdigest() # ✅ 跨源/跨轮都判重

正确的做法是按文件字节算 sha256。 跨源、跨轮、跨主题,绝不会重复入库。

这次实战的教训:我遇到的不是「重复」,是它的反面——同一张图有两种文件命名(一个来自帖子网格、一个来自轮播第一张)。按文件名判,它们是两张;按内容 hash 判,它们是一张。去重和查重都得落到字节上,不是名字上。

③ 可追溯#

每个主题目录放一份 manifest.json,记录来源、作者、hash、时间。收藏的东西要知根知底。

{
"file": "a3f9c1d2e8b7...",
"source": "instagram/@handle",
"post_id": "3784443148073331773",
"media_id": "3784443134089551574",
"sha256": "a3f9c1d2e8b7...",
"fetched_at": "2026-10-15T02:18:44"
}

四、限流:认清形态,别把 404 当没数据#

这一段是旧文里最值钱的部分,也是这次被原样踩掉的坑。

def http_get(url):
for attempt in range(1, 5):
try:
r = fetch(url)
if r.status == 200:
return r
except HTTPError as e:
if e.code in (404, 500): # ⚠️ 软限流:不是真 404!
sleep(2 ** attempt); continue
if is_cf_challenge(r): # 硬限流:换出口 IP
rebuild_proxy(); continue
if e.code == 429: # 按 Retry-After 等
sleep(retry_after(e)); continue
raise

三种限流,三种反应,别混:

形态含义正确反应
404 / 500软限流,不是没数据指数退避重试
cf-mitigated 挑战页硬限流,出口被盯上换出口 IP
429频率超限读 Retry-After 老实等

这次实战补的:这次遇上的 401 带着 require_login: true,看着像「会话失效」,实际是节点级限流。判定方法是拿同一套 cookie 换出口节点试——换个节点秒开 200,原节点要等十几分钟。换节点比等冷却快约 50 倍。

教训:401 不一定是登录问题。先怀疑出口,再怀疑 cookie。 那次差点把好好的 cookie 全重导一遍。

还有一个更阴的:零结果要拒绝写表。

扫描失败返回 0 条,如果代码照常把「0 条」写进真值表,然后返回成功——你就得到一份看起来很正常的空数据,后面所有校验都基于它,全部通过。

这次实战的铁律:扫描零结果,必须拒绝写表并报错。绝不能用空表覆盖好表之后返回成功。这个 bug 让我在「一切正常」的假象里待了很久。

五、缺口判定:别用「总格数减格号」#

这是这次实战里我最后悔没早点做对的一件事,旧文里没写。

一个帖子有多少张图、我们拿到了几张——看起来是个减法,实际很容易做错。

错误做法:

missing = [i for i in range(expected) if i not in have] # ❌

拿「帖子总格数」减「我们有第几格」。问题在于:它从没查过那一格到底是什么媒体。

Instagram 的帖子第 7 格常常是视频,而我们故意不下载视频(下载了也只是封面帧,不是照片)。于是这个判据永远报「缺第 7 张」,而你点开帖子一看——第 7 格确实是个视频,我们一张照片都没缺。

正确做法:对着真值逐格验。

for idx, slide in enumerate(truth_slides):
if slide["type"] == 2: # 视频格:不计入缺口
video_skipped += 1
continue
if idx not in have_idx: # 照片格缺了:这才是真缺
missing.append(idx)

三层分类,必须分清楚:

标记含义要处理吗
complete照片格全在不用
video_skipped帖内视频位,故意没下不用,视频不是照片
boundary窗口从这帖中间切开,更老的部分在归档区不用,是设计行为
size-unknown没有元数据,判不了老实说判不了
missing照片格真缺了这才是要补的

这次实战最严重的一个错:真相表覆盖不到的帖,我的代码走了「判不了」的分支,却把 complete 算成了 True。结果满屏绿标「完整」,真缺的帖子被藏在了「一切正常」里。

铁律:信息不足时必须输出「判不了」,绝不能输出「完整」。用默认值冒充验证通过,比直接报错害人得多。

六、窗口:裁剪 + 钉住#

素材库会长得很大。如果你的目标是「保留最新 N 张」,就需要裁剪——但裁剪有个大坑。

裁剪会误删你之前选过的图。

这次实战的做法:所有「已选」记录单独存一份清单(picked/manifest.json),裁剪时以它为准:

  • 落在窗口内的已选图:正常留着;
  • 落在窗口外的已选图:钉住(pin),不参与任何裁剪。

另外,「移动」而不是「删除」——裁剪掉的东西进 _removed/,随时可捞。素材库不做不可逆操作。

七、分级:给「以后要用」一个出口#

一开始我做的是二元判断:要 / 不要。

问题很快暴露:「不确定但舍不得删」的图没有地方放。 判「要」太满,判「不要」心疼。

于是改成分级:

S = 顶(首选素材)
A = 高(优质备用)
B = 中(有意思,留着)
C = 不用(判过了,文件还在)

关键设计:C 不代表删除。 文件一直都在,只是「判过了」。这样你可以放心大胆地判,不会有亏损感。

另外,已选的图要锁死——分级界面里它们只读,方向键按不动。不然你手滑一下,之前的选择就变了。

八、验收:不要相信「看起来能跑」#

这一节是旧文里另一块值钱的东西。

「看起来能跑」是最不可靠的信号。三重校验,缺一不可:

  1. 截图——看视觉;
  2. 推进——很多站有加载动画,静态截图只能拍到一片黑,要用滚轮事件驱动;
  3. DOM 校验——截图小不等于页面坏了,WebGL 在无 GPU 环境里不画,但结构是对的。
Terminal window
chrome --headless=new --dump-dom http://127.0.0.1:8101/ > dom.html

逻辑是:截图看视觉,滚轮推进度,DOM 查结构。

九、踩过哪些坑(都是学费)#

挑几个最典型的,括号里是这次有没有复发:

  • URL 去重 = 重复的根因 → 换成内容 sha256(复发了,这次是反过来:该合的没合)
  • 脚本里写中文路径会乱码(PowerShell 按 GBK 读坏)→ 含中文的脚本一律用 Python
  • 翻页参数被覆盖(page=dapi 被再拼一次覆盖成 HTML)→ 改用 pid
  • 限流的三种形态要认清(复发了)
  • cookie 写过期的值会静默失效(写 1 会报 “expired at 1970”)
  • 账号存在 ≠ 有作品(很多号只是注册了,几乎没发图)
  • 大库会让管理工具卡死(几万张大图建预览会超时)→ 用轻量小目录,别让工具处理全库
  • 零结果写表 = 埋雷(这次新踩的)

十、这篇文章想说的#

如果只留一句话,我会说:抓上游源,别扒单个站。

但这次我更想说的是另一件事。

上面这些教训,我都写过。一个月前就写过。然后我让另一个 AI 去干同样的事,它把每一条都原样踩了一遍——而我当时没想起这篇文章。

所以经验这件事,不是「有」就行了。得放在够得着的地方,得在开工之前读一遍。

这份方法论现在的地位变了:它不再是一个项目的记录,它就是这套活该怎么干的说明书。以后要干这个,先读它。

剩下的几条原则,更像是给自己立的规矩:

  • 先研究再动手:把渠道逐个实测(连通 / 可批量 / 限流),挑出能跑的,而不是一看到就冲。
  • 内容去重 + 可追溯:绝不重复入库,且每张图都能溯源。
  • 工具要匹配对象:Node 抓某个源超时就换 PowerShell;本机被风控标记就走代理出口;长跑配看护。
  • 先给用户看,再放量:先出一小批真成果,风格质量对味了,点头之后再规模化。
  • 进度诚实:没验证的事不说成已验证,没修的 bug 不说成已修。

十一、和「取图」的关系#

这篇文章和 取图 是同一件事的两个尺度。

取图解决的是单次提取:你刷到一组图,把链接丢进去,图摊在桌上,挑几张下载。它是「手边就能用」的那个工具。

这篇文章解决的是批量管线:当你要的不是一组,而是某个来源的几百上千张,而且要求去重、可追溯、可重跑——取图那种交互式的做法就不够了,你需要一套能跑一整夜、出错了能重来的管线。

两者本质是同一件事:把散落在各个 App 里的图,收到自己能掌控的地方。 差别只是规模。

如果你只是偶尔收图,取图 就够了。如果你要做素材库,从这里往下。


工具清单#

旧文那套工具在 D:\Saki\design-lib\(38 个脚本,逆向站复刻用),这篇讲的是采集侧的,在 .ai-work/ 下:

用途脚本
全量真值扫描hy_sweep.py
缺口权威审计hy_audit.py
真相表校验hy_verify_truth.py
按窗口补口hy_fetch800.py
合格性验收hy_accept.py
窗口裁剪hy_revert_overfetch.py
最终检查hy_final_check.py

全部可复用,改改入口就能指向别的账号或别的平台。


本站协议见 规矩。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
一个月前我写过一篇采集方法论,然后我把它忘了
https://stulanez.com/posts/capture-pipeline-methodology/
作者
Saki
发布于
2026-10-18
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Saki站长
一位 ARMY 和 FEARNOT。
公告NOTICE
FROM STULANEZ

欢迎来到永恒欲望。这里刚刚上线,之后会慢慢写下想留下的东西。

分类
标签
现在UTC+8
站点统计LIVE
分类
5
标签
27
总字数
13,971
运行时长
0 天
最后活动
0 天前
时间进度2026 / JOURNEY
本年82%

本年还剩 66 天

本月85%

本月还剩 5 天

本周18%

本周还剩 6 天

距离下一站 · 元旦

2027-01-01
66天
站点信息ONLINE
构建平台
Vercel
博客版本
Firefly vunknown
文章许可
CC BY-NC-SA 4.0