douban-deep-profile-collect
采集豆瓣当前登录用户的全量个人信息,包括基础资料、看过/想看的电影、读过/想读的书、广播动态
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
采集豆瓣当前登录用户的全量个人信息,包括基础资料、看过/想看的电影、读过/想读的书、广播动态
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
Collect user data from logged-in social platforms (Douyin, Xiaohongshu, Weibo, Douban, Bilibili), cross-analyze to build a precise personal profile, and auto-generate USER.md + MEMORY.md. Use for new user onboarding, personalization, and building user context. 通过用户已登录的社交平台自动采集数据并交叉分析,生成精准用户画像,写入 USER.md 和 MEMORY.md。
采集小红书个人主页的全量信息,包括基础资料、全部发布笔记(含详情正文和标签)、收藏笔记列表(全量)、点赞笔记列表(全量)
采集B站当前登录用户的全量个人信息,包括基础资料、投稿视频、收藏夹内容、关注列表
深度采集抖音个人主页信息,包含基础资料、全部作品(含播放数)、喜欢列表、收藏列表、全量关注列表
采集微博个人主页的全量信息,包括基础资料($CONFIG.user)、全部微博、关注列表、收藏列表
| name | douban-deep-profile-collect |
| description | 采集豆瓣当前登录用户的全量个人信息,包括基础资料、看过/想看的电影、读过/想读的书、广播动态 |
| version | 2.0.0 |
本 Skill 用于采集豆瓣当前登录用户的个人主页全量信息。通过 /mine/ 自动重定向获取用户ID,使用 fetch + DOMParser 在页面内分页采集全量数据(保持 cookie 有效)。
采集内容:
核心技术:fetch + DOMParser 页面内分页 —— 在已登录页面中用 JS 发起 fetch 请求(带 cookie),用 DOMParser 解析返回的 HTML,实现无需翻页的全量数据采集。
⚠️ 执行约束:本 Skill 中的所有 JS 脚本都必须完整复制执行,不可自行简化、改写或省略任何部分。每个脚本都经过实际测试验证,简化会导致数据不完整。
使用 douban-deep-profile-collect 采集我的豆瓣个人主页信息
无需提供参数,自动检测当前登录用户。
无。本 Skill 自动从 /mine/ 重定向检测当前登录用户。
前提条件:浏览器已登录豆瓣账号。
工具:chrome_navigate
参数:
url: https://www.douban.com/mine/说明:/mine/ 会自动重定向到 /people/{doubanId}/,从重定向后的 URL 中提取用户 ID。
工具:chrome_execute_script
参数:
world: MAINtimeout: 5000jsScript:() => {
const url = window.location.href;
const m = url.match(/\/people\/([^\/\?]+)/);
const doubanId = m ? m[1] : '';
const info = {};
const nameEl = document.querySelector('.info h1');
info.nickname = nameEl ? nameEl.textContent.trim().split('\n')[0] : '';
const intro = document.querySelector('#intro_display');
info.intro = intro ? intro.textContent.trim() : '';
const avatar = document.querySelector('.basic-info img');
info.avatar = avatar ? avatar.src : '';
info.doubanId = doubanId;
info.profileUrl = url;
return JSON.stringify(info);
}
说明:提取基础资料和用户 ID(doubanId),后续步骤使用 {step_2.doubanId} 构建分页 URL。
输出:doubanId、nickname、intro、avatar
工具:chrome_navigate
参数:
url: https://movie.douban.com/people/{step_2.doubanId}/collect说明:导航到电影收藏页,作为 fetch 分页的起始页(需要 cookie 域名匹配)。
工具:chrome_execute_script
参数:
world: MAINtimeout: 180000jsScript:async () => {
const doubanId = '{step_2.doubanId}';
const MAX_ITEMS = 500; // 上限500条
const allMovies = [];
const seen = new Set();
let start = 0;
while (allMovies.length < MAX_ITEMS) {
const url = 'https://movie.douban.com/people/' + doubanId
+ '/collect?start=' + start
+ '&sort=time&rating=all&filter=all&mode=list';
try {
const resp = await fetch(url, {credentials: 'include'});
const html = await resp.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const items = doc.querySelectorAll('.list-view .item');
if (items.length === 0) break;
items.forEach(item => {
const titleEl = item.querySelector('.title a');
const dateEl = item.querySelector('.date');
const ratingEl = item.querySelector('[class*="rating"]');
const title = titleEl ? titleEl.textContent.trim() : '';
const href = titleEl ? titleEl.getAttribute('href') : '';
const date = dateEl ? dateEl.textContent.trim() : '';
let rating = 0;
if (ratingEl) {
const m = ratingEl.className.match(/rating(\d)/);
if (m) rating = parseInt(m[1]);
}
const commentEl = item.querySelector('.comment');
const comment = commentEl ? commentEl.textContent.trim() : '';
const tagsEl = item.querySelector('.tags');
const tags = tagsEl ? tagsEl.textContent.trim().replace('标签: ', '') : '';
const key = title + '|' + date;
if (title && !seen.has(key)) {
seen.add(key);
allMovies.push({title, date, rating, comment, tags, href});
}
});
if (items.length < 15) break;
start += 15;
await new Promise(r => setTimeout(r, 800));
} catch(e) { break; }
}
const sampled = allMovies.length >= MAX_ITEMS;
return JSON.stringify({total: allMovies.length, movies: allMovies, sampled});
}
说明:采集看过的电影全量数据。关键技术:
mode=list:列表视图,每页15条,DOM 结构稳定fetch({credentials:'include'}):携带 cookie,保持登录态DOMParser:在页面内解析 HTML,不触发页面跳转rating1rating5 对应 1items.length === 0 或 < 15 时停止title + date 去重工具:chrome_execute_script
参数:
world: MAINtimeout: 180000jsScript:async () => {
const doubanId = '{step_2.doubanId}';
const MAX_ITEMS = 500;
const allMovies = [];
const seen = new Set();
let start = 0;
while (allMovies.length < MAX_ITEMS) {
const url = 'https://movie.douban.com/people/' + doubanId
+ '/wish?start=' + start
+ '&sort=time&rating=all&filter=all&mode=list';
try {
const resp = await fetch(url, {credentials: 'include'});
const html = await resp.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const items = doc.querySelectorAll('.list-view .item');
if (items.length === 0) break;
items.forEach(item => {
const titleEl = item.querySelector('.title a');
const dateEl = item.querySelector('.date');
const title = titleEl ? titleEl.textContent.trim() : '';
const href = titleEl ? titleEl.getAttribute('href') : '';
const date = dateEl ? dateEl.textContent.trim() : '';
const commentEl = item.querySelector('.comment');
const comment = commentEl ? commentEl.textContent.trim() : '';
const tagsEl = item.querySelector('.tags');
const tags = tagsEl ? tagsEl.textContent.trim().replace('标签: ', '') : '';
const key = title + '|' + date;
if (title && !seen.has(key)) {
seen.add(key);
allMovies.push({title, date, comment, tags, href});
}
});
if (items.length < 15) break;
start += 15;
await new Promise(r => setTimeout(r, 800));
} catch(e) { break; }
}
const sampled = allMovies.length >= MAX_ITEMS;
return JSON.stringify({total: allMovies.length, movies: allMovies, sampled});
}
说明:与步骤4逻辑相同,URL 路径改为 /wish。想看的电影没有评分(还没看),所以不提取 rating。
工具:chrome_navigate
参数:
url: https://book.douban.com/people/{step_2.doubanId}/collect说明:⚠️ 必须先导航到 book.douban.com 域名下,否则 fetch 请求因跨域限制会失败(movie.douban.com → book.douban.com 跨域)。
工具:chrome_execute_script
参数:
world: MAINtimeout: 180000jsScript:async () => {
const doubanId = '{step_2.doubanId}';
const MAX_ITEMS = 500;
const allBooks = [];
const seen = new Set();
let start = 0;
while (allBooks.length < MAX_ITEMS) {
const url = 'https://book.douban.com/people/' + doubanId
+ '/collect?start=' + start
+ '&sort=time&rating=all&filter=all&mode=list';
try {
const resp = await fetch(url, {credentials: 'include'});
const html = await resp.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const items = doc.querySelectorAll('.subject-item, .list-view .item');
if (items.length === 0) break;
items.forEach(item => {
const titleEl = item.querySelector('.title a, h2 a');
const dateEl = item.querySelector('.date, .short-note .date');
const ratingEl = item.querySelector('[class*="rating"]');
const title = titleEl ? titleEl.textContent.trim() : '';
const href = titleEl ? titleEl.getAttribute('href') : '';
const date = dateEl ? dateEl.textContent.trim() : '';
let rating = 0;
if (ratingEl) {
const m = ratingEl.className.match(/rating(\d)/);
if (m) rating = parseInt(m[1]);
}
const commentEl = item.querySelector('.comment, .short-note .comment');
const comment = commentEl ? commentEl.textContent.trim() : '';
const tagsEl = item.querySelector('.tags');
const tags = tagsEl ? tagsEl.textContent.trim().replace('标签: ', '') : '';
const pubEl = item.querySelector('.pub');
const pub = pubEl ? pubEl.textContent.trim() : '';
const key = title + '|' + date;
if (title && !seen.has(key)) {
seen.add(key);
allBooks.push({title, date, rating, comment, tags, pub, href});
}
});
if (items.length < 15) break;
start += 15;
await new Promise(r => setTimeout(r, 800));
} catch(e) { break; }
}
const sampled = allBooks.length >= MAX_ITEMS;
return JSON.stringify({total: allBooks.length, books: allBooks, sampled});
}
说明:采集读过的书。与电影类似但注意:
book.douban.com 域名.subject-item 和 .list-view .itempub(出版信息:作者/出版社/年份)工具:chrome_execute_script
参数:
world: MAINtimeout: 180000jsScript:async () => {
const doubanId = '{step_2.doubanId}';
const MAX_ITEMS = 500;
const allBooks = [];
const seen = new Set();
let start = 0;
while (allBooks.length < MAX_ITEMS) {
const url = 'https://book.douban.com/people/' + doubanId
+ '/wish?start=' + start
+ '&sort=time&rating=all&filter=all&mode=list';
try {
const resp = await fetch(url, {credentials: 'include'});
const html = await resp.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const items = doc.querySelectorAll('.subject-item, .list-view .item');
if (items.length === 0) break;
items.forEach(item => {
const titleEl = item.querySelector('.title a, h2 a');
const dateEl = item.querySelector('.date, .short-note .date');
const title = titleEl ? titleEl.textContent.trim() : '';
const href = titleEl ? titleEl.getAttribute('href') : '';
const date = dateEl ? dateEl.textContent.trim() : '';
const commentEl = item.querySelector('.comment, .short-note .comment');
const comment = commentEl ? commentEl.textContent.trim() : '';
const tagsEl = item.querySelector('.tags');
const tags = tagsEl ? tagsEl.textContent.trim().replace('标签: ', '') : '';
const pubEl = item.querySelector('.pub');
const pub = pubEl ? pubEl.textContent.trim() : '';
const key = title + '|' + date;
if (title && !seen.has(key)) {
seen.add(key);
allBooks.push({title, date, comment, tags, pub, href});
}
});
if (items.length < 15) break;
start += 15;
await new Promise(r => setTimeout(r, 800));
} catch(e) { break; }
}
const sampled = allBooks.length >= MAX_ITEMS;
return JSON.stringify({total: allBooks.length, books: allBooks, sampled});
}
说明:与步骤6逻辑相同,URL 改为 /wish。想读的书没有评分。
工具:chrome_navigate
参数:
url: https://www.douban.com/people/{step_2.doubanId}/statuses工具:chrome_execute_script
参数:
world: MAINtimeout: 60000jsScript:async () => {
await new Promise(r => setTimeout(r, 2000));
let prevH = 0, same = 0;
for (let i = 0; i < 30; i++) {
window.scrollTo({top: document.body.scrollHeight, behavior: 'smooth'});
await new Promise(r => setTimeout(r, 1500));
if (document.body.scrollHeight === prevH) {
same++; if (same > 5) break;
} else { same = 0; }
prevH = document.body.scrollHeight;
}
window.scrollTo({top: 0});
const MAX_STATUSES = 200; // 广播上限200条
const statuses = [];
document.querySelectorAll('.status-item, .new-status, [data-sid]').forEach(el => {
if (statuses.length >= MAX_STATUSES) return;
const text = el.textContent.trim();
if (text.length > 10) statuses.push(text.substring(0, 300));
});
return JSON.stringify({count: statuses.length, statuses, sampled: statuses.length >= MAX_STATUSES});
}
说明:滚动加载全部广播内容后提取。智能停止:连续5轮页面高度不变则停止。
工具:chrome_close_tabs
本 Skill 的核心采集方式:在已登录的豆瓣页面内执行 JS 脚本,使用 fetch 请求同域 URL(自动携带 cookie),用 DOMParser 解析返回的 HTML 提取数据。
优势:
关键参数:
mode=list:列表视图,DOM 结构最稳定sort=time:按时间排序rating=all / filter=all:不过滤start=N:偏移量,每页15条| 页面 | URL |
|---|---|
| 个人主页 | www.douban.com/people/{id}/ |
| 自动跳转 | www.douban.com/mine/ |
| 看过电影 | movie.douban.com/people/{id}/collect |
| 想看电影 | movie.douban.com/people/{id}/wish |
| 读过的书 | book.douban.com/people/{id}/collect |
| 想读的书 | book.douban.com/people/{id}/wish |
| 广播 | www.douban.com/people/{id}/statuses |
⚠️ 注意不同内容使用不同子域名(movie/book/www)。
⚠️ 不能只提取标题! 每条数据必须包含以下字段,这些是画像分析的关键输入:
| 数据类型 | 必含字段 | 说明 |
|---|---|---|
| 看过的电影 | title, date, rating, comment, tags, href | rating=1-5星, comment=用户短评 |
| 想看的电影 | title, date, comment, tags, href | 无 rating(还没看) |
| 读过的书 | title, date, rating, comment, tags, pub, href | pub=出版信息 |
| 想读的书 | title, date, comment, tags, pub, href | 无 rating |
为什么这些字段重要:
rating:分析用户评分风格(严格/宽松/打分率)date:分析观影时间模式(补标行为/特殊日期)comment:用户自己的一句话评价,最能反映真实想法tags:用户自定义标签,反映分类习惯rating1rating5 对应 1问题1:/mine/ 跳转到登录页
问题2:fetch 返回空数据
问题3:广播提取内容不干净