尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
画师作品批量归档:Python爬虫登录态与传输层稳定实践
批量归档自己关注的画师作品这件事听起来简单真正动手就会发现问题一堆登录窗口的人机校验卡在第一步、跑着跑着连接层抛 SSL 异常、请求发得稍密一点列表接口就开始返回空数据、下载到一半断了又得从头再来。这篇讲的就是我给自己搭的这套插画归档工具从某日系插画社区下文统一叫“图站”的关注列表出发把关注画师的全部公开插画批量拉到本地按目录整理好。整套东西不复杂但踩过的坑足够写一篇登录态怎么持久化、人机校验环节怎么处理才不越线、SSL 报错到底该看哪一层、限速和重试怎么设计才不至于把自己搞得寸步难行。适合已经会写点 Python、想拿一个真实项目练手爬虫工程化的朋友也适合只是想把自己收藏夹备份下来、不打算深究原理的使用者——后半部分的实操步骤可以直接照抄。1. 这套工具到底要解决什么问题1.1 从“手动另存为”到关注列表全量归档我最初的动机非常朴素关注列表里有几百个画师每个画师多则上千张作品少则几十张。想按画师分目录存档手动点是不现实的一张一张右键另存为光是改名就能把人逼疯。更麻烦的是有些作品是多页的一张作品下面挂着十几张图手动存的时候极容易漏页事后翻回去核对又是一场灾难。所以目标很明确输入一个账号的关注列表输出一个按画师组织的本地目录树每个画师目录下是该画师的全部公开作品多页作品按页序排列文件名携带作品 ID 方便日后去重和溯源。这件事的价值不止于“省事”。当你把几百个画师的作品结构化地落到本地之后可以做很多二次的事情按标签统计自己偏好的题材分布、按时间线看某个画师风格演变、给自己做一个离线检索的小索引。我在做完第一版之后又顺手加了个按作品标签生成索引的功能找参考图的速度提升非常明显。所以这套东西的定位不是“爬虫练习”而是一个长期可维护的个人数字资产归档系统这个定位决定了后面所有的设计取舍——我要的不是跑一次就完事而是三个月后重新跑一次还能增量更新、不重复下载、不把已有的目录搞乱。1.2 三个硬骨头登录态、人机校验、传输层报错真正写起来才会发现难点根本不在“发请求解析 HTML”。这个站点的公开作品信息大部分不需要登录就能看但关注列表属于账号私有数据必须登录后才能拿到。于是第一道坎就是登录态你不能每次跑都弹一次登录窗口手动点那就失去了自动化的意义。第二道坎是人机校验登录环节偶尔会触发这是个必须正面处理但绝不能越界的问题。第三道坎是传输层的报错SSL 握手失败、连接被重置、读取超时这些错误在批量请求场景下出现频率远高于单次请求如果不做统一的错误处理和重试策略整个任务跑到一半卡死是常态。这三个问题之外还有一层隐性的坎请求节奏。站点对高频请求是有防护的短时间内密集请求同一个接口轻则返回空结果重则让你的会话直接失效前面的登录态白做。所以整套工具的核心其实不是“怎么抓”而是“怎么稳定地、长期地、不惹麻烦地抓”。我把这三块拆成独立模块分别处理互不干扰会话模块管 Cookie 和请求头传输模块管重试、超时和错误分类调度模块管限速和任务队列。这样任何一块出问题定位范围都很小。1.3 先把边界划清楚只碰自己有权限看的东西这一点我必须放在前面讲因为它决定了后面很多技术选择的方向。这套工具的全部职责是把我自己账号能看到的内容复制一份到我自己的硬盘上。它不碰付费内容、不碰需要额外权限才能访问的内容、不绕过任何访问控制、不对外分发任何抓下来的东西。基于这个前提我在设计上做了几条硬性约束不做自动化的登录流程登录这一步必须由真人完成一次不做验证码的自动识别或破解遇到校验就停下来交给人不做任何并发压榨请求间隔按秒级算宁可跑一晚上也不要在十分钟内把对方服务器打出一堆 4xx。这几条约束听起来像是“自缚手脚”实际上反而让整个工程更省心。因为一旦你放弃“全自动无人值守”这个不切实际的目标登录态的维护就变成了一个简单的持久化问题人机校验就变成了一个人工介入的检查点你省下的是大量反检测特征的调试时间。我见过太多人在这上面死磕最后写出来的代码脆弱得像纸糊对方一个小改动就全盘失效。承认“有些环节必须人工”工程复杂度会断崖式下降。2. 整体架构与方案选型2.1 分层设计会话层、调度层、解析层、落盘层整套工具我拆成四层每层职责单一方便单独调试。会话层负责创建和复用 HTTP 会话对象、加载 Cookie、维护请求头调度层负责控制请求节奏、管理重试、维护一个待处理队列解析层负责从 JSON 或 HTML 里提取画师 ID、作品 ID、页数和图片直链落盘层负责目录创建、文件命名、断点续传和去重。四层之间通过简单的数据对象传递不用框架纯 Python 脚本加几个类就够。为什么不用现成的爬虫框架我试过一个主流框架配置起来反而更绕而且它的调度模型面向大规模分布式场景我这种单机、低并发、需要频繁人工介入的任务用不上。自建四层的好处是每层都能单独跑我会写一些小脚本专门测解析层拿一段保存下来的响应离线跑不用每次都真发请求调度层的限速逻辑也可以用一个假的请求函数测。这种可测性在后期维护时价值极大站点结构一变我只需要改解析层其余三层完全不动。2.2 为什么不做全自动登录标题里“模拟登录”这个词容易引起误解我说的“模拟登录”不是指程序自动填账号密码、自动过校验而是指把浏览器里已经完成的登录结果搬到程序里复用。具体做法是用带界面的浏览器自动化工具打开登录页由你本人手动完成登录包括可能出现的人机校验登录成功后把浏览器的 Cookie 全部导出来存成本地文件。程序每次启动时加载这份 Cookie直接带着它发请求相当于“借用”你已经登录的会话。这个方案的优点是显而易见的登录环节的所有复杂度包括那些人眼一看就过、程序一看就懵的校验环节都留在了浏览器里程序只处理纯 HTTP 请求。缺点也明确Cookie 会过期过期了就得重新来一次。所以我在工具里加了一个“健康检查”步骤——启动时先用会话请求一个需要登录才能访问的轻量接口如果返回的是登录页或者 401就提示“登录态失效请重新导出 Cookie”而不是让程序带着废 Cookie 跑一晚上然后发现自己下了一堆登录页的 HTML。2.3 存储结构与命名规则的设计目录结构我改过三版最后定下来是这样archive/ artists.json # 画师元信息索引 artworks.db # 已下载作品记录SQLite 1001_某某画师/ avatar.jpg 120000001_p1.jpg 120000001_p2.jpg 120000002_p1.jpg画师目录名用“画师 ID 下划线 画师名”的组合。为什么加 ID 前缀因为画师改昵称是常事如果只用名字做目录改一次名就会多出一个新目录历史文件全散架。用 ID 做前缀名字只是给人看的程序认 ID改名时只需要把目录重命名一次或者干脆新建目录后把文件搬过去去重逻辑不受影响。文件名用“作品 ID 页码 扩展名”。作品 ID 是全局唯一的这就保证了去重的可靠性——判断一个文件是否已下载不需要比对文件内容或大小直接查数据库里有没有这个作品 ID 加页码的记录就行。页码补零到两位保证按文件名排序时页序正确p10排在p2后面的问题补零后p10就是p10p2是p02字典序正确。扩展名保留原图格式不做统一转换因为我需要的是原图备份不是二次压缩。3. 登录态与人机校验环节的处理3.1 手动登录加 Cookie 持久化一次操作长期复用具体流程分三步。第一步用一个带可视化界面的浏览器自动化工具开一个持久化的用户数据目录这个目录的作用是保存浏览器的完整状态Cookie、LocalStorage、缓存下次打开同一个目录登录状态还在。第二步脚本打开登录页后就暂停等你在窗口里完成登录。我用的判断方式是轮询检查当前页面 URL 或者某个“已登录才会出现的元素”一旦出现就认为登录完成脚本继续往下走。第三步登录完成后从浏览器上下文里把所有 Cookie 导出成 JSON按域名分组存好。这里有个细节值得说不要把 Cookie 只存成一条Cookie头字符串。理由是有些站点会同时使用 HttpOnly 的会话 Cookie 和一些普通 Cookie还可能区分不同的子域。存成结构化数据name、value、domain、path、expires之后加载时可以精确还原也能判断哪些快过期了。我用的格式大概是这样{ domain_a: [ {name: session_id, value: xxxx, domain: .example.com, path: /, expires: 1735689600, httpOnly: true} ] }加载到请求库的会话对象里时逐条塞进 cookie jar注意domain字段要正确否则请求根本不会带上这条 Cookie。我第一版就栽在这——导出时把 domain 前面的点丢了结果请求一直返回未登录排查了两个小时才发现是 Cookie 域匹配的问题。3.2 登录态失效的判断与刷新节奏Cookie 的过期时间不一定准服务端可能提前让会话失效比如检测到异地、检测到请求特征异常。所以不能只依赖expires字段做判断必须做实际的健康检查。我的做法是每次任务启动时先用会话请求一个需要登录的轻量接口比如账号自己的基本信息接口检查返回内容里有没有用户 ID 字段。有说明登录态有效没有或者返回了登录页特征说明失效。健康检查还有一个额外好处它顺便验证了传输层是否正常。如果这次请求直接抛 SSL 异常或超时那说明网络环境有问题跟登录态无关错误分类就清晰了。我把这个检查放在任务开始的第一秒成本极低收益极高——省掉了无数次“跑了两小时发现全是废数据”的崩溃。至于刷新节奏实测下来 Cookie 在持续活跃使用的情况下能撑比较久但如果中途停用几天再跑大概率要重新登录。所以我在工具里加了个时间戳记录超过一定天数没跑过就直接提示重新导出不做无谓的尝试。3.3 会话对象复用的两个坑第一个坑是会话复用导致的连接池耗尽。请求库的会话对象默认会维持一个连接池如果你用多线程共享同一个会话对象在高并发下可能出现连接池排队甚至报错。我的应对很简单并发控制在很小的数值后面会讲并且每个线程持有一个独立的会话对象只共享 Cookie 数据不共享连接池。第二个坑是请求头里的某些字段在会话复用时容易被忽略。比如Referer图片直链在有些情况下会校验来源页缺了 Referer 直接返回 403。我在下载图片时固定带上作品详情页的 URL 作为 Referer这个问题就解决了。还有Accept-Language和User-Agent这两个字段在会话初始化时统一设置一次避免每个请求单独传参时漏掉。这些细节单看都很小但组合起来就是“跑得好好的突然就开始 403”的元凶。注意Cookie 文件里包含的是你的账号凭证等同于密码。落地时放在项目目录之外或者加进.gitignore不要随代码一起提交。4. SSL 与传输层报错的排查路径4.1 先把报错分类不要一上来就改代码批量请求跑起来之后控制台里经常是一堆花花绿绿的异常。我的经验是先把它们分成三类分类处理比逐个看堆栈高效得多。第一类是证书验证失败典型报错是SSLCertVerificationError或者CERTIFICATE_VERIFY_FAILED提示unable to get local issuer certificate。这类问题的根源通常在本地的证书链不完整或者 Python 依赖的证书包版本太旧跟目标站点本身没什么关系。第二类是握手阶段的连接中断表现为SSLError伴随EOF occurred in violation of protocol这种常见于连接被中间设备重置或者是服务端主动断掉了长连接。第三类是超时和读取失败ReadTimeout、ConnectionResetError属于纯粹的链路抖动重试就能解决。我把三类的处理策略分开写在了传输模块里第一类先修复环境不做无脑重试因为重试一百次结果一样第二类退避重试同时降低并发第三类直接指数退避重试。这个分类让我的日志清爽了很多出问题时一眼就能看出是环境问题还是网络抖动。4.2 证书问题的定位手法遇到证书验证失败我按这个顺序排查。先确认是不是全局性的——用一个最简单的请求访问任意一个 HTTPS 站点如果也报同样的错那问题百分百在本地环境跟目标站点无关。第二步检查证书包是否最新很多环境里装的是很久以前打包的证书根证书过期了还不自知更新一下就能解决。第三步检查系统的 CA 存储是否被正确读取某些环境下需要显式指定证书文件路径。这里必须强调一句不要用关闭证书校验的方式来“解决”问题。我知道很多教程里一句verifyFalse就带过了但那等于把你的流量暴露在任何中间人面前对于一个要携带登录 Cookie 的工具来说这是极其糟糕的做法。我承认在定位阶段临时关掉校验、看看是不是证书问题是个有用的诊断手段但绝不能留在生产代码里。诊断完立刻关掉并且在代码里留个注释提醒自己。如果确实遇到公司内网自签证书之类的场景正确做法是把那张证书显式加入信任列表而不是一刀切关闭校验。4.3 重试逻辑的写法与参数选择重试不是简单包个for循环参数选不对反而会加剧问题。我用的是带抖动的指数退避基准等待 2 秒每次失败翻倍上限 60 秒最后加一个 0 到 1 秒之间的随机抖动。公式大概是wait min(60, 2 * 2**(n-1)) random.uniform(0, 1)。为什么加抖动因为如果多个线程同时失败没有抖动的话它们会在同一时刻一起重试形成新的尖峰反而更容易被防护机制盯上。抖动把这些重试打散效果立竿见影。重试次数的上限我设成 5 次。超过 5 次还失败说明不是临时抖动可能是会话失效、被封、或者页面结构变了继续重试只是在浪费时间和制造噪音。这时候应该把任务挂起、记录到失败队列、继续处理下一个目标等整轮跑完再统一处理失败队列。这个“失败队列”的设计救了我很多次因为往往跑完一轮之后前面的限速阈值也摸清楚了回头补跑的成功率高得多。错误类型典型报错处理策略重试次数上限证书验证失败SSLCertVerificationError修复本地证书环境不重试0握手中断EOF occurred in violation of protocol退避重试同时降并发3读取超时ReadTimeout指数退避重试5连接重置ConnectionResetError指数退避重试5状态码 429限流响应长退避降速到一半3状态码 403拒绝访问检查 Referer 与 Cookie不盲目重试15. 反反爬用“像人”的方式换取长期可用5.1 请求特征的一致性比花哨的技巧更重要这个话题网上的讨论容易走偏很多人一上来就想着伪造各种指纹、随机化各种头。我的经验恰恰相反请求特征的一致性远比随机化重要。什么叫一致性就是你的 User-Agent、Accept、Accept-Language、Accept-Encoding 这几个字段在整轮任务里保持固定并且跟真实的浏览器一致。如果你每个请求随机换一个 UA一会儿是某版本浏览器、一会儿是另一个平台从服务端的视角看这就是明显的异常——真实用户不会在一秒内换三次浏览器。所以我的做法是选定一组真实的、当前主流浏览器的请求头写死在配置里整轮任务不变。如果任务跨天跑可以换一组但一天之内不换。Cookie、Referer、请求头的组合逻辑也要连贯访问详情页时 Referer 是列表页下载图片时 Referer 是详情页这个链条符合真实浏览行为。这些细节加起来比任何“高科技”手段都管用。5.2 限速参数的推算是怎么来的限速不是拍脑袋定的。我的推算逻辑是这样先搞清楚任务总量比如 500 个画师、平均每人 300 个作品、平均每个作品 1.5 页图。那么作品列表请求大约 500 次每人分页可能多次算 1000 次图片请求约 225000 次。如果目标是 8 小时内跑完平均每秒要发 7.8 个请求。这个速率对图片 CDN 来说可能勉强能接受但对 API 接口来说绝对太快。所以我把两类请求分开限速API 接口关注列表、作品列表控制在每分钟 20 次以内也就是间隔 3 秒左右图片 CDN 放开一些间隔 0.3 到 0.5 秒并发 2 到 3 个。这样算下来主要瓶颈在 API 那一侧1000 次请求需要 50 分钟左右图片下载可以并行铺开。整个任务跑一晚上的时间完全够用而且峰值压力很小。宁可慢也不要快——快一小时对你没有任何意义被封一天才是真的损失。并发数我压得很低最多 3 个线程处理图片下载。原因是并发一高退避和限速的逻辑就会互相干扰出问题时也难定位。3 个线程对图片下载来说已经足够了因为瓶颈在网络往返而不是 CPU。5.3 状态码要当语义读不要当错误读这是我认为最有价值的一条经验。很多人写爬虫看到非 200 就一律当错误重试。实际上状态码是在告诉你信息你得读它。返回 429 是在明确告诉你“太快了”这时候正确的反应是大幅降速并延长退避而不是重试返回 403 往往意味着你的请求特征不对缺 Referer、Cookie 失效、UA 被识别重试一百次也没用返回 404 可能是作品已被删除应该直接标记跳过而不是反复请求返回 200 但内容是空的列表这在图站很常见往往意味着分页到底了或者触发了某种软限制需要区分对待。我专门写了一个状态码处理函数把每个码映射到具体动作而不是统一的raise_for_status。这个函数大概是这样def handle_response(resp, task): code resp.status_code if code 200: return ok if code 404: return gone # 作品已删除标记跳过 if code 403: return forbidden # 特征问题挂起待人工检查 if code 429: return slow_down # 限流全局降速 if 500 code 600: return retry # 服务端问题退避重试 return unknown有了这个映射调度层就能根据返回值做全局决策比如收到一次slow_down就把全局间隔乘以 1.5收到forbidden就暂停整个任务并打印诊断信息。这种“读状态码做决策”的模式让工具在遇到软限制时能自己收敛而不是一路撞墙。6. 完整实操流程与核心环节6.1 环境准备与依赖清单环境用 Python 3.10 以上依赖不多requests做 HTTP 会话urllib3处理重试playwright负责那个手动登录窗口和 Cookie 导出tqdm给个进度条纯属心理安慰长任务没进度条真的难受。数据库用标准库自带的sqlite3不需要额外装。pip install requests urllib3 playwright tqdm playwright install chromium装完之后先跑一个连通性测试确认能正常访问目标站点的公开页面。这一步很重要如果这一步就报 SSL 错误说明是环境问题先把环境修好再往下走别在业务代码里找原因。6.2 关注列表的抓取与分页处理关注列表接口通常是分页的需要先拿到总数或者总页数然后逐页请求。这里的注意点是分页参数的类型——有些站点用offset加limit有些用page加per_page还有些用游标返回结果里带一个next_cursor。看图站的实际响应来判断别凭经验硬猜。抓取逻辑我写成这样先请求第一页从响应里解析出总页数然后循环请求后续页每页之间固定睡 2 到 3 秒。如果某一页返回了空数组但总页数还没到就认为触发了软限制主动延长等待到 10 秒再重试一次还是空就跳出并记录。这个“空页判定”非常关键因为返回 200 加空数组是最容易被忽略的软限制信号如果不处理你会静悄悄地丢掉大量数据最后发现关注列表只抓到了一半。def fetch_following(session, user_id): page 1 result [] while True: resp session.get(API_FOLLOWING, params{user_id: user_id, page: page}, timeout20) action handle_response(resp, None) if action ! ok: break items resp.json().get(items, []) if not items: time.sleep(10) resp session.get(API_FOLLOWING, params{user_id: user_id, page: page}, timeout20) items resp.json().get(items, []) if not items: break result.extend(items) page 1 time.sleep(random.uniform(2.0, 3.0)) return result6.3 作品列表与多页作品的解析拿到画师 ID 之后逐个拉作品列表。这里有个容易踩的坑作品列表里返回的图片信息往往不包含全部页有些站点只给第一页的缩略图完整页列表需要再请求一次作品详情。我一开始就是只用了列表里的数据结果下载下来全是只有第一页的残图还以为是站点限制了绕了一大圈才发现要二次请求详情。详情请求要不要单独限速要。我把它和列表请求放在同一个限速桶里共享那个每分钟 20 次的上限。因为详情请求的数量跟作品数同级如果放开请求量会翻倍。解析出来的数据结构我统一成一个字典包含作品 ID、页数、每页的图片直链、标签、发布时间。图片直链里往往带一个尺寸参数比如_master1200或者_p0这种后缀要保留原样不要自己拼接——自己拼很容易拼错路径规则直接用它给的链接最稳。6.4 下载与断点续传的正确姿势下载环节我踩过最惨的坑是“文件写了一部分然后程序崩溃”留下一个半截的图片文件下次跑的时候因为文件名已存在就跳过了于是本地永久存了一张坏图。解决办法是标准的临时文件加原子重命名先写到xxx.jpg.part写完之后校验一下接收到的字节数是否等于响应头里的Content-Length一致才os.replace成正式文件名。这样任何中途崩溃都不会污染正式文件下次跑还会重新下。def download(session, url, dest, referer): part dest.with_suffix(dest.suffix .part) dest.parent.mkdir(parentsTrue, exist_okTrue) with session.get(url, streamTrue, timeout60, headers{Referer: referer}) as r: r.raise_for_status() expected int(r.headers.get(Content-Length, 0)) written 0 with open(part, wb) as f: for chunk in r.iter_content(64 * 1024): if chunk: f.write(chunk) written len(chunk) if expected and written ! expected: part.unlink(missing_okTrue) raise IOError(f字节数不符 {written}/{expected}) os.replace(part, dest)区块大小选 64KB 是个折中太小会导致写盘次数过多太大在慢链路上不好控制进度。校验Content-Length这一步不能省尤其是图片这种二进制文件截断了肉眼看不出问题打开才发现下半截是灰的。6.5 增量更新与去重去重我用 SQLite 存一张表主键是(artwork_id, page)。每次下载前查一下是否存在存在就跳过。这样第二次运行的时候只处理新增的作品几百个画师的列表请求照发因为要知道有没有新作品但图片请求几乎为零几分钟就跑完。表结构很简单CREATE TABLE IF NOT EXISTS downloaded ( artwork_id TEXT NOT NULL, page INTEGER NOT NULL, artist_id TEXT NOT NULL, path TEXT NOT NULL, size INTEGER, ts INTEGER, PRIMARY KEY (artwork_id, page) );用数据库而不是“检查文件是否存在”的好处是文件可能被你手动删掉、移动、改名但数据库里有记录你能知道曾经下过什么、路径原本是什么。我通常会定期做一个一致性检查用数据库里的记录去核对磁盘上的文件发现缺失的就标记成待重下。这个检查脚本只有二十来行但非常实用。7. 常见问题速查与踩坑实录7.1 症状与处理对照表症状大概率原因处理办法启动即报证书错误本地证书包过旧或不完整更新证书包检查系统 CA 配置所有请求都返回登录页Cookie 过期或域名匹配错误重新导出 Cookie核对 domain 字段图片全变 403缺 Referer 或 UA 被识别补上详情页 Referer固定请求头列表抓一半变空触发软限制延长等待降低全局速率图片是灰色的半截下载中断未校验加 Content-Length 校验与原子重命名目录里出现重复画师用了昵称做目录名改用 ID 做前缀程序卡死不退出超时未设置所有请求强制 timeout进度条卡住不动单线程阻塞在某个下载加单任务超时超时转入失败队列7.2 几个没写在任何文档里的细节第一个目录里的文件数量太多会导致某些操作变慢。几百个画师如果都平铺在一个目录下还好但单个画师上千个文件时文件管理器打开会明显卡顿。我的处理是把单个画师的文件量做软上限超过之后按年份分子目录。这个改动不大但日常使用体验好很多。第二个不要相信任何一次跑完的结果。长任务我习惯在结束之后做一次完整性抽查随机抽十个画师核对数据库记录数跟目录文件数是否一致。有偏差就说明中途出了静默错误这种错误不会报异常只会悄悄少文件不抽查根本发现不了。第三个日志要记请求 URL但不要记完整 Cookie。排查问题时最需要知道的是“哪个 URL 在什么时候返回了什么码”而 Cookie 属于敏感信息日志里写出去就有泄漏风险。我的日志格式是时间 | 状态码 | 耗时 | URL足够了。第四个多页作品下载时页与页之间加一个小延迟0.2 秒。因为多页作品的图片往往在同一个 CDN 上连续请求不加延迟时瞬时并发看起来很不自然。加了之后 403 的比例明显下降。7.3 关于“别把工具用成负担”的一点体会我一开始特别执着于“全自动、无人值守、跑完自动通知”。为此折腾了很久的自动登录、自动重试、自动恢复。后来发现这套东西的维护成本远高于它节省的时间——每次站点改点什么我就得花一晚上修修完还提心吊胆地怕哪里埋了雷。后来我把它改成“半自动、有人看着、出错就停”的模式反而用得最舒服。每周找一次空闲时间手动登录一次导一次 Cookie启动任务跑的过程中偶尔瞄一眼日志。真出问题了停下来看看改一改再跑。这种节奏下工具回归到它应有的位置——一个帮我省力的东西而不是一个需要我伺候的东西。如果你也打算搭类似的系统我建议一开始就接受“半自动”这个设定把精力花在错误分类、断点续传、增量更新这些真正提升可靠性的地方而不是花在跟登录环节死磕上。前者让你的工具越用越稳后者让你的工具越改越脆。
RELATED

相关推荐

KV Cache技术:大模型推理加速的核心机制与优化实践

KV Cache技术:大模型推理加速的核心机制与优化实践

1. KV Cache技术解析:大模型推理加速的核心机制 KV Cache(Key-Value缓存)是Transformer架构在推理阶段实现高效计算的关键技术。这项技术通过缓存中间计算结果,显著减少了自回归生成过程中的重复计算。在GPT、LLaMA等主流大语言模…

📅 2026/10/31 12:58:22
AI Agent 刹不住车?权限边界与中断机制详解

AI Agent 刹不住车?权限边界与中断机制详解

那天我在调试一个信息检索类的 Agent 任务,让它去查几条公开公示信息,回来整理成摘要。终端里调用搜索工具的日志刷得飞快,我心想“够了,停下”,手指已经按了中断键,嘴上喊完刹车,结果回头一看日…

📅 2026/10/31 12:58:22
OpenRig:面向生产环境的AI智能体协作操作系统

OpenRig:面向生产环境的AI智能体协作操作系统

1. OpenRig 不是又一个“Agent 框架”,而是把 AI 智能体当人用的协作操作系统我第一次在 GitHub 上看到 OpenRig 的 README 时,下意识划了过去——又一个带“Rig”后缀的 Rust 项目,八成是封装了 LLM 调用、加了点状态管理,再套个…

📅 2026/10/31 12:58:22
MORE NEWS

更多资讯

📰

AI云账户系统后端源码拆解:银行科技岗Java实战

简介:这套《基于Java的银行科技岗AI云账户系统后端设计源码》面向银行科技岗开发者与金融科技学习者,围绕AI云账户系统的设计落地,完整覆盖转账、对账、充值、提现等关键业务,并保留了后续接入AI能力的扩展空间。资源包共139个文件…

📰

回文数与罗马数字题的底层数据思维解析

1. 为什么“回文数”和“罗马数字转整数”被并列安排在刷题第二天?刚接触LeetCode的新手常会疑惑:为什么平台把第9题“回文数”和第13题“罗马数字转整数”放在同一天训练?它们看起来毫无关联——一个考数字对称性,一个考字符映射…

📰

AI编程时代的需求工程方法论与实践

1. AI编程时代的需求分析变革 当GitHub Copilot能自动补全整段代码、Cursor可以理解自然语言需求时,传统"产品经理写PRD→开发人员解读"的协作模式正在被颠覆。最近用AI工具重构一个电商系统时,我发现需求阶段的对话质量直接决定了最终代码的准…

📰

深入解析开闭原则:面向对象设计的核心思想与实践

1. 开闭原则的本质解析 开闭原则(Open/Closed Principle, OCP)作为面向对象设计的五大SOLID原则之一,其核心思想可以概括为:软件实体(类、模块、函数等)应当对扩展开放,对修改关闭。这个看似矛盾…

📰

没有浮力公式也能造船:吃水、船模试验与比例放大的工程智慧

做船模这些年,被问得最多的一个问题不是“这条船好不好看”,而是“古人没有浮力公式,凭什么敢造那么大的船”。这个问题每次都能把我拉回最初接触传统造船的那段时间。那时候我跟着老师傅学放样,一边削样板一边听他念叨“船吃水多…

📰

AI原生SDLC设计阶段:从需求到可执行提示词的工程实践

1. AI 原生 SDLC 中的设计环节到底在解决什么问题1.1 从“写代码”到“设计意图”的范式转移传统软件开发生命周期里,设计阶段往往被压缩成几页文档、几张架构图,然后大家就急着进入编码。但在 AI 原生 SDLC 的语境下,设计环节的权重被大幅拉…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬