Greasy Fork 支持简体中文。

[SNOLAB] Alt + 123... Searching Results Links List Batch Open

To quickly understand a field, press Alt+1 ...2,3,4...Alt+5 on the search page of Google or Bing to open the search results of the first 2 nth power items and copy the opened ones link. Currently supports: Google, Bing, Zhihu.

目前為 2022-12-27 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name [SNOLAB] Alt + 123... Searching Results Links List Batch Open
  3. // @name:zh [雪星实验室] Alt + 123... 一键批量打开谷歌必应搜索的前2的n次方项搜索结果
  4. // @namespace snomiao@gmail.com
  5. // @version 1.0.4
  6. // @description To quickly understand a field, press Alt+1 ...2,3,4...Alt+5 on the search page of Google or Bing to open the search results of the first 2 nth power items and copy the opened ones link. Currently supports: Google, Bing, Zhihu.
  7. // @description:zh 快速了解一个领域用,在谷歌或必应的搜索页面 按 Alt+1 ...2,3,4... Alt+5 将会打开前2的n次方项的搜索结果,并复制打开的链接。目前支持:谷歌、必应、知乎。
  8. // @author snomiao
  9. // @match *://google.com/*
  10. // @match *://bing.com/*
  11. // @match *://youtube.com/*
  12. // @match *://zhihu.com/*
  13. // @match *://and-all-searching-results.com/*
  14. // @match *://*/*
  15. // @grant none
  16. // ==/UserScript==
  17.  
  18. /*
  19. TODO 修复列表识别算法于Google学术搜索的错误
  20. 错误例子
  21. [中国能源统计年鉴 - Google 学术搜索]( https://scholar.google.com/scholar?q=%E4%B8%AD%E5%9B%BD%E8%83%BD%E6%BA%90%E7%BB%9F%E8%AE%A1%E5%B9%B4%E9%89%B4&hl=zh-CN&as_sdt=0&as_vis=1&oi=scholart )
  22. */
  23.  
  24. globalThis.LinksListBatchUnload?.();
  25. globalThis.LinksListBatchUnload = hotkeyMapper({
  26. "alt+1": () => tryOpenLinkByCount(2 ** 1),
  27. "alt+2": () => tryOpenLinkByCount(2 ** 2),
  28. "alt+3": () => tryOpenLinkByCount(2 ** 3),
  29. "alt+4": () => tryOpenLinkByCount(2 ** 4),
  30. "alt+5": () => tryOpenLinkByCount(2 ** 5),
  31. "alt+6": () => tryOpenLinkByCount(2 ** 6),
  32. "alt+7": () => tryOpenLinkByCount(2 ** 7),
  33. "alt+8": () => tryOpenLinkByCount(2 ** 8),
  34. "alt+9": () => tryOpenLinkByCount(2 ** 9),
  35. "shift+alt+1": () => tryCopyLinkByCount(2 ** 1),
  36. "shift+alt+2": () => tryCopyLinkByCount(2 ** 2),
  37. "shift+alt+3": () => tryCopyLinkByCount(2 ** 3),
  38. "shift+alt+4": () => tryCopyLinkByCount(2 ** 4),
  39. "shift+alt+5": () => tryCopyLinkByCount(2 ** 5),
  40. "shift+alt+6": () => tryCopyLinkByCount(2 ** 6),
  41. "shift+alt+7": () => tryCopyLinkByCount(2 ** 7),
  42. "shift+alt+8": () => tryCopyLinkByCount(2 ** 8),
  43. "shift+alt+9": () => tryCopyLinkByCount(2 ** 9),
  44. });
  45.  
  46. // console.log('[谷歌一键打开前N项搜索结果] LOADED');
  47. // 链接列获取();
  48.  
  49. function tryOpenLinkByCount(count = 1) {
  50. const links = getLinks(count);
  51. copyLinks(links);
  52. //在 chrome 下需要反过来才能让第1个链接先打开。。;
  53. links
  54. .map(({ 链接 }) => 链接)
  55. .reverse()
  56. .map(链接打开);
  57. }
  58.  
  59. function tryCopyLinkByCount(count = 1) {
  60. const links = getLinks(count);
  61. copyLinks(links);
  62. return links;
  63. }
  64.  
  65. function copyLinks(links) {
  66. textCopy(links.map(md格式链接生成).join("\n\n"));
  67. }
  68.  
  69. function 最长共列阵输出(矩阵, a, b, x, y) {
  70. return !x || !y
  71. ? ""
  72. : a[x - 1] === b[y - 1]
  73. ? 最长共列阵输出(矩阵, a, b, x - 1, y - 1) + a[x - 1]
  74. : 矩阵[y][x - 1] > 矩阵[y - 1][x]
  75. ? 最长共列阵输出(矩阵, a, b, x - 1, y)
  76. : 最长共列阵输出(矩阵, a, b, x, y - 1);
  77. }
  78. function 最长共列(a, b) {
  79. const w = a.length,
  80. h = b.length;
  81. const m = Array(1 + h)
  82. .fill(0)
  83. .map(() => Array(1 + w).fill(0));
  84. for (let y = 0; y < h; y++)
  85. for (let x = 0; x < w; x++)
  86. m[1 + y][1 + x] =
  87. a[x] === b[y]
  88. ? m[y][x] + 1
  89. : Math.max(m[y][1 + x], m[1 + y][x]);
  90. false && console.table(m);
  91. return 最长共列阵输出(m, a, b, w, h);
  92. }
  93. function 元素特征列取(e) {
  94. return [...e.querySelectorAll("*")].map((e) => e?.tagName + e?.className);
  95. }
  96. function 特征元素筛函数(e) {
  97. return !"style script span".toUpperCase().split(" ").includes(e.tagName);
  98. }
  99. function 元素列表强度(e) {
  100. return (
  101. e.textContent.length *
  102. (e.children.length - 1) *
  103. [...e.children]
  104. .filter(特征元素筛函数)
  105. .map(元素特征列取)
  106. .map((_, i, a) => 最长共列(a[i], a[i + 1] || []))
  107. .reduce((前, 后) => + 后.length, 0)
  108. );
  109. }
  110. function 列元素列提取() {
  111. return [...document.querySelectorAll("div,dl,ul,ol,tbody,table,td")]
  112. .filter((e) => e.children.length > 1)
  113. .map((元素) => ({
  114. 元素,
  115. 强度: 元素列表强度(元素),
  116. 特征列: [...元素.children]
  117. .filter(特征元素筛函数)
  118. // .filter((e) => !["style", "script", "span"].includes(e.tagName))
  119. .map(元素特征列取),
  120. }))
  121. .sort((a, b) => -(a.强度 - b.强度));
  122. }
  123. function 标链元素提取(e) {
  124. return (
  125. e?.querySelector("dd,dt,h1,h2,h3,h4,h5,h6")?.querySelector("a") ||
  126. [...e?.querySelectorAll("a")]?.filter((e) =>
  127. e.querySelector("dd,dt,h1,h2,h3,h4,h5,h6")
  128. )?.[0] ||
  129. e?.querySelector("a")
  130. );
  131. }
  132. function 标链提取(元素) {
  133. return {
  134. 元素,
  135. 标题: 元素?.textContent?.replace(/\s+/g, " ").trim(),
  136. 链接: 元素?.href,
  137. };
  138. }
  139. function 子列父亲节点筛函数(eFa, i, a) {
  140. return !a.some((eSon, j) => i != j && eFa.元素.contains(eSon.元素));
  141. }
  142. function 页主标链列提取() {
  143. return 列元素列提取()
  144. .filter(子列父亲节点筛函数)
  145. .filter((e, _, a) => e.强度 > a[0].强度 * 0.1) // 按最强者 10% 筛选
  146. .sort((a, b) => a.元素.offsetTop - b.元素.offsetTop) // 按垂直位置对比
  147. .filter(({ 元素, 强度, 特征列 }) => !console.log(元素, 强度, 特征列)) // 元素提取debug
  148. .map(({ 元素 }) => 元素) // 元素提取
  149. .flatMap((e) => [...e?.children]?.map?.(标链元素提取) || [])
  150. .filter((e) => e)
  151. .map(标链提取)
  152. .filter(({ 标题, 链接 }) => 标题)
  153. .filter(({ 标题, 链接 }) => 链接?.match?.(/^http/));
  154. }
  155. function textCopy(内容) {
  156. const input = document.createElement("textarea");
  157. input.setAttribute("readonly", "readonly");
  158. input.setAttribute("value", 内容);
  159. input.innerHTML = 内容;
  160. input.setAttribute("style", "position: fixed; top:0; left:0;z-index: 9999");
  161. document.body.appendChild(input);
  162. input.select();
  163. input.setSelectionRange(0, input.value.length);
  164. if (document.execCommand("copy")) {
  165. document.execCommand("copy");
  166. console.log(`长度为${内容.length}的内容已复制`);
  167. } else {
  168. // alert(`长度为${内容.length}的内容复制失败,请检查浏览器配置,或在页面上先按一下键盘任意键解除剪贴板保护`);
  169. }
  170. document.body.removeChild(input);
  171. }
  172. const 已打开过的链接 = {};
  173. function 链接打开(链接) {
  174. if (!已打开过的链接[链接]) {
  175. window.open(链接);
  176. }
  177. 已打开过的链接[链接] = 1;
  178. }
  179. function getLinks(数量 = Infinity) {
  180. return 页主标链列提取().slice(0, 数量);
  181. }
  182. function md格式链接生成({ 标题, 链接 }) {
  183. return `- [${标题}](${链接})`;
  184. }
  185. function hotkeyMapper(mapping) {
  186. const handlers = mapObjIndexed(hotkeyHandler, mapping);
  187. mapObjIndexed(
  188. (handler) => window.addEventListener("keydown", handler),
  189. handlers
  190. );
  191. return function unload() {
  192. return mapObjIndexed(
  193. (handler) => window.removeEventListener("keydown", handler),
  194. handlers
  195. );
  196. };
  197. function hotkeyHandler(fn, hotkey) {
  198. return (e) => {
  199. e[`${e.key}Key`] = true;
  200. const mods = "meta+alt+shift+ctrl";
  201. const conds = `${mods}+${hotkey}`
  202. .replace(/win|command|search/, "meta")
  203. .replace(/control/, "ctrl")
  204. .split("+")
  205. .map((k, i) => [k, Boolean(i >= 4) === Boolean(e[`${k}Key`])]);
  206. const covered = Object.entries(Object.fromEntries(conds));
  207. const matched = covered.every(([keyName, pass]) => pass);
  208. if (!matched) return;
  209. e.stopPropagation();
  210. e.preventDefault();
  211. return fn(e);
  212. };
  213. }
  214. }
  215.  
  216. function mapObjIndexed(fn, obj) {
  217. if (arguments.length === 1) {
  218. return (_obj) => mapObject(fn, _obj);
  219. }
  220. let index = 0;
  221. const objKeys = keys(obj);
  222. const len = objKeys.length;
  223. const willReturn = {};
  224. while (index < len) {
  225. const key = objKeys[index];
  226. willReturn[key] = fn(obj[key], key, obj);
  227. index++;
  228. }
  229. return willReturn;
  230. }