plugin/discourse/json结构分析.md
https://linux.do/search.jsonGETapplication/jsonGET https://linux.do/search.json?q={keyword}%20%23resource%3Acloud-asset%20in%3Atitle&page={page}
| 参数名 | 类型 | 必需 | 默认值 | 说明 |
|---|---|---|---|---|
q | string | 是 | - | 搜索查询,包含关键词和过滤条件,需要URL编码 |
page | int | 否 | 1 | 页码,从1开始 |
{keyword} #resource:cloud-asset in:title
说明:
{keyword}: 搜索关键词(如:遮天)#resource:cloud-asset: 过滤标签,只搜索云盘资源类别in:title: 只在标题中搜索{
"posts": [...],
"topics": [...],
"users": [],
"categories": [],
"tags": [],
"groups": [],
"grouped_search_result": {
"more_posts": null,
"more_users": null,
"more_categories": null,
"term": "遮天 #resource:cloud-asset in:title",
"search_log_id": 16604511,
"more_full_page_results": true,
"can_create_topic": true,
"error": null,
"extra": {},
"post_ids": [...],
"user_ids": [],
"category_ids": [],
"tag_ids": [],
"group_ids": []
}
}
包含搜索到的帖子信息,每个帖子包含网盘链接:
{
"id": 9619992,
"name": "lxwh",
"username": "lxwh",
"avatar_template": "/user_avatar/linux.do/lxwh/{size}/387453_2.png",
"created_at": "2025-10-21T10:29:05.613Z",
"like_count": 2,
"blurb": "紫川更新 遮天... 夸克网盘: https://pan.quark.cn/s/99758a147076 点击进入 百度网盘: https://pan.baidu.com/s/1wF1YzQ14Vo8us_k9UfFNJQ?pwd=hccn 点击进入...",
"post_number": 1,
"topic_id": 1067663
}
| 字段名 | 类型 | 说明 |
|---|---|---|
id | int | 帖子ID |
name | string | 发帖人姓名 |
username | string | 发帖人用户名 |
avatar_template | string | 头像模板URL |
created_at | string | 发布时间(ISO 8601格式) |
like_count | int | 点赞数 |
blurb | string | 帖子内容摘要(包含网盘链接) |
post_number | int | 帖子楼层号 |
topic_id | int | 主题ID |
包含搜索到的主题信息:
{
"fancy_title": "遮天 第132集&紫川2更15集 【4K高码】",
"id": 1067663,
"title": "遮天 第132集&紫川2更15集 【4K高码】",
"slug": "topic",
"posts_count": 8,
"reply_count": 2,
"highest_post_number": 8,
"created_at": "2025-10-21T10:29:05.493Z",
"last_posted_at": "2025-10-22T00:28:29.185Z",
"bumped": true,
"bumped_at": "2025-10-22T00:28:29.185Z",
"archetype": "regular",
"unseen": false,
"pinned": false,
"unpinned": null,
"visible": true,
"closed": false,
"archived": false,
"bookmarked": null,
"liked": null,
"tags": [
"夸克网盘",
"影视",
"百度网盘",
"动漫"
],
"tags_descriptions": {},
"category_id": 94,
"has_accepted_answer": false,
"can_have_answer": false
}
| 字段名 | 类型 | 说明 |
|---|---|---|
id | int | 主题ID |
title | string | 主题标题 |
fancy_title | string | 格式化标题(HTML实体) |
tags | array | 标签列表(包含网盘类型) |
posts_count | int | 回复数 |
created_at | string | 创建时间 |
last_posted_at | string | 最后回复时间 |
category_id | int | 分类ID(94=云盘资源) |
{
"term": "遮天 #resource:cloud-asset in:title",
"search_log_id": 16604511,
"more_full_page_results": true,
"can_create_topic": true,
"error": null,
"post_ids": [9619992, 9620329, ...],
"user_ids": [],
"category_ids": [],
"tag_ids": [],
"group_ids": []
}
| 字段名 | 类型 | 说明 |
|---|---|---|
term | string | 搜索词 |
post_ids | array | 所有帖子ID列表 |
more_full_page_results | bool | 是否有更多结果 |
blurb 字段包含帖子的文本摘要,其中包含网盘链接。需要使用正则表达式提取:
| 网盘类型 | URL 格式 | 提取码格式 |
|---|---|---|
| 夸克网盘 | https://pan.quark.cn/s/{code} | 无需提取码 |
| 百度网盘 | https://pan.baidu.com/s/{code}?pwd={password} | ?pwd={password} |
| 阿里云盘 | https://www.aliyundrive.com/s/{code} | 无需提取码 |
| 迅雷网盘 | https://pan.xunlei.com/s/{code}?pwd={password} | ?pwd={password} |
| 天翼云盘 | https://cloud.189.cn/t/{code} | 访问码: {code} |
| UC网盘 | https://drive.uc.cn/s/{code} | 无需提取码 |
// 夸克网盘
quarkPattern := regexp.MustCompile(`https://pan\.quark\.cn/s/[0-9a-zA-Z]+`)
// 百度网盘(带提取码)
baiduPattern := regexp.MustCompile(`https://pan\.baidu\.com/s/[0-9a-zA-Z_\-]+(?:\?pwd=([0-9a-zA-Z]+))?`)
// 阿里云盘
aliyunPattern := regexp.MustCompile(`https://(?:www\.)?aliyundrive\.com/s/[0-9a-zA-Z]+`)
// 迅雷网盘
xunleiPattern := regexp.MustCompile(`https://pan\.xunlei\.com/s/[0-9a-zA-Z_\-]+(?:\?pwd=([0-9a-zA-Z]+))?`)
// 天翼云盘
tianyiPattern := regexp.MustCompile(`https://cloud\.189\.cn/t/[0-9a-zA-Z]+`)
// UC网盘
ucPattern := regexp.MustCompile(`https://drive\.uc\.cn/s/[0-9a-zA-Z]+`)
tags 数组包含网盘类型标签,可以用于过滤和分类:
tags := []string{"夸克网盘", "百度网盘", "动漫"}
| 标签名 | 英文标识 |
|---|---|
| 夸克网盘 | quark |
| 百度网盘 | baidu |
| 阿里云盘 | aliyun |
| 迅雷网盘 | xunlei |
| UC网盘 | uc |
| 天翼云盘 | tianyi |
| 115网盘 | 115 |
| 123网盘 | 123 |
// 输入格式: "2025-10-21T10:29:05.613Z" (ISO 8601)
// 解析为 time.Time
parsedTime, err := time.Parse(time.RFC3339, "2025-10-21T10:29:05.613Z")
Linux.do 使用 Cloudflare 防护,必须使用 cloudscraper 库绕过:
import "github.com/Advik-B/cloudscraper/lib"
// 创建 cloudscraper 客户端
sc, err := cloudscraper.New()
// 发送请求
resp, err := sc.Get(searchURL)
// 搜索关键词需要 URL 编码
keyword := "遮天"
query := fmt.Sprintf("%s #resource:cloud-asset in:title", keyword)
searchURL := fmt.Sprintf("https://linux.do/search.json?q=%s&page=%d",
url.QueryEscape(query), page)
// 从 blurb 中提取所有网盘链接
func extractNetDiskLinks(blurb string) []model.Link {
var links []model.Link
// 提取夸克网盘
quarkLinks := quarkPattern.FindAllString(blurb, -1)
for _, linkURL := range quarkLinks {
links = append(links, model.Link{
Type: "quark",
URL: linkURL,
})
}
// 提取百度网盘(带提取码)
baiduMatches := baiduPattern.FindAllStringSubmatch(blurb, -1)
for _, match := range baiduMatches {
link := model.Link{
Type: "baidu",
URL: match[0],
}
if len(match) > 1 && match[1] != "" {
link.Password = match[1]
}
links = append(links, link)
}
// ... 其他网盘类型
return links
}
func convertToSearchResult(post Post, topic Topic) model.SearchResult {
// 提取网盘链接
links := extractNetDiskLinks(post.Blurb)
// 解析时间
createdAt, _ := time.Parse(time.RFC3339, post.CreatedAt)
return model.SearchResult{
UniqueID: fmt.Sprintf("linuxdo-%d", post.ID),
Title: topic.Title,
Content: post.Blurb,
Links: links,
Tags: topic.Tags,
Channel: "", // 插件搜索结果必须为空
Datetime: createdAt,
}
}
#resource:cloud-asset in:title 过滤条件根据 Linux.do 的特点,建议设置插件优先级为 2:
当搜索结果中的 blurb 字段无法提供完整的网盘链接时,可以通过详情页API获取完整内容。
GET https://linux.do/t/{topic_id}.json?track_visit=true&forceLoad=true
| 参数名 | 类型 | 必需 | 说明 |
|---|---|---|---|
topic_id | int | 是 | 主题ID,从搜索结果的 topic_id 字段获取 |
track_visit | bool | 否 | 是否跟踪访问 |
forceLoad | bool | 否 | 是否强制加载 |
{
"post_stream": {
"posts": [
{
"id": 9619992,
"username": "lxwh",
"created_at": "2025-10-21T10:29:05.613Z",
"cooked": "<p>HTML格式的完整帖子内容...</p>",
"post_number": 1,
"topic_id": 1067663,
"link_counts": [
{
"url": "https://pan.quark.cn/s/d6b8b0908959",
"internal": false,
"reflection": false,
"clicks": 29
},
{
"url": "https://pan.baidu.com/s/1KJylsrBbKbMhi9e-i9YMVA?pwd=tn44",
"internal": false,
"reflection": false,
"clicks": 16
}
]
}
]
},
"id": 1067663,
"title": "遮天 第132集&紫川2更15集 【4K高码】",
"fancy_title": "遮天 第132集&紫川2更15集 【4K高码】",
"tags": ["夸克网盘", "影视", "百度网盘", "动漫"],
"category_id": 94
}
| 字段名 | 类型 | 说明 |
|---|---|---|
cooked | string | HTML格式的完整帖子内容 |
link_counts | array | 所有外部链接列表(含网盘链接) |
这是最可靠的链接提取来源,包含了帖子中所有外部链接:
{
"url": "https://pan.quark.cn/s/d6b8b0908959",
"internal": false,
"reflection": false,
"clicks": 29
}
| 字段名 | 类型 | 说明 |
|---|---|---|
url | string | 完整的链接URL |
internal | bool | 是否为内部链接(false表示外部链接) |
clicks | int | 点击次数 |
推荐方式:优先使用 link_counts 数组
// 从 link_counts 提取网盘链接
for _, linkCount := range post.LinkCounts {
// 跳过内部链接
if linkCount.Internal {
continue
}
// 判断是否为网盘链接
if isNetDiskURL(linkCount.URL) {
link := parseNetDiskLink(linkCount.URL)
links = append(links, link)
}
}
备用方式:从 cooked HTML 中提取
当 link_counts 为空或不完整时,可以从 HTML 中提取:
// 使用 goquery 解析 HTML
doc, _ := goquery.NewDocumentFromReader(strings.NewReader(post.Cooked))
// 提取所有 <a> 标签
doc.Find("a").Each(func(i int, s *goquery.Selection) {
href, exists := s.Attr("href")
if exists && isNetDiskURL(href) {
link := parseNetDiskLink(href)
links = append(links, link)
}
})
第一步:搜索API
blurb 快速提取部分链接topic_id 用于详情请求第二步:详情API(按需)
blurb 中链接不完整时link_counts 获取所有链接topic_id 的详情可缓存GET https://linux.do/search.json?q=%E9%81%AE%E5%A4%A9%20%23resource%3Acloud-asset%20in%3Atitle&page=1
{
"posts": [
{
"id": 9619992,
"username": "lxwh",
"created_at": "2025-10-21T10:29:05.613Z",
"blurb": "夸克网盘: https://pan.quark.cn/s/99758a147076",
"topic_id": 1067663
}
],
"topics": [
{
"id": 1067663,
"title": "遮天 第132集&紫川2更15集 【4K高码】",
"tags": ["夸克网盘", "影视", "动漫"]
}
]
}
GET https://linux.do/t/1067663.json?track_visit=true&forceLoad=true
{
"post_stream": {
"posts": [
{
"id": 9619992,
"link_counts": [
{
"url": "https://pan.quark.cn/s/d6b8b0908959",
"internal": false,
"clicks": 29
},
{
"url": "https://pan.baidu.com/s/1KJylsrBbKbMhi9e-i9YMVA?pwd=tn44",
"internal": false,
"clicks": 16
}
]
}
]
},
"title": "遮天 第132集&紫川2更15集 【4K高码】"
}