// ==UserScript==
// @name 聚合搜索 - 增强版
// @description 整合百度、必应、谷歌搜索,支持无刷新自动翻页
// @version 0.4.0
// @author yinanno
// @website https://github.com/Yinbao77
// @match *://www.baidu.com/s*
// @match *://www.bing.com/search*
// @match *://cn.bing.com/search*
// @match *://www.google.com.hk/search*
// @match *://www.google.com/search*
// @namespace https://gf.qytechs.cn/users/1489016
// ==/UserScript==
// 主要搜索引擎配置
const mainSearchEngines = [
{
name: '百度',
searchUrl: 'https://www.baidu.com/s?wd=',
keyName: 'wd',
testUrl: /https:\/\/www\.baidu\.com\/s.*/,
nextPageSelector: '#page .n',
resultsSelector: '#content_left',
pageParamName: 'pn'
},
{
name: '必应',
searchUrl: 'https://www.bing.com/search?q=',
keyName: 'q',
testUrl: /https:\/\/(www|cn)\.bing\.com\/search.*/,
nextPageSelector: '.sb_pagN',
resultsSelector: '#b_results',
pageParamName: 'first'
},
{
name: 'Google',
searchUrl: 'https://www.google.com/search?q=',
keyName: 'q',
testUrl: /https:\/\/www\.google\.com(\.hk)?\/search.*/,
nextPageSelector: '#pnnext',
resultsSelector: '#search',
pageParamName: 'start'
}
];
// 专业搜索配置
const specialSearches = [
{
name: '知乎',
searchUrl: 'https://www.zhihu.com/search?type=content&q=',
},
{
name: 'GitHub',
searchUrl: 'https://github.com/search?q=',
},
{
name: 'Stack Overflow',
searchUrl: 'https://stackoverflow.com/search?q=',
},
{
name: 'B站',
searchUrl: 'https://search.bilibili.com/all?keyword=',
}
];
// 增强的URL参数获取函数 - 支持特殊字符
function getQueryVariable(variable) {
try {
const urlParams = new URLSearchParams(window.location.search);
const value = urlParams.get(variable);
return value ? decodeURIComponent(value) : '';
} catch (e) {
// 如果URLSearchParams失败,使用传统方法
const query = window.location.search.substring(1);
const vars = query.split('&');
for (let i = 0; i < vars.length; i++) {
const pair = vars[i].split('=');
if (pair[0] === variable) {
try {
return decodeURIComponent(pair[1] || '');
} catch (e) {
return pair[1] || '';
}
}
}
return '';
}
}
// 从URL中获取搜索关键词
function getKeywords() {
const currentUrl = window.location.href;
const engine = mainSearchEngines.find(item => item.testUrl.test(currentUrl));
if (engine) {
const keywords = getQueryVariable(engine.keyName);
return keywords.trim();
}
return '';
}
// 获取当前搜索引擎
function getCurrentEngine() {
const currentUrl = window.location.href;
return mainSearchEngines.find(item => item.testUrl.test(currentUrl));
}
// 安全的URL编码函数
function safeEncodeURIComponent(str) {
try {
return encodeURIComponent(str);
} catch (e) {
// 如果编码失败,尝试替换特殊字符
return str.replace(/[^\w\s\u4e00-\u9fff]/g, function(char) {
try {
return encodeURIComponent(char);
} catch (e) {
return char;
}
});
}
}
// 自动翻页相关变量
let isAutoPageEnabled = true;
let isLoading = false;
let currentPage = 1;
let maxPages = 10; // 最多自动翻10页,防止无限加载
// 获取下一页URL
function getNextPageUrl() {
const currentEngine = getCurrentEngine();
if (!currentEngine) return null;
const currentUrl = new URL(window.location.href);
const params = currentUrl.searchParams;
let nextPageValue;
if (currentEngine.name === '百度') {
// 百度: pn参数,每页10个结果
const currentPn = parseInt(params.get('pn') || '0');
nextPageValue = currentPn + 10;
params.set('pn', nextPageValue.toString());
} else if (currentEngine.name === '必应') {
// 必应: first参数,每页约10个结果
const currentFirst = parseInt(params.get('first') || '1');
nextPageValue = currentFirst + 10;
params.set('first', nextPageValue.toString());
} else if (currentEngine.name === 'Google') {
// Google: start参数,每页10个结果
const currentStart = parseInt(params.get('start') || '0');
nextPageValue = currentStart + 10;
params.set('start', nextPageValue.toString());
}
return currentUrl.toString();
}
// 加载下一页内容
async function loadNextPage() {
if (isLoading || currentPage >= maxPages) return;
const nextUrl = getNextPageUrl();
if (!nextUrl) return;
isLoading = true;
currentPage++;
// 显示加载状态
showLoadingIndicator();
try {
const response = await fetch(nextUrl);
const html = await response.text();
// 创建临时DOM来解析HTML
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const currentEngine = getCurrentEngine();
const newResults = doc.querySelector(currentEngine.resultsSelector);
if (newResults) {
// 获取新结果的子元素
const newItems = Array.from(newResults.children);
const currentResults = document.querySelector(currentEngine.resultsSelector);
if (currentResults && newItems.length > 0) {
// 添加分页标识
const pageIndicator = document.createElement('div');
pageIndicator.style = `
margin: 20px 0;
padding: 10px;
background: linear-gradient(90deg, #f0f0f0, #e0e0e0, #f0f0f0);
text-align: center;
font-size: 14px;
color: #666;
border-radius: 5px;
position: relative;
`;
pageIndicator.innerHTML = `━━━ 第 ${currentPage} 页 ━━━`;
currentResults.appendChild(pageIndicator);
// 添加新结果
newItems.forEach(item => {
// 跳过分页导航等无关元素
if (item.classList.contains('result') ||
item.classList.contains('b_algo') ||
item.classList.contains('g') ||
item.id.includes('result') ||
!item.classList.contains('page')) {
currentResults.appendChild(item);
}
});
// 更新URL历史记录(不刷新页面)
history.pushState({page: currentPage}, '', nextUrl);
console.log(`已加载第 ${currentPage} 页内容`);
}
}
} catch (error) {
console.error('加载下一页失败:', error);
} finally {
isLoading = false;
hideLoadingIndicator();
}
}
// 显示加载指示器
function showLoadingIndicator() {
let indicator = document.getElementById('loading-indicator');
if (!indicator) {
indicator = document.createElement('div');
indicator.id = 'loading-indicator';
indicator.style = `
position: fixed;
bottom: 50px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px 20px;
border-radius: 20px;
font-size: 14px;
z-index: 10000;
animation: fadeIn 0.3s ease-in-out;
`;
indicator.innerHTML = '🔄 大宝子疯狂加载下一页...';
document.body.appendChild(indicator);
// 添加CSS动画
if (!document.getElementById('loading-animation-style')) {
const style = document.createElement('style');
style.id = 'loading-animation-style';
style.textContent = `
@keyframes fadeIn {
from { opacity: 0; transform: translateX(-50%) translateY(10px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes fadeOut {
from { opacity: 1; transform: translateX(-50%) translateY(0); }
to { opacity: 0; transform: translateX(-50%) translateY(-10px); }
}
`;
document.head.appendChild(style);
}
}
indicator.style.display = 'block';
}
// 隐藏加载指示器
function hideLoadingIndicator() {
const indicator = document.getElementById('loading-indicator');
if (indicator) {
indicator.style.animation = 'fadeOut 0.3s ease-in-out';
setTimeout(() => {
indicator.style.display = 'none';
}, 300);
}
}
// 检测是否滚动到底部
function isScrolledToBottom() {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
// 提前200px开始加载
return scrollTop + windowHeight >= documentHeight - 200;
}
// 滚动事件监听
function initAutoPageScroll() {
let scrollTimer = null;
window.addEventListener('scroll', () => {
if (scrollTimer) clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
if (isAutoPageEnabled && isScrolledToBottom() && !isLoading) {
loadNextPage();
}
}, 100); // 防抖,避免频繁触发
});
}
function jumpToSearch(targetUrl) {
const keywords = getKeywords();
if (!keywords) {
alert('未检测到搜索关键词');
return;
}
const encodedKeywords = safeEncodeURIComponent(keywords);
window.open(targetUrl + encodedKeywords, '_blank');
}
// 添加搜索框
function addBox() {
const currentEngine = getCurrentEngine();
const div = document.createElement('div');
div.id = 'search-app-box';
div.style = `
position: fixed;
top: 50%;
left: 20px;
transform: translateY(-50%);
width: 120px;
background-color: #EEEEEE;
font-size: 12px;
z-index: 99999;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
padding-bottom: 10px;
font-family: 'Microsoft YaHei', Arial, sans-serif;
`;
document.body.appendChild(div);
// 作者信息
const author = document.createElement('div');
author.innerText = "by 大宝子丶";
author.style = "text-align: center; font-size: 12px; color: #666; margin: 8px 0;";
div.appendChild(author);
// 标题
const title = document.createElement('span');
title.innerText = "🔍聚合搜索";
title.style = "display: block; text-align: center; margin: 10px 0; font-size: 14px; font-weight: bold; color: #333;";
div.appendChild(title);
// 主要搜索引擎列表
mainSearchEngines.forEach(engine => {
const a = document.createElement('a');
a.href = 'javascript:;';
a.innerText = engine.name;
// 当前搜索引擎高亮显示
if (currentEngine && currentEngine.name === engine.name) {
a.style = `
display: block;
padding: 8px 0;
text-align: center;
color: #fff;
text-decoration: none;
border-top: 1px solid #ddd;
background-color: #4CAF50;
font-weight: bold;
`;
} else {
a.style = `
display: block;
padding: 8px 0;
text-align: center;
color: #333;
text-decoration: none;
border-top: 1px solid #ddd;
transition: background-color 0.2s;
`;
// 添加悬停效果
a.onmouseover = function() {
this.style.backgroundColor = '#f5f5f5';
};
a.onmouseout = function() {
this.style.backgroundColor = '';
};
}
a.onclick = () => {
if (currentEngine && currentEngine.name === engine.name) {
return; // 当前引擎不需要跳转
}
jumpToSearch(engine.searchUrl);
};
div.appendChild(a);
});
// 自动翻页开关
const autoPageToggle = document.createElement('div');
autoPageToggle.style = `
border-top: 1px solid #ddd;
margin: 5px 0;
text-align: center;
font-size: 10px;
color: #333;
padding: 8px 0;
cursor: pointer;
background-color: ${isAutoPageEnabled ? '#e8f5e8' : '#f5f5f5'};
transition: background-color 0.2s;
`;
autoPageToggle.innerHTML = `🔄 自动翻页: ${isAutoPageEnabled ? 'ON' : 'OFF'}`;
autoPageToggle.onclick = () => {
isAutoPageEnabled = !isAutoPageEnabled;
autoPageToggle.innerHTML = `🔄 自动翻页: ${isAutoPageEnabled ? 'ON' : 'OFF'}`;
autoPageToggle.style.backgroundColor = isAutoPageEnabled ? '#e8f5e8' : '#f5f5f5';
// 保存设置到localStorage
try {
localStorage.setItem('autoPageEnabled', isAutoPageEnabled.toString());
} catch (e) {
// 忽略localStorage错误
}
};
div.appendChild(autoPageToggle);
// 分割线
const separator = document.createElement('div');
separator.style = `
border-top: 1px solid #ddd;
margin: 5px 0;
text-align: center;
font-size: 10px;
color: #999;
padding: 5px 0;
`;
separator.innerText = "专业搜索";
div.appendChild(separator);
// 专业搜索列表
specialSearches.forEach(search => {
const a = document.createElement('a');
a.href = 'javascript:;';
a.innerText = search.name;
a.style = `
display: block;
padding: 6px 0;
text-align: center;
color: #666;
text-decoration: none;
border-top: 1px solid #eee;
font-size: 11px;
transition: background-color 0.2s;
`;
// 添加悬停效果
a.onmouseover = function() {
this.style.backgroundColor = '#f0f0f0';
this.style.color = '#333';
};
a.onmouseout = function() {
this.style.backgroundColor = '';
this.style.color = '#666';
};
a.onclick = () => {
jumpToSearch(search.searchUrl);
};
div.appendChild(a);
});
// 添加拖拽功能
let isDragging = false;
let startX, startY, initialX, initialY;
div.onmousedown = function(e) {
if (e.target === div || e.target === title || e.target === author) {
isDragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = div.getBoundingClientRect();
initialX = rect.left;
initialY = rect.top;
div.style.cursor = 'move';
e.preventDefault();
}
};
document.onmousemove = function(e) {
if (isDragging) {
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
div.style.left = (initialX + deltaX) + 'px';
div.style.top = (initialY + deltaY) + 'px';
// 保持左侧定位
}
};
document.onmouseup = function() {
if (isDragging) {
isDragging = false;
div.style.cursor = '';
}
};
}
// 检测页面是否为搜索结果页
function isSearchPage() {
return mainSearchEngines.some(engine => engine.testUrl.test(window.location.href));
}
// 主函数
(function() {
'use strict';
if (!isSearchPage()) {
return;
}
// 从localStorage读取自动翻页设置
try {
const saved = localStorage.getItem('autoPageEnabled');
if (saved !== null) {
isAutoPageEnabled = saved === 'true';
}
} catch (e) {
// 忽略localStorage错误
}
// 延迟执行,确保页面加载完成
setTimeout(() => {
// 检查是否已存在搜索框,避免重复添加
if (!document.getElementById('search-app-box')) {
addBox();
}
// 初始化自动翻页功能
initAutoPageScroll();
console.log('聚合搜索增强版已加载');
console.log('自动翻页功能已启用,滚动到底部将自动加载下一页');
}, 1000);
})();