API仅供学习交流使用,禁止用于商业用途、违法用途等,否则后果自负!
技术分析
最右平台的API相对封闭,官方文档不完善,主要通过抓包和逆向工程方法进行实现。完整的解析流程包含以下步骤:
- 分享链接解析,提取内容ID(pid)
- 调用API获取内容详情数据
- 解析响应数据,提取无水印资源URL
- 构建标准化响应结构
技术栈采用 uniCloud
云函数,配合 uni-cloud-router
框架实现接口路由和服务解耦,保持各平台解析功能的独立性与接口一致性
技术实现详解
技术实现详解
针对最右平台分享链接格式 https://share.xiaochuankeji.cn/hybrid/share/post?pid=xxx...
,采用URL对象方法提取参数:
extractShareId(url) {
try {
const urlObj = new URL(url);
const pid = urlObj.searchParams.get('pid');
return pid;
} catch (error) {
console.error("提取分享ID失败:", error);
return null;
}
}
API请求实现
内容详情获取需通过 POST
方式请求,数据结构如下:
async getContentData(shareId) {
try {
const api = "https://share.xiaochuankeji.cn/planck/share/post/detail_h5";
// 构造请求数据
const postData = {
"pid": parseInt(shareId) // 转成数字类型
};
const response = await this.curl(api, {
method: 'POST',
headers: this.headers,
data: postData,
dataType: "json"
});
// 验证响应
const responseData = response.data;
if (!responseData || responseData.ret!== 1) {
console.error("API返回错误:", responseData);
return null;
}
return responseData.data;
} catch (error) {
console.error("获取内容详情失败:", error);
return null;
}
}
资源数据结构解析
数据结构设计中,视频资源通过图片ID关联,这是一个关键的技术点:
// 获取基本信息
const title = this.safeGet(post, 'content', '');
const author = this.safeGet(post, 'member.name', '');
const imgs = this.safeGet(post, 'imgs', []);
// 获取图片ID
let imgId = null;
if (imgs && imgs.length > 0) {
imgId = String(imgs[0].id);
}
// 通过图片ID获取视频URL
let videoUrl = '';
if (imgId && post.videos && post.videos[imgId]) {
videoUrl = post.videos[imgId].url;
}
这种设计利用图片ID作为视频对象键名的方式,有效关联了视频与其封面图,是最右平台API的特殊之处
图片资源处理
针对最右多种尺寸的图片资源,实现了优先级获取策略:
// 构造图片URL数组
const imageUrls = [];
if (imgs && imgs.length > 0) {
for (const img of imgs) {
// 优先使用中等尺寸的webp格式
if (img.urls && img.urls['540_webp'] && img.urls['540_webp'].urls) {
imageUrls.push({
url: img.urls['540_webp'].urls[0],
width: img.width || 0,
height: img.height || 0
});
}
// 备选原图格式
else if (img.urls && img.urls.origin && img.urls.origin.urls) {
imageUrls.push({
url: img.urls.origin.urls[0],
width: img.width || 0,
height: img.height || 0
});
}
}
}
健壮性处理
为提高服务稳定性,实现了安全属性获取方法,有效避免因API变更导致的引用错误:
safeGet(obj, path, defaultValue = '') {
return path.split('.').reduce((acc, part) => {
if (acc && typeof acc === 'object' && part in acc) {
return acc[part];
}
return defaultValue;
}, obj);
}
完整服务实现
const { Service } = require("uni-cloud-router");
module.exports = class ZuiyouService extends Service {
constructor(ctx) {
super(ctx);
this.headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
"Accept": "application/json",
"Content-Type": "application/json",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
};
}
async parse() {
try {
const url = this.ctx.data.url;
if (!url) {
return { code: 400, msg: "请提供最右分享链接" };
}
console.log("开始解析最右URL:", url);
// 提取分享ID
const shareId = this.extractShareId(url);
if (!shareId) {
return { code: 400, msg: "无法从URL中提取分享ID" };
}
console.log("提取到分享ID:", shareId);
// 获取内容详情
const contentData = await this.getContentData(shareId);
if (!contentData) {
return { code: 201, msg: "无法获取内容详情" };
}
// 解析内容数据
return this.parseContentData(contentData);
} catch (error) {
console.error("解析过程中出错:", error);
return {
code: 500,
msg: `解析失败: ${error.message}`,
data: null
};
}
}
extractShareId(url) {
try {
// 从查询参数中提取pid
const urlObj = new URL(url);
const pid = urlObj.searchParams.get('pid');
return pid;
} catch (error) {
console.error("提取分享ID失败:", error);
return null;
}
}
async getContentData(shareId) {
try {
// 参考最右的API请求
const api = "https://share.xiaochuankeji.cn/planck/share/post/detail_h5";
console.log("请求API:", api);
// 构造请求数据
const postData = {
"pid": parseInt(shareId)
};
// 发送POST请求
const response = await this.curl(api, {
method: 'POST',
headers: this.headers,
data: postData,
dataType: "json"
});
// 检查API响应
const responseData = response.data;
console.log(responseData);
if (!responseData || responseData.ret!== 1) {
console.error("API返回错误:", responseData);
return null;
}
return responseData.data;
} catch (error) {
console.error("获取内容详情失败:", error);
return null;
}
}
parseContentData(data) {
try {
// 提取帖子信息
const post = this.safeGet(data, 'post', {});
if (!post) {
return {
code: 404,
msg: "未找到内容信息",
data: null
};
}
// 获取基本信息
const title = this.safeGet(post, 'content', '');
const author = this.safeGet(post, 'member.name', '');
const imgs = this.safeGet(post, 'imgs', []);
// 获取第一张图片的ID
let imgId = null;
if (imgs && imgs.length > 0) {
imgId = String(imgs[0].id);
}
// 获取视频URL
let videoUrl = '';
if (imgId && post.videos && post.videos[imgId]) {
videoUrl = post.videos[imgId].url;
}
// 构造图片URL数组
const imageUrls = [];
if (imgs && imgs.length > 0) {
for (const img of imgs) {
if (img.urls && img.urls['540_webp'] && img.urls['540_webp'].urls) {
imageUrls.push({
url: img.urls['540_webp'].urls[0],
width: img.width || 0,
height: img.height || 0
});
} else if (img.urls && img.urls.origin && img.urls.origin.urls) {
imageUrls.push({
url: img.urls.origin.urls[0],
width: img.width || 0,
height: img.height || 0
});
}
}
}
// 构造返回数据
const isVideo = videoUrl!== '';
const result = {
code: imgId? 200 : 201,
msg: imgId? "解析成功" : "未找到媒体内容",
data: {
author: author,
uid: this.safeGet(post, 'member.id', ''),
avatar: this.safeGet(post, 'member.avatar_urls.origin.urls.0', ''),
like_count: this.safeGet(post, 'like_count', 0),
comment_count: this.safeGet(post, 'comment_count', 0),
time: this.formatTime(this.safeGet(post, 'create_time', 0)),
title: title,
platform: "zuiyou"
}
};
if (isVideo) {
// 视频内容
result.data.type = "video";
result.data.cover = imageUrls.length > 0? imageUrls[0].url : '';
result.data.url = videoUrl;
result.data.video_url = videoUrl;
} else if (imageUrls.length > 0) {
// 图片内容
result.data.type = "image_set";
result.data.cover = imageUrls[0].url;
result.data.images = imageUrls;
} else {
// 纯文本内容
result.data.type = "text";
}
return result;
} catch (error) {
console.error("解析内容数据失败:", error);
return {
code: 500,
msg: "解析内容数据失败",
data: null
};
}
}
formatTime(timestamp) {
if (!timestamp) return '';
// 将秒级时间戳转换为毫秒级
if (timestamp.toString().length === 10) {
timestamp = timestamp * 1000;
}
return new Date(timestamp).toISOString();
}
safeGet(obj, path, defaultValue = '') {
return path.split('.').reduce((acc, part) => {
if (acc && typeof acc === 'object' && part in acc) {
return acc[part];
}
return defaultValue;
}, obj);
}
};
关键技术总结
- 链接解析技术:应用URL对象进行高效参数提取
- API逆向分析:确定接口、请求方式及数据结构
- 特殊数据结构处理:解析视频与图片ID关联机制
- 资源优化策略:优先选择体积较小的webp格式图片
- 健壮性设计:实现全链路异常处理和安全属性读取
接口规范与集成
针对多平台统一解析需求,设计了一致的接口规范:
- 请求方式:POST
- 请求参数:{ url: "分享链接" }
- 响应格式:标准化JSON结构
性能与稳定性优化
- 缓存策略:针对热门内容实现结果缓存
- 异步处理:解决大文件处理的性能问题
- 异常处理:完善错误捕获和日志记录
- 资源选择:根据场景智能选择资源质量