聚合搜索V4

整合百度、F搜、Google、微信、Bing、知乎、知网空间搜索,提高搜索效率,可以自由添加和删除搜索引擎

目前为 2022-11-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 聚合搜索V4
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.0
  5. // @description 整合百度、F搜、Google、微信、Bing、知乎、知网空间搜索,提高搜索效率,可以自由添加和删除搜索引擎
  6. // @author Liao Brant
  7. // @match *://*/*
  8. // @grant unsafeWindow
  9. // @grant window.onload
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // @run-at document-body
  14.  
  15. // @license MIT
  16. // ==/UserScript==
  17.  
  18. const defaultUrlMap = [
  19. {
  20. name: '百度',
  21. searchUrl: 'https://www.baidu.com/s?wd=%s',
  22. keyName: 'wd',
  23. },
  24. {
  25. name: 'F搜',
  26. searchUrl: 'https://fsoufsou.com/search?q=%s',
  27. keyName: 'q',
  28. },
  29. {
  30. name: 'Google',
  31. searchUrl: 'https://www.google.com/search?q=%s',
  32. keyName: 'q',
  33. },
  34. {
  35. name: '开发者',
  36. searchUrl: 'https://kaifa.baidu.com/searchPage?wd=%s&module=SEARCH',
  37. keyName: 'wd',
  38. },
  39. {
  40. name: '微信文章',
  41. searchUrl: 'https://weixin.sogou.com/weixin?type=2&s_from=input&query=%s',
  42. keyName: 'query',
  43. },
  44. {
  45. name: 'Bing',
  46. searchUrl: 'https://www.bing.com/search?ensearch=0&q=%s',
  47. keyName: 'q',
  48. },
  49. {
  50. name: '知乎',
  51. searchUrl: 'https://www.zhihu.com/search?type=content&q=%s',
  52. keyName: 'q',
  53. },
  54. {
  55. name: '知网空间',
  56. searchUrl: 'https://search.cnki.com.cn/Search/Result?content=%s',
  57. keyName: 'q',
  58. },
  59. ];
  60.  
  61. // 获取搜索配置
  62. function getSearchConfig() {
  63. const urlConfig = GM_getValue('SearchList');
  64. if (!urlConfig || !urlConfig.length) {
  65. // 默认的网址配置
  66. GM_setValue('SearchList', defaultUrlMap);
  67. return defaultUrlMap;
  68. } else {
  69. return urlConfig;
  70. }
  71. }
  72.  
  73. // 搜索网址配置
  74. let urlMapping = getSearchConfig();
  75.  
  76. // JS获取url参数
  77. function getQueryVariable(variable) {
  78. let query = window.location.search.substring(1);
  79. let pairs = query.split('&');
  80. for (let pair of pairs) {
  81. let [key, value] = pair.split('=');
  82. if (key == variable) {
  83. return decodeURIComponent(value);
  84. }
  85. }
  86. return null;
  87. }
  88.  
  89. // 从url中获取搜索关键词
  90. function getKeywords() {
  91. let keywords = '';
  92. for (let item of urlMapping) {
  93. // 判断url是否符合搜索配置的条件
  94. if (item.searchUrl.includes(window.location.hostname + window.location.pathname)) {
  95. keywords = getQueryVariable(item.keyName);
  96. break;
  97. }
  98. }
  99. console.log(keywords);
  100. return keywords;
  101. }
  102.  
  103. // 域名
  104. const hostname = window.location.hostname;
  105.  
  106. let isBlank = GM_getValue('isBlank');
  107.  
  108. console.log('新标签页打开?', isBlank);
  109. if (isBlank === undefined) {
  110. GM_setValue('isBlank', false);
  111. isBlank = false;
  112. }
  113.  
  114. // 改变打开搜索引擎的方式
  115. const engine = document.getElementsByClassName('search-engine-a');
  116. function triggerAttribute(value) {
  117. for (const item of engine) {
  118. item.target = value;
  119. }
  120. }
  121.  
  122. // 适配火狐浏览器的百度搜索
  123. const isFirefox = () => {
  124. if (navigator.userAgent.indexOf('Firefox') > 0) {
  125. console.warn('[ Firefox ] 🚀');
  126. urlMapping[0].searchUrl = 'https://www.baidu.com/baidu?wd=%s';
  127. } else {
  128. }
  129. };
  130.  
  131. // 适配cn.bing.com的必应域名
  132. const cnBing = {
  133. name: 'Bing',
  134. searchUrl: 'https://cn.bing.com/search?q=%s',
  135. keyName: 'q',
  136. };
  137. // 匹配到cn.bing就修改必应配置对象
  138. if (window.location.hostname === 'cn.bing.com') {
  139. for (let item of urlMapping) {
  140. if (item.name === 'Bing') {
  141. item = cnBing;
  142. }
  143. }
  144. }
  145.  
  146. // 添加自定义样式
  147. function addStyle() {
  148. const styleSheet = document.createElement('style');
  149. document.head.appendChild(styleSheet);
  150.  
  151. styleSheet.textContent = `
  152. .search-container {
  153. position: fixed;
  154. top: 160px;
  155. left: 20px;
  156. width: 100px;
  157. background-color: #EEEEEE;
  158. font-size: 12px;
  159. z-index: 99999;
  160. }
  161.  
  162. .search-title {
  163. display: block;
  164. text-align: center;
  165. margin-top: 10px;
  166. margin-bottom: 5px;
  167. font-size: 14px;
  168. font-weight: bold;
  169. -webkit-user-select:none;
  170. -moz-user-select:none;
  171. -ms-user-select:none;
  172. user-select:none;
  173. }
  174.  
  175. .search-list__item {
  176. display: flex;
  177. justify-content: space-between;
  178. align-items: center;
  179. color: #333333 !important;
  180. padding-right: 6px;
  181. }
  182. .search-list__item:hover {
  183. color: #ffffff !important;
  184. background-color: #666666;
  185. }
  186.  
  187. .search-list__item .search-engine-a {
  188. color: inherit !important;
  189. padding: 10px 6px 10px 20px;
  190. text-decoration: none;
  191. flex-grow: 2;
  192. }
  193.  
  194. .search-list__close {
  195. width: 12px;
  196. height: 12px;
  197. opacity: 0;
  198. z-index: 10;
  199. }
  200. .search-list__close:hover {
  201. opacity: 1;
  202. }
  203.  
  204. #search-black_over {
  205. content: "";
  206. position: absolute;
  207. top: 0;
  208. left: 0;
  209. width: 100vw;
  210. height: 100%;
  211. // background-color: #F0F5FF;
  212. background-color: #f0f5ffe3;
  213. z-index: 999;
  214. }
  215. #search-popup {
  216. position: fixed;
  217. top: 50vh;
  218. left: 50vw;
  219. background-color: #FFFFFF;
  220. width: 460px;
  221. transform: translate(-50%, -50%);
  222. z-index: 1000;
  223. }
  224. #setting-engine-ul {
  225. padding: 20px;
  226. height: 60vh;
  227. min-height: 400px;
  228. overflow: auto;
  229. }
  230. #setting-engine-ul::-webkit-scrollbar {
  231. width: 10px;
  232. }
  233.  
  234. #setting-engine-ul::-webkit-scrollbar-thumb {
  235. background-color: #d9d9d9;
  236. border-radius: 9999em;
  237. }
  238. .setting-engine-item{
  239. margin-bottom: 10px;
  240. padding: 10px 0;
  241. border-radius: 4px !important;
  242. box-shadow: 0 2px 8px rgb(22 93 255 / 8%) !important;
  243. }
  244. .engine-info{
  245. margin: 5px;
  246. }
  247. .engine-label{
  248. width: 70px;
  249. display: inline-block;
  250. }
  251. .engine-info input {
  252. width: 275px;
  253. }
  254.  
  255. .setting-engine-header, .setting-engine-footer{
  256. display: flex;
  257. padding: 15px;
  258. justify-content: space-between;
  259. align-items: center;
  260. border-top: 1px solid #e8e8e8;
  261. }
  262. `;
  263. }
  264.  
  265. // 添加节点
  266. function addBox() {
  267. // 主元素
  268. const div = document.createElement('div');
  269. div.id = 'search-app-box';
  270. div.className = 'search-container';
  271. document.body.insertAdjacentElement('afterBegin', div);
  272.  
  273. // 标题
  274. let title = document.createElement('span');
  275. title.innerText = '聚合搜索';
  276. title.className = 'search-title';
  277. title.style.textDecoration = isBlank ? 'underline' : '';
  278. title.ondblclick = () => {
  279. title.style.textDecoration = !isBlank ? 'underline' : '';
  280. GM_setValue('isBlank', !isBlank);
  281. isBlank = !isBlank;
  282. triggerAttribute(isBlank ? '_blank' : '');
  283. };
  284. div.appendChild(title);
  285.  
  286. // 搜索列表
  287. for (let index in urlMapping) {
  288. const item = urlMapping[index];
  289. // 单个搜索引擎
  290. const searchItem = document.createElement('div');
  291. searchItem.className = 'search-list__item';
  292. const a_target = !item.searchUrl.includes(hostname) && isBlank ? '_blank' : '';
  293.  
  294. searchItem.innerHTML = `
  295. <a class="search-engine-a" href="${item.searchUrl.replace('%s', getKeywords())}" ${a_target}>
  296. ${item.name}
  297. </a>
  298. `;
  299. // 移除按钮
  300. const closeImg = document.createElement('img');
  301. closeImg.className = 'search-list__close';
  302. closeImg.src =
  303. 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjY4ODU1MDA0ODc1IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjI2ODEiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkxIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik01MDkuMjYyNzEzIDUuNDc0NTc0YzI4MS4yNzIxNjIgMCA1MDkuMjYyNzEzIDIyOC4wMjIzOCA1MDkuMjYyNzEzIDUwOS4yNjI3MTMgMCAyODEuMjcyMTYyLTIyNy45OTA1NTEgNTA5LjI2MjcxMy01MDkuMjYyNzEzIDUwOS4yNjI3MTNzLTUwOS4yNjI3MTMtMjI3Ljk5MDU1MS01MDkuMjYyNzEzLTUwOS4yNjI3MTNjMC0yODEuMjQwMzMzIDIyNy45OTA1NTEtNTA5LjI2MjcxMyA1MDkuMjYyNzEzLTUwOS4yNjI3MTN6IG0xMzUuMDUwMTA2IDI3OC43MjU4NDlMNTA5LjI2MjcxMyA0MTkuMjUwNTI4bC0xMzUuMDUwMTA2LTEzNS4wNTAxMDUtOTAuMDEyMTg0IDkwLjAxMjE4NEw0MTkuMTg2ODcxIDUwOS4yNjI3MTNsLTEzNS4wMTgyNzcgMTM1LjA4MTkzNSA5MC4wMTIxODQgOTAuMDEyMTg0TDUwOS4yNjI3MTMgNTk5LjI3NDg5N2wxMzUuMDUwMTA2IDEzNS4wNTAxMDYgOTAuMDEyMTg0LTkwLjAxMjE4NEw1OTkuMjc0ODk3IDUwOS4yNjI3MTNsMTM1LjA1MDEwNi0xMzUuMDUwMTA2LTkwLjAxMjE4NC05MC4wMTIxODR6IiBwLWlkPSIyNjgyIiBkYXRhLXNwbS1hbmNob3ItaWQ9ImEzMTN4Ljc3ODEwNjkuMC5pMCIgY2xhc3M9IiIgZmlsbD0iI2UxNjUzMSI+PC9wYXRoPjwvc3ZnPg==';
  304. // 点击按钮移除节点
  305. closeImg.onclick = function () {
  306. urlMapping.splice(index, 1);
  307. searchItem.remove();
  308. GM_setValue('SearchList', urlMapping);
  309. };
  310.  
  311. searchItem.appendChild(closeImg);
  312. div.appendChild(searchItem);
  313. }
  314. }
  315. // 重新生成节点
  316. function addBoxAgain() {
  317. document.getElementById('search-app-box')?.remove();
  318. addBox();
  319. }
  320. // 关闭面板
  321. closePanel = () => {
  322. document.getElementById('search-black_over')?.remove();
  323. document.getElementById('search-popup')?.remove();
  324. };
  325. // 新增搜索引擎
  326. addEngine = () => {
  327. const engineUl = document.getElementById('setting-engine-ul');
  328. const li = document.createElement('li');
  329. li.className = 'setting-engine-item';
  330. li.innerHTML = `
  331. <div class="engine-info">
  332. <label class="engine-label">引擎名称</label>
  333. <input class="GF-engine-name__input" />
  334. </div>
  335. <div class="engine-info">
  336. <label class="engine-label">引擎地址</label>
  337. <input class="GF-engine-url__input"/>
  338. </div>
  339. <div class="engine-info">
  340. <label class="engine-label">搜索参数</label>
  341. <input class="GF-engine-key__input" />
  342. </div>
  343. `;
  344. engineUl.appendChild(li);
  345. };
  346. // 重置配置
  347. resetEngineConfig = () => {
  348. urlMapping = defaultUrlMap;
  349. addBoxAgain();
  350. closePanel();
  351. };
  352. // 保存配置
  353. saveEngineConfig = () => {
  354. const list = [];
  355. const names = document.getElementsByClassName('GF-engine-name__input');
  356. const urls = document.getElementsByClassName('GF-engine-url__input');
  357. const keys = document.getElementsByClassName('GF-engine-key__input');
  358.  
  359. for (let index = 0; index < names.length; index++) {
  360. list.push({
  361. name: names[index]?.value,
  362. searchUrl: urls[index]?.value,
  363. keyName: keys[index]?.value,
  364. });
  365. }
  366.  
  367. urlMapping = list;
  368. GM_setValue('SearchList', list);
  369. addBoxAgain();
  370. closePanel();
  371. };
  372. // 创建设置面板
  373. function createSettingPanel() {
  374. // 遮罩
  375. const over = document.createElement('div');
  376. over.id = 'search-black_over';
  377. // 弹窗
  378. const popup = document.createElement('div');
  379. popup.id = 'search-popup';
  380.  
  381. let engineList = '';
  382. urlMapping.forEach((item) => {
  383. engineList += `<li class="setting-engine-item">
  384. <div class="engine-info">
  385. <label class="engine-label">引擎名称</label>
  386. <input class="GF-engine-name__input" value="${item.name}" />
  387. </div>
  388. <div class="engine-info">
  389. <label class="engine-label">引擎地址</label>
  390. <input class="GF-engine-url__input" value="${item.searchUrl}" />
  391. </div>
  392. <div class="engine-info">
  393. <label class="engine-label">搜索参数</label>
  394. <input class="GF-engine-key__input" value="${item.keyName}" />
  395. </div>
  396. </li>`;
  397. });
  398.  
  399. popup.innerHTML = `
  400. <div class="setting-engine-header">
  401. <button id="setting-engine-reset" onclick="closePanel()">关闭</button>
  402. <button id="setting-engine-save" onclick="addEngine()">新增</button>
  403. </div>
  404. <ul id="setting-engine-ul">${engineList}</ul>
  405. <div class="setting-engine-footer">
  406. <button id="setting-engine-reset" onclick="resetEngineConfig()">重置</button>
  407. <button id="setting-engine-save" onclick="saveEngineConfig()">保存</button>
  408. </div>
  409. `;
  410.  
  411. document.body.insertAdjacentElement('afterBegin', popup);
  412. document.body.insertAdjacentElement('afterBegin', over);
  413. }
  414. // 添加油猴设置菜单
  415. function addUserSetting() {
  416. GM_registerMenuCommand('设置', createSettingPanel);
  417. }
  418. // 初始化
  419. function init() {
  420. if (!getKeywords()) return;
  421. addStyle();
  422. isFirefox();
  423. addBox();
  424. addUserSetting();
  425. }
  426.  
  427. (function () {
  428. 'use strict';
  429. window.onload = init();
  430. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址