douban-deep-profile-collect
采集豆瓣当前登录用户的全量个人信息,包括基础资料、看过/想看的电影、读过/想读的书、广播动态
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
采集豆瓣当前登录用户的全量个人信息,包括基础资料、看过/想看的电影、读过/想读的书、广播动态
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
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:广播提取内容不干净