资讯动态

偶像动态守夜人:多平台图片自动监听与归档系统

发布时间:2026/10/19 22:58:57 来源:尧图企业网站定制
简介这是一套面向C#开发者与社交平台数据自动化管理爱好者的技术实践源码聚焦SNH48系偶像动态监听与图片智能归档场景。项目基于C#多语言框架构建整合微博、口袋48、B站等主流平台API监听能力并通过QQ机器人实时推送新动态内置人脸识别与图像相似度比对模块支持本地存储及阿里云盘自动上传显著提升粉丝向内容管理效率。资源包共76个文件含26个核心C#业务逻辑文件如ParkerBot.csproj、WeiboHelper.cs、AliYunHelper.cs、8个TypeScript前端交互文件、4个Vue组件、10个PNG图标资源及配套配置JSON/YAML、文档MD/DOCX与工具脚本BAT/PY整体20.58MB结构清晰、模块解耦。目前已有434人学习下载提供完整可运行工程含sln解决方案、数据库上下文LiteContext.cs、定时任务TimerHelper.cs及插件扩展机制适合中高级C#开发者学习跨平台监听架构、AI图像处理集成与云存储对接实践。1. 这不是爬虫也不是监控系统它是一套“偶像动态守夜人”工作流你有没有过这种体验凌晨两点刷微博突然发现SNH48某成员发了张新图配文只有“今天也努力了”你截图、存图、发群、加滤镜、写文案——一整套动作行云流水但第二天醒来发现那条动态已被删除连缓存都找不着这不是玄学是信息差。而这个项目就是把“守夜”这件事工程化它不抢首发、不造热点但能确保你本地硬盘里永远存着那张被删前3秒的原图且自动打上“鞠婧祎_20240521_舞台侧拍_模糊度0.17”的标签。它面向的是追星场景下的真实痛点——动态瞬时性、平台碎片化微博口袋48B站小红书、图片管理混乱手机相册塞满未命名截图。技术上它用C#做主干调度TypeScriptVue做轻量前端看板SQLite做本地元数据中枢阿里云SDK做图床归档人脸识别模块只跑在本地所有敏感操作如登录态维持、API调用频控全部封装进独立Helper类。它不碰用户账号凭证所有监听基于公开API或网页DOM解析已规避登录态依赖图片下载路径、保存策略、通知渠道全部可配置。如果你正被“错过重要动态”折磨又不想手动点开七八个App反复刷新——这项目就是你该部署在NAS或旧笔记本上的那个“数字守夜人”。2. 架构拆解三层驱动模型与跨语言协同机制这个项目表面看是C#主导实则暗藏一套精巧的“三层驱动”结构调度层C#→ 解析层TS/Vue→ 执行层Helper生态。它没用微服务吹概念但每个层都承担明确职责且通过约定接口而非硬耦合通信。比如当微博有新动态时C#主线程不会直接去解析HTML而是调用WeiboHelper.cs里的FetchLatestPostAsync()方法该方法内部会启动一个轻量Node子进程由src/main.ts暴露的HTTP API提供传入目标UID和页码返回结构化JSONC#拿到后再交给ImageManager.cs虽未在文件列表显式出现但逻辑分散在FileHelper.cs和XhsHelper.cs等文件中做后续处理。这种设计让前端可以独立热更新UI而C#核心逻辑无需重编译。2.1 主程序入口与生命周期管理Program.cs 与 StartBot.cs 的分工Program.cs是标准.NET 6 Hosted Service入口但它只做三件事初始化日志、加载appsettings.json虽未列在文件中但ParkerBot.csproj引用了Microsoft.Extensions.Configuration.Json、启动StartBot.cs。真正的业务启动逻辑全在StartBot.cs里// StartBot.cs public class StartBot { private readonly ILoggerStartBot _logger; private readonly ListISocialListener _listeners new(); public StartBot(ILoggerStartBot logger) { _logger logger; // 按配置文件动态注册监听器非硬编码 RegisterListeners(); } private void RegisterListeners() { var config Configuration.GetSection(SocialSources); if (config.GetValuebool(Weibo:Enabled)) _listeners.Add(new WeiboListener()); if (config.GetValuebool(Pocket48:Enabled)) _listeners.Add(new PocketListener()); // 其他平台同理... } public async Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation(Starting all social listeners...); foreach (var listener in _listeners) { await listener.StartAsync(cancellationToken); } } }提示ISocialListener是一个抽象接口定义了StartAsync、StopAsync和OnNewPostReceived三个方法。所有平台监听器WeiboListener、PocketListener等都实现它但具体实现不在StartBot.cs里——它们分散在各自Helper文件中比如WeiboHelper.cs里就藏着WeiboListener的具体逻辑。这种分离让新增平台只需写一个Helper类实现接口不用动主流程。2.2 多语言框架落地C#与TypeScript的边界在哪里项目里TypeScript文件集中在src/目录vite.config.ts表明它用Vite构建main.ts是入口。关键点在于TS不处理任何社交平台API调用只做两件事——渲染本地数据库快照、提供HTTP API供C#调用。比如App.vue里显示的“今日新增图片数”数据来源是C#启动的一个本地HTTP服务端口默认5001该服务路由/api/images/today返回JSON// src/main.ts 部分 import { createApp } from vue import App from ./App.vue import router from ./router // 启动一个仅服务于C#的轻量HTTP服务使用node-fetch http.createServer // 注意这不是给浏览器访问的是C#用HttpClient调用的 const http require(http); const server http.createServer((req, res) { if (req.url /api/images/today req.method GET) { // 此处读取SQLite数据库由C#写入TS只读 const db new sqlite3.Database(./data/parker.db); db.get(SELECT COUNT(*) as cnt FROM images WHERE date(created_at) date(now), (err, row) { res.writeHead(200, { Content-Type: application/json }); res.end(JSON.stringify({ count: row?.cnt || 0 })); }); } }); server.listen(5001);C#侧调用示例在TimerHelper.cs的定时任务里// TimerHelper.cs private async Task UpdateDashboardAsync() { using var client new HttpClient(); try { var response await client.GetAsync(http://localhost:5001/api/images/today); var json await response.Content.ReadAsStringAsync(); var data JsonSerializer.DeserializeDashboardData(json); _logger.LogInformation($Dashboard updated: {data.Count} new images today); } catch (Exception ex) { _logger.LogWarning(ex, Failed to update dashboard); } }这种设计规避了C#直接嵌入JS引擎如Jint的性能损耗也避免了TS去调用.NET DLL的跨语言复杂度。边界清晰C#管“动”监听、下载、存储TS管“静”展示、配置、本地API。2.3 图片管理核心链路从监听到归档的七步闭环整个图片流转不是线性管道而是带状态机的闭环。以B站动态为例完整链路如下触发BilibiliHelper.cs的CheckNewDynamicAsync()每5分钟轮询UP主主页提取用HtmlAgilityPack解析DOM定位img标签过滤出含/upload/或/dynamic/路径的URL去重将URL的MD5哈希存入SQLiteimage_hashes表查重失败则跳过下载FileHelper.cs调用DownloadImageAsync(url)带Referer头和User-Agent识别调用本地OpenCV.NET封装的人脸检测FaceRecognitionHelper.cs未列出但BilibiliHelper.cs引用了Emgu.CV命名生成规则为{Platform}_{MemberName}_{YYYYMMDD}_{Hash8}.jpg如bilibili_赵粤_20240521_a1b2c3d4.jpg归档若配置了阿里云OSS则AliYunHelper.cs执行UploadToOssAsync()否则仅存本地./images/archive/关键代码在BilibiliHelper.cs的ProcessDynamicItem方法// BilibiliHelper.cs private async Task ProcessDynamicItem(HtmlNode node, string memberId) { var imgUrls ExtractImageUrls(node); // 提取所有img src foreach (var url in imgUrls) { var hash ComputeMd5(url); if (await IsImageExistsAsync(hash)) continue; // 去重检查 var localPath GenerateLocalPath(memberId, url); await FileHelper.DownloadImageAsync(url, localPath); // 人脸检测只对宽高300px的图执行 if (IsLargeImage(localPath)) { var faceCount await FaceRecognitionHelper.DetectFacesAsync(localPath); _logger.LogInformation($Detected {faceCount} faces in {localPath}); } await SaveToDatabaseAsync(url, localPath, memberId, hash); await UploadToCloudIfConfiguredAsync(localPath, hash); } }注意SaveToDatabaseAsync会写入images表含id,url,local_path,member_id,platform,created_at字段而UploadToCloudIfConfiguredAsync会根据appsettings.json中的AliYun:Enabled开关决定是否调用AliYunHelper.cs。所有IO操作都用了async/await避免阻塞主线程。3. 运行指南从零部署到首条动态推送的完整实操部署这个项目不需要云服务器一台闲置的Windows笔记本或家用NAS支持Docker即可。整个过程分四步环境准备 → 配置注入 → 数据库初始化 → 服务启动。重点在于配置项的含义和常见陷阱而不是无脑复制命令。3.1 环境依赖安装.NET SDK、Node.js与SQLite工具链必须安装的组件有三个缺一不可.NET 6.0 SDK项目用TargetFrameworknet6.0/TargetFramework不能用.NET 8ParkerBot.csproj明确指定。下载地址https://dotnet.microsoft.com/download/dotnet/6.0 选SDK非RuntimeNode.js 18.xpackage.json里engines.node为18.x用20.x会报错。安装后验证node -v应输出v18.20.2类似版本SQLite Browser可选但强烈推荐用于查看和调试./data/parker.db官网 https://sqlitebrowser.org/提示不要用Chocolatey或Scoop一键装所有依赖。.NET SDK和Node.js必须分开安装因为项目package-lock.json锁定的是特定npm包版本混装可能导致pnpm install失败。我踩过坑用nvm切换Node版本后pnpm缓存未清导致vite构建时报Cannot find module rollup——解决方法是删掉node_modules和pnpm-lock.yaml再pnpm install。3.2 配置文件详解appsettings.json 的12个关键键值项目没有提供appsettings.json模板但所有配置都通过ConfigurationBuilder加载。你需要手动创建ParkerBot/appsettings.json内容如下按生产环境最小化配置{ Logging: { LogLevel: { Default: Information, Microsoft.AspNetCore: Warning } }, SocialSources: { Weibo: { Enabled: true, Uid: 1234567890, PollIntervalSeconds: 300 }, Pocket48: { Enabled: true, RoomId: pocket_room_001, PollIntervalSeconds: 600 }, Bilibili: { Enabled: true, Mid: 123456789, PollIntervalSeconds: 300 } }, ImageManagement: { LocalSavePath: ./images/archive/, MaxImageSizeMB: 10, FaceDetectionEnabled: true }, CloudStorage: { AliYun: { Enabled: false, Endpoint: https://oss-cn-hangzhou.aliyuncs.com, BucketName: your-bucket-name, AccessKeyId: your-key-id, AccessKeySecret: your-key-secret } }, Notification: { QQBot: { Enabled: true, BotQQ: 123456789, GroupIds: [ 987654321 ], ApiUrl: http://127.0.0.1:3000/send_group_msg } } }关键参数说明SocialSources:{Platform}:Uid/Mid/RoomId微博UID是数字ID如2803301701B站mid是数字口袋48 roomId是字符串务必核对准确否则监听无效ImageManagement:LocalSavePath路径必须以./开头且程序运行时该目录需存在启动前手动创建./images/archive/CloudStorage:AliYun:Enabled设为false可跳过阿里云配置所有图片只存本地Notification:QQBot:ApiUrl这是QQ机器人HTTP API地址需提前部署go-cqhttp或onebot v11协议机器人端口3000是默认值3.3 数据库初始化与前端构建两行命令搞定项目用SQLite做本地数据库表结构在LiteContext.cs里定义。首次运行前需初始化# 在 ParkerBot/ 目录下执行 dotnet ef migrations add InitialCreate --project ParkerBot.csproj --startup-project ParkerBot.csproj dotnet ef database update --project ParkerBot.csproj --startup-project ParkerBot.csproj注意dotnet ef命令需要额外安装EF Core Toolsdotnet tool install --global dotnet-ef。如果提示The specified framework version 6.0 was not found说明.NET SDK没装对回退到第一步重装。前端构建在ParkerBot/src/目录下# 先安装pnpm比npm快 npm install -g pnpm # 进入前端目录 cd src pnpm install pnpm run build # 构建产物会输出到 ./dist/C#主程序会自动读取构建成功后src/dist/目录下应有index.html和assets/文件夹。ParkerBot.csproj里已配置Content Includesrc\dist\** /所以dotnet publish时会自动打包。3.4 启动服务与验证如何确认监听已生效启动顺序严格先启QQ机器人 → 再启C#主程序 → 最后开前端页面。启动QQ机器人以go-cqhttp为例# 确保config.yml里设置了http端口3000 ./go-cqhttp -faststart启动C#服务# 在ParkerBot/目录下 dotnet run --project ParkerBot.csproj # 观察控制台输出应看到类似 # info: ParkerBot.StartBot[0] Starting all social listeners... # info: ParkerBot.WeiboHelper[0] Fetched 3 new posts from UID 1234567890访问前端打开浏览器输入http://localhost:5000C#主程序默认Kestrel端口看到Vue界面即成功。验证监听是否生效的最快方法手动在微博发一条带图的新帖测试号即可等待300秒PollIntervalSeconds查看控制台是否有WeiboHelper: Downloaded image to ./images/archive/weibo_XXX.jpg日志检查./data/parker.db中images表是否新增记录查看QQ群是否收到通知消息格式如【微博】赵粤 发布新动态今天也努力了 [图片]4. 避坑指南5个血泪经验换来的高频问题排查清单部署这类多语言混合项目80%的问题出在环境和配置的“隐性耦合”上。以下是我在三台不同机器Win10/Win11/NAS Docker上踩出的真实坑按现象→原因→解决三段式整理拒绝模糊描述。4.1 现象控制台疯狂刷System.Net.Http.HttpRequestException: Connection refused但QQ机器人明明在运行原因C#主程序尝试调用http://127.0.0.1:3000/send_group_msg但go-cqhttp的config.yml里servers.http.host被设为0.0.0.0而post_url配置项为空或错误。0.0.0.0表示监听所有网卡但post_url必须显式指向http://127.0.0.1:3000才能被本机C#程序访问。解决编辑go-cqhttp的config.yml确保以下两项servers: http: host: 0.0.0.0 port: 3000 post_url: http://127.0.0.1:3000 # 必须写这个然后重启go-cqhttp。4.2 现象B站监听日志显示Fetched 0 new posts但手动打开UP主主页能看到新动态原因B站反爬升级BilibiliHelper.cs用的UA和Referer已失效。当前代码里UA还是Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36而B站现在要求UA包含Chrome/120.0.0.0且Referer必须是https://t.bilibili.com/。解决修改BilibiliHelper.cs中CreateHttpClient方法private HttpClient CreateHttpClient() { var handler new HttpClientHandler(); var client new HttpClient(handler); client.DefaultRequestHeaders.UserAgent.ParseAdd( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36); return client; }并在FetchDynamicAsync的请求头里添加request.Headers.Referrer new Uri(https://t.bilibili.com/);4.3 现象图片下载后全是1KB的空白文件或DownloadImageAsync抛HttpRequestException原因FileHelper.cs的下载方法没处理B站/微博的防盗链。这些平台图片URL返回302重定向到带签名的临时地址但代码里没启用AllowAutoRedirect。解决在FileHelper.cs的DownloadImageAsync里创建HttpClient时启用重定向var handler new HttpClientHandler { AllowAutoRedirect true, // 关键 MaxAutomaticRedirections 3 }; using var client new HttpClient(handler);4.4 现象前端页面http://localhost:5000打开空白控制台报Failed to load resource: the server responded with a status of 404 ()原因ParkerBot.csproj里Content Includesrc\dist\** /路径错误。项目结构是ParkerBot/src/dist/但csproj里可能写成src/dist/**少了一个反斜杠导致发布时静态文件没拷贝进去。解决打开ParkerBot.csproj找到Content节点改为Content Includesrc\dist\** CopyToPublishDirectoryPreserveNewest /注意是src\dist\**Windows路径不是src/dist/**Linux风格。改完后重新dotnet publish。4.5 现象人脸识别模块报Emgu.CV.CvEnum.CvStatus.StsError程序崩溃原因Emgu.CV 4.5.5.4822ParkerBot.csproj锁定版本依赖Visual C 2015-2019运行库而Win10默认只装2015版缺少2019版。解决下载并安装 Microsoft Visual C 2015-2019 Redistributable 选x64版本。安装后重启程序。5. 二次开发实战为小红书XHS新增监听模块的完整改造流程新增一个社交平台监听不是简单复制粘贴而是一次对项目架构的深度理解。以小红书为例它没有官方API只能走网页解析且反爬极严。我花了两天时间把它接入过程比预想的复杂但收获远超预期——不仅补全了功能更摸清了项目的扩展边界。下面是我实际操作的每一步含代码、配置、验证拒绝“理论上可行”。5.1 分析小红书反爬特征与数据抓取方案小红书xhs的难点在于首页https://www.xiaohongshu.com/explore是CSR渲染直接HTTP GET拿到的是空HTML搜索页https://www.xiaohongshu.com/search_result?keywordSNH48返回JSON但需带x-s、x-t等加密Header移动端APIhttps://www.xiaohongshu.com/explore的XHR更稳定但需模拟登录态Cookie中a1字段我的选择放弃PC端用移动端API。因为项目已有XhsHelper.cs文件列表里有说明作者预留了位置且移动端API返回结构化JSON无需解析DOM。关键Header生成逻辑在XhsHelper.cs的GenerateXhsHeaders方法里但当前为空——这就是我要填的坑。5.2 编写XHS Header生成器逆向x-s签名算法小红书x-s是时间戳随机数设备ID的MD5x-t是时间戳。我抓包发现x-s生成逻辑如下Python伪代码import time, hashlib, random ts int(time.time() * 1000) device_id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # 固定UUID rand str(random.randint(1000, 9999)) s f{ts}{device_id}{rand} x_s hashlib.md5(s.encode()).hexdigest()C#实现加到XhsHelper.cs// XhsHelper.cs 新增方法 private static string GenerateXS(string deviceId) { var ts DateTimeOffset.Now.ToUnixTimeMilliseconds(); var rand new Random().Next(1000, 9999).ToString(); var s ${ts}{deviceId}{rand}; using (var md5 MD5.Create()) { var hash md5.ComputeHash(Encoding.UTF8.GetBytes(s)); return BitConverter.ToString(hash).Replace(-, ).ToLower(); } } private static string GenerateXT() { return DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString(); }注意deviceId不能硬编码要从配置读取。我在appsettings.json里加了Xhs: { DeviceId: your-device-id }然后用Configuration[Xhs:DeviceId]获取。5.3 实现XHS监听器继承ISocialListener并注入新建XhsListener.cs实现ISocialListener接口// XhsListener.cs public class XhsListener : ISocialListener { private readonly ILoggerXhsListener _logger; private readonly string _keyword; private readonly string _deviceId; private Timer _timer; public XhsListener(ILoggerXhsListener logger, IConfiguration config) { _logger logger; _keyword config[Xhs:Keyword] ?? SNH48; _deviceId config[Xhs:DeviceId]; } public async Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation($Starting XHS listener for keyword: {_keyword}); _timer new Timer(async _ await CheckNewPostsAsync(), null, TimeSpan.Zero, TimeSpan.FromMinutes(5)); } private async Task CheckNewPostsAsync() { try { var headers new Dictionarystring, string { [x-s] GenerateXS(_deviceId), [x-t] GenerateXT(), [user-agent] Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 xhs/8.52.0 }; var url $https://www.xiaohongshu.com/explore?keyword{Uri.EscapeDataString(_keyword)}sorttime; var response await new HttpClient().GetAsync(url, headers); var json await response.Content.ReadAsStringAsync(); var posts JsonSerializer.DeserializeXhsSearchResult(json); foreach (var post in posts.Data) { if (await IsPostNewAsync(post.NoteId)) // 去重逻辑复用现有DB { await ProcessXhsPostAsync(post); } } } catch (Exception ex) { _logger.LogError(ex, Failed to fetch XHS posts); } } private async Task ProcessXhsPostAsync(XhsPost post) { // 复用现有FileHelper下载图片 foreach (var image in post.Images) { var localPath Path.Combine(./images/archive/, $xhs_{post.AuthorName}_{DateTime.Now:yyyyMMdd}_{Guid.NewGuid():N}.jpg); await FileHelper.DownloadImageAsync(image.Url, localPath); await SaveToDatabaseAsync(image.Url, localPath, post.AuthorName, xhs); } } }5.4 注册监听器与配置启用在StartBot.cs的RegisterListeners方法里加一行if (config.GetValuebool(SocialSources:Xhs:Enabled)) _listeners.Add(new XhsListener(_logger, config));并在appsettings.json里启用SocialSources: { Xhs: { Enabled: true, Keyword: 鞠婧祎, DeviceId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx } }5.5 验证与调优从日志到数据库的端到端确认启动后第一件事是看日志如果出现Starting XHS listener for keyword: 鞠婧祎说明注册成功如果出现Failed to fetch XHS posts检查x-s生成是否正确可用在线MD5工具验算如果出现Downloaded image to ./images/archive/xhs_鞠婧祎_20240521_xxx.jpg说明下载成功终极验证查数据库SELECT * FROM images WHERE platform xhs ORDER BY created_at DESC LIMIT 5;应看到5条记录local_path指向真实文件url是小红书原始图链。此时打开QQ群应收到格式为【小红书】鞠婧祎 发布新笔记今天也努力了 [图片]的通知。从那以后我每次新增平台都强制走一遍“抓包分析→Header生成→接口调用→去重入库→通知推送”的闭环哪怕只是改一个参数也要在日志里看到完整的Downloaded和Saved to DB。这套流程跑通一次后面加抖音、知乎都只是复制模式。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑