222 lines
7.2 KiB
JavaScript
222 lines
7.2 KiB
JavaScript
/**
|
|
* SupJav 爬虫
|
|
* 作者:deepseek
|
|
* 版本:1.0
|
|
* 最后更新:2025-12-17
|
|
* 发布页 https://supjav.com/zh
|
|
*/
|
|
|
|
const baseUrl = 'https://supjav.com/zh';
|
|
|
|
/**
|
|
* 初始化配置
|
|
*/
|
|
async function init(cfg) {
|
|
return {
|
|
webview: {
|
|
debug: true, // 是否开启调试模式
|
|
showWebView: false, // 默认不显示
|
|
widthPercent: 80, // 窗口宽度百分比
|
|
heightPercent: 40, // 窗口高度百分比
|
|
keyword: '', // 页面不存在该关键字时显示webview
|
|
returnType: 'dom', // 返回DOM对象
|
|
timeout: 30, // 超时时间(秒)
|
|
blockImages: false, // 是否禁止加载图片
|
|
enableJavaScript: true, // 是否启用JS
|
|
header: {
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
'Referer': baseUrl + '/',
|
|
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 首页分类
|
|
*/
|
|
async function homeContent(filter) {
|
|
return {
|
|
class: [
|
|
{ type_id: "popular", type_name: "热门" },
|
|
{ type_id: "category/censored-jav", type_name: "有码" },
|
|
{ type_id: "category/uncensored-jav", type_name: "无码" },
|
|
{ type_id: "category/amateur", type_name: "素人" },
|
|
{ type_id: "category/chinese-subtitles", type_name: "中文字幕" },
|
|
{ type_id: "category/english-subtitles", type_name: "英文字幕" },
|
|
{ type_id: "category/reducing-mosaic", type_name: "无码破解" }
|
|
]
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 首页推荐视频
|
|
*/
|
|
async function homeVideoContent() {
|
|
const document = await Java.wvOpen(baseUrl + '/');
|
|
const videos = parseVideoList(document);
|
|
return { list: videos };
|
|
}
|
|
|
|
/**
|
|
* 分类内容
|
|
*/
|
|
async function categoryContent(tid, pg, filter, extend) {
|
|
const pgSuffix = (pg === 1 ? '' : `/page/${pg}`);
|
|
const document = await Java.wvOpen(`${baseUrl}/${tid}${pgSuffix}`);
|
|
const videos = parseVideoList(document);
|
|
return { code: 1, msg: "数据列表", list: videos, page: pg, pagecount: 10000, limit: 24, total: 240000 };
|
|
}
|
|
|
|
/**
|
|
* 详情页
|
|
*/
|
|
async function detailContent(ids) {
|
|
const document = await Java.wvOpen(ids[0]);
|
|
const list = parseDetailPage(document, ids[0]);
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* 搜索
|
|
*/
|
|
async function searchContent(key, quick, pg) {
|
|
// 暂未实现搜索逻辑
|
|
return { list: [] };
|
|
}
|
|
|
|
/**
|
|
* 播放器
|
|
*/
|
|
async function playerContent(flag, id, vipFlags) {
|
|
const ids = id.split('---');
|
|
Java.showWebView();
|
|
|
|
return {
|
|
type: 'sniff', // 必填,标识嗅探模式
|
|
url: ids[1], // 必填,要加载的播放页面 URL
|
|
keyword: '.m3u8|.mp4', // 可选,嗅探关键字,多个用 | 分隔
|
|
script: `
|
|
(function(){
|
|
setTimeout(() => {
|
|
document.querySelectorAll('.btn-server').forEach(btn => {
|
|
if(btn.textContent.trim() === '${ids[0]}') {
|
|
btn.click();
|
|
console.log('已点击服务器: ${ids[0]}');
|
|
}
|
|
});
|
|
setTimeout(() => {
|
|
document.querySelector("#a > div.spin").click();
|
|
console.log('已点击播放按钮');
|
|
}, 3000);
|
|
}, 500);
|
|
})();
|
|
`,
|
|
timeout: 15 // 可选,嗅探超时时间(秒),默认 15
|
|
};
|
|
}
|
|
|
|
/**
|
|
* action
|
|
*/
|
|
async function action(actionStr) {
|
|
try {
|
|
const params = JSON.parse(actionStr);
|
|
console.log("action params:", params);
|
|
} catch (e) {
|
|
console.log("action is not JSON, treat as string");
|
|
}
|
|
return { list: [] };
|
|
}
|
|
|
|
|
|
/* ---------------- 工具函数 ---------------- */
|
|
|
|
/**
|
|
* 提取视频列表
|
|
*/
|
|
function parseVideoList(document) {
|
|
const posts = document.querySelectorAll('.post');
|
|
return Array.from(posts).map(post => {
|
|
const titleLink = post.querySelector('h3 a');
|
|
const vod_name = titleLink?.getAttribute('title') || titleLink?.textContent || '';
|
|
const vod_id = titleLink?.getAttribute('href') || '';
|
|
const img = post.querySelector('img');
|
|
const vod_pic = img?.getAttribute('data-original') || img?.getAttribute('src') || '';
|
|
const meta = post.querySelector('.meta');
|
|
let vod_remarks = '';
|
|
if (meta) {
|
|
const metaText = meta.textContent || '';
|
|
const viewsMatch = metaText.match(/(\d+)\s*Views/);
|
|
if (viewsMatch) {
|
|
vod_remarks = `${viewsMatch[1]}次浏览`;
|
|
} else {
|
|
const chineseMatch = metaText.match(/(\d+)\s*次浏览/);
|
|
vod_remarks = chineseMatch ? `${chineseMatch[1]}次浏览` : '';
|
|
}
|
|
}
|
|
return { vod_id, vod_name, vod_pic, vod_remarks };
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 解析详情页
|
|
*/
|
|
function parseDetailPage(doc, id) {
|
|
const title = doc.querySelector('.archive-title h1')?.textContent?.trim() || '';
|
|
const vod_pic = doc.querySelector('.post-meta .img')?.src || '';
|
|
const viewsText = doc.querySelector('.dz_view .views')?.textContent || '';
|
|
const vod_remarks = viewsText.replace(/[^0-9]/g, '') || '0';
|
|
const tagElements = doc.querySelectorAll('.tags a');
|
|
const vod_actor = Array.from(tagElements).map(tag => tag.textContent.trim()).join(',');
|
|
let vod_director = '';
|
|
const catElements = doc.querySelectorAll('.cats p, .cats span, .cats a');
|
|
for (let el of catElements) {
|
|
if (el.textContent.includes('Maker')) {
|
|
if (el.nextElementSibling?.tagName === 'A') {
|
|
vod_director = el.nextElementSibling.textContent.trim();
|
|
break;
|
|
}
|
|
const linkInParent = el.querySelector('a');
|
|
if (linkInParent) {
|
|
vod_director = linkInParent.textContent.trim();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const vod_content = doc.querySelector('.post-content')?.textContent?.trim() || '';
|
|
const serverButtons = doc.querySelectorAll('.btnst .btn-server');
|
|
const playItems = Array.from(serverButtons).map(btn => {
|
|
const serverName = btn.textContent.trim();
|
|
const dataLink = btn.getAttribute('data-link');
|
|
return serverName && dataLink ? `${serverName}$${serverName}---${id}` : '';
|
|
}).filter(Boolean);
|
|
const vod_play_url = playItems.join('#') || '';
|
|
const vod = {
|
|
code: 1,
|
|
msg: "数据列表",
|
|
page: 1,
|
|
pagecount: 1,
|
|
limit: 1,
|
|
total: 1,
|
|
list: [{
|
|
vod_id: id || 'unknown_id',
|
|
vod_name: title,
|
|
vod_pic: vod_pic,
|
|
vod_remarks: vod_remarks,
|
|
vod_director: vod_director,
|
|
vod_actor: vod_actor,
|
|
vod_content: vod_content,
|
|
vod_play_from: 'SERVER',
|
|
vod_play_url: vod_play_url
|
|
}]
|
|
};
|
|
// console.log(vod);
|
|
return vod;
|
|
}
|
|
|
|
|
|
/* ---------------- 导出对象 ---------------- */
|
|
const spider = { init, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent, action };
|
|
spider;
|