您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
(感谢闲鱼买家ZEP的有偿定制) 用文字列表的方式展示B站搜索结果,方便按各列排序。
// ==UserScript== // @name BiliBili-TextSearchList // @name:zh-CN BiliBili-文字搜索列表 // @namespace https://github.com/Mehver // @version 1.0 // @description (Thanks to ZEP's paid customization) Display Bilibili search results in a text list, which is convenient for sorting by each column. // @description:zh-CN (感谢闲鱼买家ZEP的有偿定制) 用文字列表的方式展示B站搜索结果,方便按各列排序。 // @sponsor ZEP // @author https://github.com/Mehver // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAuIwAALiMBeKU/dgAABjBJREFUeJztl2lsVFUUgKdTSi1L19CWspRFAkgFikWqbIopKoWyCBgNhMUYAtGAJBiiVkkwiCYoiQYjokYwFKNtZzpLpxtTuokthdJCFxAoXWxn7n1v9ulM35u5njtbh9d5LYl/OcmX9+bOvfecd84957wnkTwRESGEhAExwDxgCZAOzADG/Y89lwElgA24AEyQuIlDOCkaOAK0AW4yXOhYHbAekBA3F/aYytcBOsFepyUzSwypSSo2G9gTr2De39NouXj2gcOi6h8kt0w8sfPugNZ7VhdphrEWoNHA9X53b+DA7BI2Q9nFjhlBsRR4GbgZ4mGqJAlK9kRYPnZL8jEJRlqAySQlS9bVmkkV5ggHFmyoM5MEBUtgjYeYIoaLLMRmLRqIFVFOw7gV6AuhfBDYJxknw7lC5UKmqFlSjTk+q9o07L8oGUNchLwpYgB98l4R5blAuCRRxRwezQBKeoWxYX4pe1M4PknF0g0RMF+gfA7QHEL5AHDIo5wKPN3exzFgbCFzY7Ka0QrHZ2oM/o21wFif8qeI95QLhYbiQxqagKVxCmaTcNPwAsxPUjI/jSnAPUMG4GspakYhnLugzBCs4IBP+SbiTbVgoa7aBjx6YGdo2KwQBljBM2vGy5ky/9hEOXMuQcGcFc59QWsMVtIDbASqBco54OOA24NlVonheeGmkBV8RCHukObjAfob7jvB0Iypxey3wrlZNSahmzsBp2CMFp+4kHk6Vc0uGin2kI58soo5+lmHTQo5f0r4/3pIzVHEAWwRqxOSTC07V7hpbBFDdjRYqNs9v6cXs00P7a71z5Synwvnbrs6qgEaIFLUgGfLjdOEm6YWs0Te5/Rc6W/wErlr5d3f3x8wC+curzS2kNAlm/hC8bqociqv/mWOHy0FV1QaiQVKYV63I1SR+gOUXBEx4Lpo7P1SaeWjw8TjT1ZXmUirmffspgSvDPOWhqH5flDEgE9HVE6le8Apyaw0LExWs9mxCmY31PZ9sUX4g7Qy5quTHdYfm42DD+9baS9wExs0puuGQVLLeDnRbsndfw2tPHbbnNNh4QlyuDyxoNd2M8er++w7TG4+zAUd91KvOfKtWt2SFWX9OUtL+zdvre5fJmrUS5Vo3ngZ7vOlJJmqYow9Nq69y8aRtVWsZ8xPZgWq8c9LUjDk1047OdZq9YZHqS/RudzSlZfRhsQidEca5LlEBTJ1c+7QrXx7HZoxQY4f+CfD/R1Zr31Zt53v2FxHGxIT2Gj1Zf2F4JDEQOYs13oPb5IC5a3SoteioGMKQxctQ+46s3NCSANyqvqTYFHrUBXEbbktOKXTxv/8dr05uGCR5eW6L4SbRxR4r7M1OC9Vhc6HOmMRBYic6zQleRRC2MYDmcBSIOGTpn/jwcLmIWvxzS9b9HG9du7ku41DLTkc2FKrOyKWPellqGiiDLUHj8EZs4BnKlKUeuWxW7rpVHkUUEC8bZI2kIo+B58GB7Eh4AEZ+ruBdSZYOJf2YJM5EAL6pO814p1iBswtRr/DWQo0tGg5vv9iBdqeXYUzFpfjlWsq2eeoAZuFuQOH/fw0Fb7hX5iiRPVQBnYPutyWj1osAQXjCj0GrPKHI1HJkGB3p5XozoDSgAemqLCs0uQMn1OMisEwbnYxPCTo2ys0AFLJsa7a4PAvXHPFYGszcyZILXK8dciAaJnedapNlx7m80YNdpKd9SbqZs//CzV9ubPU6Kx0yAPdGeVof6oKF/geTEMNmE6G926y/epQrMFakqJiyK4GI/n6ji0wHifHzrweW0p4PiKRcKicYLkVXHUa5iSDN9I06GB2DX4aYn4FWrzLFzb6Hmml93M06E//IfxGaMCZf2xkB5z4nT7o/aUuO7msc3juF5ayV2eqUJ4G2aOmKPRFG6rZLt8LNHHBtazf0Z1TjdfyUIQ21uDkBSXocLIClcbKUVO0HN2Ok+uvLy7VH/IbQD9A8ulakZIqlHog3u4mEicZlNiIm779/iKYU6Yb5B8pNGq9LeJ0hy72aAtK/OEujrntCCpEsGAy8BugH0ExNfAWsEhYO2DsuGDuxRF7gJjAwolAFrCLeJsM5R3gDeL9NJOKrKPvgq/4oK/jIb8VnohQ/gOO0btjxXIDWwAAAABJRU5ErkJggg== // @match http*://search.bilibili.com/* // @license MPL-2.0 // @license^ Mozilla Public License 2.0 // @charset UTF-8 // @homepageURL https://github.com/SynRGB/BiliBili-TextSearchList // @contributionURL https://github.com/SynRGB/BiliBili-TextSearchList // @copyright Copyright © 2022-PRESENT, Mehver (https://github.com/Mehver) // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @resource DataTablesCSS https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css // ==/UserScript== let table_font_size = GM_getValue('table_font_size', 16); GM_registerMenuCommand('设置表格字体大小', async () => { let newFontSize = prompt('请输入新的字体大小(单位px):', table_font_size); if (newFontSize) { table_font_size = newFontSize; GM_setValue('table_font_size', table_font_size); alert('字体大小已更新!请刷新页面以查看更改。'); } }); ////////////////////////////////////// //////////// DataTables ////////////// let cssTxt = GM_getResourceText("DataTablesCSS"); GM_addStyle(cssTxt); let head = document.head || document.getElementsByTagName('head')[0]; let link = document.createElement('link'); link.type = 'text/css'; link.rel = 'stylesheet'; link.href = 'https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css'; head.appendChild(link); (function() { let jQueryScript = document.createElement("script"); jQueryScript.src = "https://code.jquery.com/jquery-3.6.0.min.js"; jQueryScript.onload = () => { let dtScript = document.createElement("script"); dtScript.src = "https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"; // 加载完成后首次运行 dtScript.onload = () => { main(); }; document.body.appendChild(dtScript); }; document.body.appendChild(jQueryScript); })(); //////////// DataTables ////////////// ////////////////////////////////////// ////////////////////////////////////// /////////////// 触发器 //////////////// // 延时避免在 dtScript 和 jQueryScript 加载完成前就运行 setTimeout(function() { // if DOM changed, re-run the script let observer = new window.MutationObserver(function (mutations) { mutations.forEach(function () { const biliResultsTable = document.querySelector('#biliResultsTable'); if (biliResultsTable === null) { main(); } }); }); observer.observe(document.body, { childList: true, subtree: true }); }, 1000); /////////////// 触发器 //////////////// ////////////////////////////////////// ///////////////////////////////////// /////////////// main //////////////// function main() { // Create table with thead for DataTables let table = document.createElement('table'); table.id = "biliResultsTable"; let thead = document.createElement('thead'); let tbody = document.createElement('tbody'); let header = ["发布日期", "时长", "标题", "播放量", "UP主"]; let trHead = document.createElement('tr'); header.forEach(text => { let th = document.createElement('th'); th.textContent = text; trHead.appendChild(th); }); thead.appendChild(trHead); setTimeout(function() { let videoCards = document.querySelectorAll('.bili-video-card'); videoCards.forEach(videoCard => { let title = videoCard.querySelector('.bili-video-card__info--tit')?.textContent.trim(); let up = videoCard.querySelector('.bili-video-card__info--author')?.textContent.trim(); let playCount = videoCard.querySelector('.bili-video-card__stats--item > span')?.textContent.trim(); let danmakuCount = videoCard.querySelectorAll('.bili-video-card__stats--item > span')[1]?.textContent.trim(); let duration = videoCard.querySelector('.bili-video-card__stats__duration')?.textContent.trim(); let date = videoCard.querySelector('.bili-video-card__info--date')?.textContent.trim().replace('· ', ''); let link_video = videoCard.querySelectorAll('a')[0].getAttribute('href'); // UP主的link在targetDiv1页面有概率玄学报错,应该是异步逻辑的问题,用try之后基本正常 let link_up; try { link_up = videoCard.querySelectorAll('a')[2].getAttribute('href'); } catch (e) {} let tr = document.createElement('tr'); // 确保没有为空的数据 if ( (title !== undefined) && (up !== undefined) && (playCount !== undefined) && (danmakuCount !== undefined) && (duration !== undefined) && (date !== undefined) && (link_video !== undefined) ) { [date, duration, title, playCount, up].forEach(text => { let td = document.createElement('td'); td.textContent = text; tr.appendChild(td); }); let tdTitle = tr.querySelector('td:nth-child(3)'); tdTitle.innerHTML = `<a href="${link_video}" target="_blank">${title}</a>`; let tdUp = tr.querySelector('td:nth-child(5)'); tdUp.innerHTML = `<a href="${link_up}" target="_blank">${up}</a>`; // b230815.02 时长加粗 tr.querySelector('td:nth-child(2)').style.fontWeight = 'bold'; // b230815.02 标题用 `#00AEEC` 颜色 tr.querySelector('td:nth-child(3)').style.color = '#00AEEC'; tbody.appendChild(tr); } }); // console.log(videoCards); table.appendChild(thead); table.appendChild(tbody); // 回调获取异步数据,适用于下方 targetDiv2 的异步加载 if (tbody.childElementCount === 0) { main(); return; } // 搜索第一页的 DOM 结构与其他页不同,直接用这种方式即可匹配 let targetDiv1 = document.querySelector("#i_cecream > div > div:nth-child(2) > div.search-content.search-content--gray > div > div > div > div.video.i_wrapper.search-all-list"); if (targetDiv1 !== null) { targetDiv1.innerHTML = ''; targetDiv1.appendChild(table); } else { // 匹配除第一页外的其他 DOM 结构,第一页返回的结果是一次性全部加载的,而其他页是异步加载的 let targetDiv2_rm = document.querySelector("#i_cecream > div > div:nth-child(2) > div.search-content--gray.search-content > div > div > div.video-list.row"); targetDiv2_rm.innerHTML = ''; let targetDiv2_bott = document.querySelector("#i_cecream > div > div:nth-child(2) > div.search-content--gray.search-content > div > div > div.flex_center.mt_x50.mb_lg"); targetDiv2_bott.parentNode.insertBefore(table, targetDiv2_bott); // 每次翻页时,上一页的表格不会自动被覆盖而是叠加在一起,所以需要手动删除 let tables = document.querySelectorAll('#biliResultsTable'); if (tables.length > 1) { tables.forEach(table => { if (table !== tables[0]) { table.remove(); } }); } } // DataTables 的自定义排序算法 $.fn.dataTable.ext.type.order['duration-sort-pre'] = function (d) {return convertDurationToSeconds(d);}; $.fn.dataTable.ext.type.order['playcount-sort-pre'] = function (d) {return convertPlayCount(d);}; $.fn.dataTable.ext.type.order['date-sort-pre'] = function (d) {return convertDate(d);}; // Initialize DataTables $(table).DataTable({ "paging": false, "searching": false, "info": false, "columnDefs": [ { "type": "playcount-sort", "targets": 3 }, { "type": "duration-sort", "targets": 1 }, { "type": "date-sort", "targets": 0 } ] }); // b230815.02 去掉底边横线 GM_addStyle("table.dataTable.no-footer { border-bottom: 0px none !important; }"); // b230815.02 去掉表头横线 (因CSS复杂,所以创建白色色块覆盖) GM_addStyle(".dataTable thead th { border-bottom: 0px none !important; }"); // b230815.02 调大字号 GM_addStyle(`.dataTable { font-size: ${table_font_size}px !important; }`); }, 100); } /////////////// main //////////////// ///////////////////////////////////// /////////////////////////////////// /////// DataTable 的排序算法 //////// function convertDurationToSeconds(duration) { let parts = duration.split(':').map(part => parseInt(part, 10)); if (parts.length === 3) { return parts[0] * 3600 + parts[1] * 60 + parts[2]; } else if (parts.length === 2) { return parts[0] * 60 + parts[1]; } else { return NaN; } } function convertPlayCount(playCount) { if (playCount.includes('万')) { return parseFloat(playCount.replace('万', '')) * 10000; } else { return parseInt(playCount, 10); } } function convertDate(date) { const now = new Date(); if (date.includes('小时前')) { const hoursAgo = parseFloat(date.replace('小时前', '')); return now - hoursAgo * 3600 * 1000; // Convert hours to milliseconds } if (date === "昨天") { return now - 24 * 3600 * 1000; // 24 hours in milliseconds } if (date.includes('-')) { const parts = date.split('-').map(part => { return part.padStart(2, '0'); }); // If only month and day are given, use the current year. if (parts.length === 2) { parts.unshift(now.getFullYear().toString()); } // Create a new Date object and return its time value in milliseconds return new Date(parts.join('-')).getTime(); } } /////// DataTable 的排序算法 //////// /////////////////////////////////// console.log("JS script BiliBili-TextSearchList (BiliBili-文字搜索列表) loaded. See more details at https://github.com/SynRGB/BiliBili-TextSearchList");