writing-beats
MAmattpocock/skills318k9,301 new stars in the last 7 days
获取腾讯 ima 知识库(ima.qq.com/wikis)中的文章列表和原始文章详情。适用于:(1) 用户已经登录 ima 知识库,需要导出或读取知识库中的文章列表;(2) ima 网页端不支持直接查看文章详情,需要通过 source_path 原始链接获取内容;(3) 批量抓取个人知识库或共享知识库中的文章标题、来源和正文。触发词:ima知识库、ima.qq.com、腾讯ima、知识库文章列表、万仁刚的知识库。
通过浏览器自动化连接已登录的 ima.qq.com/wikis 页面,从 React 组件内部状态中提取文章的 sourcePath(原始微信公众号等外部链接),再逐个打开新标签页获取文章正文。
mac_computer_use_tool 配合 plane="bu" 进行浏览器操作import seed_browser_use as bu
# 同步连接浏览器
bu.resync()
# 确认页面已加载
bu.wait_for_load(timeout=15)
page_info = bu.page_info()
print("当前页面:", page_info.get("url"))
如果 resync 失败或没有活动页面,直接用 bu.navigate("https://ima.qq.com/wikis") 打开页面。
ima 知识库使用虚拟滚动列表,文章数据存储在 React Fiber 内部状态中,DOM 元素上没有直接的链接属性。需要通过遍历 React Fiber 提取。
使用 scripts/extract_source_paths.js 中的代码逻辑,提取前 N 条文章的标题和原始链接:
js_code = """
(async () => {
const results = [];
const count = arguments[0] || 10; // 要提取的条数
for (let i = 0; i < count; i++) {
const item = document.querySelector(`[data-index="${i}"]`);
if (!item) continue;
const fiberKey = Object.keys(item).find(k => k.startsWith('__reactFiber'));
let fiber = item[fiberKey];
let foundPath = null;
let foundTitle = null;
let depth = 0;
function searchObj(obj, path, depth2) {
if (!obj || depth2 > 8 || foundPath) return;
if (typeof obj !== 'object') return;
for (let key in obj) {
try {
if ((key === 'sourcePath' || key === 'source_path') && typeof obj[key] === 'string') {
foundPath = obj[key];
}
if ((key === 'title' || key === 'name') && typeof obj[key] === 'string' && obj[key].length > 5) {
foundTitle = obj[key];
}
if (typeof obj[key] === 'object' && obj[key] !== null) {
searchObj(obj[key], path + '.' + key, depth2 + 1);
}
} catch(e) {}
}
}
while (fiber && depth < 30 && !foundPath) {
if (fiber.memoizedProps) {
searchObj(fiber.memoizedProps, 'props', 0);
}
fiber = fiber.return;
depth++;
}
results.push({
index: i,
title: foundTitle || item.innerText.split('\\n')[0],
sourcePath: foundPath
});
}
return results;
})()
"""
# 传入要提取的条数
result = bu.js(js_code.replace('arguments[0] || 10', '5'))
关键点:
data-index 属性定位,从 0 开始sourcePath 通常是微信公众号链接(mp.weixin.qq.com/s/...)提取到 sourcePath 后,逐个打开新标签页获取正文:
import time
for article in result:
if not article.get('sourcePath'):
continue
# 关闭上一个标签页(可选,节省资源)
# bu.close_tab()
# time.sleep(1)
# 打开新标签页
bu.new_tab(article['sourcePath'])
time.sleep(3)
bu.wait_for_load(timeout=10)
# 获取文章正文
content = bu.get_page_text()
print(f"标题: {article['title']}")
print(f"内容: {content[:500]}...")
# 处理完后关闭
bu.close_tab()
time.sleep(1)
虚拟滚动列表只渲染可视区域的项。要获取更多文章,需要先向下滚动:
# 滚动列表区域
bu.scroll(500, 500, "down", amount=5)
time.sleep(1)
# 然后重新提取 data-index 更大的项
bu.snapshot() 看看页面结构depth < 30 改大)url、link、originalUrlinteraction.request_action 让用户接管完成登录/cgi-bin/knowledge_tab_reader/get_knowledge_list),需要复杂的签名和参数,容易失败bu.get_page_text() 获取MAmattpocock/skills318k9,301 new stars in the last 7 days
MAmattpocock/skills318k9,301 new stars in the last 7 days
MAmattpocock/skills318k9,301 new stars in the last 7 days
Write a glossary, record ADRs, and clarify vague terms.
MAmattpocock/skills318k9,301 new stars in the last 7 days
OBobra/superpowers314k4,048 new stars in the last 7 days
OBobra/superpowers314k4,048 new stars in the last 7 days