spm_Track_Block_Tool

移除链接中的spm跟踪参数

当前为 2022-06-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name spm_Track_Block_Tool
  3. // @namespace _s7util__
  4. // @version 0.5.10
  5. // @description:en Remove [spm] track paramter in URL
  6. // @description 移除链接中的spm跟踪参数
  7. // @author shc0743
  8. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  9. // @grant none
  10. // @license GPL-3.0
  11. // @supportURL https://github.com/shc0743/MyUtility/issues/new
  12. // @run-at document-start
  13. // @match http*://*.bilibili.com/*
  14. // @match http*://*.baidu.com/*
  15. // @match http*://*.cctv.com/*
  16. // @match http*://*.taobao.com/*
  17. // @match http*://*.alibaba.com/*
  18. // @exclude http*://*.paypal.com/*
  19. // @exclude http*://*.alipay.com/*
  20. // ==/UserScript==
  21.  
  22. /*
  23. Description:
  24. 说明:
  25.  
  26. This user script removes the spm paramter in <a href> elements.
  27. 此脚本移除 <a href> 元素中的spm参数。
  28.  
  29. If it doesn't work, try refreshing it a few times or wait a while.
  30. 若无法生效,请尝试刷新几次或等一会。
  31.  
  32. Examples:
  33. 示例:
  34.  
  35. https://www.bilibili.com/video/av170001?spm_id_from=114514
  36. -> https://www.bilibili.com/video/av170001
  37.  
  38. https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514&query2=data3
  39. -> https://www.bilibili.com/video/av170001?query1=arg2&query2=data3
  40.  
  41. https://www.bilibili.com/video/av170001?spm=114514.1919810#hash
  42. -> https://www.bilibili.com/video/av170001#hash
  43.  
  44. https://www.bilibili.com/video/av170001?spm=114514.1919810&query2=data3#hash1
  45. -> https://www.bilibili.com/video/av170001?query2=data3#hash1
  46. */
  47.  
  48. (function () {
  49. 'use strict';
  50.  
  51. // Your code here...
  52.  
  53. var track_args_list = [
  54. { 'domain': '*', 'keyword': 'spm' },
  55. { 'domain': '*', 'keyword': 'spm_id_from' },
  56. { 'domain': '*', 'keyword': 'from_source' },
  57. { 'domain': 'bilibili.com', 'keyword': 'from' },
  58. { 'domain': 'bilibili.com', 'keyword': 'seid' },
  59. { 'domain': 'bilibili.com', 'keyword': 'vd_source' },
  60. { 'domain': 'baike.baidu.com', 'keyword': 'fr' },
  61. { 'domain': 'alibaba.com', 'keyword': 'tracelog' },
  62. ];
  63. var unwritable_list = [
  64. // https://greasyfork.org/zh-CN/scripts/443049/discussions/132536
  65. { object: window, key: 'goldlog' },
  66. ];
  67. try {
  68. for (let i of unwritable_list) {
  69. Object.defineProperty(i.object, i.key, {
  70. get() { return undefined },
  71. set(value) { void (value) },
  72. enumerable: false,
  73. configurable: true
  74. });
  75. }
  76. }
  77. catch (error) {
  78. console.warn(error);
  79. }
  80.  
  81. return (function (global) {
  82.  
  83. //var expr = /\?[\s\S]*spm/i;
  84.  
  85. /**
  86. * 去除字符串中的spm参数
  87. * @param {String} str URL to remove spm
  88. * @returns 去除spm后的结果
  89. */
  90. var remove_spm = function (str) {
  91. if (typeof (str) != 'string') return str;
  92. var newstr = '';
  93. var len = str.length;
  94. // 只去除查询参数部分,避免正常url被替换而导致404
  95. var hash_part_begin = str.indexOf('#');
  96. var query_part_begin = str.indexOf('?');
  97. if (query_part_begin == -1 ||
  98. (hash_part_begin != -1 && query_part_begin > hash_part_begin))
  99. { return str; } // 没有查询参数或?在#后面,直接返回
  100. newstr = str.substring(0, query_part_begin);
  101. var domain = '';
  102. {
  103. let index = str.indexOf('://');
  104. if (index + 1) {
  105. index = str.indexOf('/', index + 3);
  106. if (index + 1) {
  107. domain = str.substring(0, index);
  108. }
  109. }
  110. }
  111.  
  112. for (let i = query_part_begin, need_break; i < len; ++i) {
  113. for (let j = 0; j < track_args_list.length; ++j) {
  114. if (!(track_args_list[j].domain == '*' ||
  115. domain.indexOf(track_args_list[j].domain) != -1)) {
  116. need_break = false;
  117. break;
  118. }
  119. need_break = true;
  120. if (track_args_list[j].keyword == str.substring(i,
  121. i + track_args_list[j].keyword.length - 0)) {
  122. // 检测到
  123. while ((++i) < len) {
  124. if (str[i] == '&') { // 不能单独保留一个 & 号
  125. i++;
  126. break; // 去掉
  127. }
  128. if (str[i] == '#') break; // 保留hash部分
  129. }
  130. if (i == len) break; // 越界,直接break,以免url出现undefined
  131. }
  132. need_break = false;
  133. }
  134. if (need_break) break;
  135. newstr += str[i];
  136. }
  137.  
  138. var _lastchar;
  139. for (let i = 0; i < newstr.length; ++i) {
  140. _lastchar = newstr[newstr.length - 1];
  141. if (_lastchar == '?' || _lastchar == '&') { // 如果移除后只剩下 ? 或 &
  142. newstr = newstr.substring(0, newstr.length - 1); // 去掉
  143. } else break;
  144. }
  145. // Bug-Fix:
  146. // https://example.com/example?q1=arg&spm=123#hash1
  147. // -> https://example.com/example?q1=arg&#hash1
  148. // Invalid URL syntax at ^^
  149. newstr = newstr.replace(/\&\#/igm, '#');
  150. newstr = newstr.replace(/\?\#/igm, '#');
  151. return newstr;
  152. }
  153. var test_spm = function (str) {
  154. for (let tracker of track_args_list) {
  155. if (new RegExp(tracker, 'i').test(str)) {
  156. return true;
  157. }
  158. }
  159. return false;
  160. };
  161. var _realwindowopen = window.open;
  162. var _realhistorypushState = window.history.pushState;
  163. var _realhistoryreplaceState = window.history.replaceState;
  164.  
  165. /*var _link_click_test = function (val) {
  166. if (/\#/.test(val)) return true;
  167. if (/javascript\:/i.test(val)) return true;
  168. return false;
  169. };
  170. var _link_click = function (event) {
  171. if (_link_click_test(this.href)) return;
  172. event.preventDefault();
  173. // 防止被再次加入spm
  174. this.href = remove_spm(this.href);
  175. _realwindowopen(this.href, this.target || '_self');
  176. return false;
  177. };*/
  178. var _link_mouseover = function () {
  179. if (test_spm(this.href)) this.href = remove_spm(this.href);
  180. };
  181. var link_clean_worker = function (el) {
  182. if (test_spm(el.href)) {
  183. // 链接已经被加入spm , 需要移除
  184. el.href = remove_spm(el.href);
  185. }
  186. }
  187. var linkclickhandlerinit = function () {
  188. var el = document.querySelectorAll('a[href]');
  189. for (let i = el.length - 1; i >= 0; --i) {
  190. link_clean_worker(el[i]);
  191. }
  192. };
  193.  
  194. try {
  195. let wopen = function (url, target, features) {
  196. return _realwindowopen.call(window,
  197. remove_spm(url),
  198. target,
  199. features);
  200. };
  201. let hp = function (data, title, url) {
  202. return _realhistorypushState.call(
  203. window.history, data, title,
  204. remove_spm(url));
  205. };
  206. let hr = function (data, title, url) {
  207. return _realhistoryreplaceState.call(
  208. window.history, data, title,
  209. remove_spm(url));
  210. };
  211. wopen.toString =
  212. hp.toString =
  213. hr.toString =
  214. new Function("return 'function () {\\n [native code]\\n}'");
  215. // 必须定义成 writable 否则一些网站(例如B站收藏夹页面)会出错
  216. Object.defineProperty(window, 'open', {
  217. value: wopen,
  218. writable: true,
  219. enumerable: true,
  220. configurable: true
  221. }); // 重定义window.open 以阻止弹出窗口中的spm
  222. Object.defineProperty(window.history, 'pushState', {
  223. value: hp,
  224. writable: true,
  225. enumerable: true,
  226. configurable: true
  227. }); // 重定义history.pushState
  228. Object.defineProperty(window.history, 'replaceState', {
  229. value: hr,
  230. writable: true,
  231. enumerable: true,
  232. configurable: true
  233. }); // 重定义history.replaceState
  234.  
  235. }
  236. catch (error) {
  237. console.warn("This browser doesn't support redefining" +
  238. " window.open , so [SpmBlockTool] cannot remove" +
  239. " spm in popup window.\nError:", error);
  240. }
  241.  
  242. var DOM_observer;
  243. let DOM_observer_observe = function () {
  244. DOM_observer.observe(document.body, {
  245. attributes: true,
  246. childList: true,
  247. subtree: true
  248. });
  249. };
  250. DOM_observer = new MutationObserver(function (args) {
  251. //debugger
  252. // console.log('DOM changed: ', args);
  253. DOM_observer.disconnect();
  254. for (let i of args) {
  255. if (i.type == 'attributes') {
  256. link_clean_worker(i.target);
  257. }
  258. else if (i.type == 'childList') {
  259. for (let j of i.addedNodes) {
  260. link_clean_worker(j);
  261. }
  262. }
  263. }
  264. DOM_observer.takeRecords();
  265. DOM_observer_observe();
  266. });
  267.  
  268. window.addEventListener('DOMContentLoaded', function () {
  269. // window.setInterval(linkclickhandlerinit, 5000);
  270. new Promise(o => { linkclickhandlerinit(); o() }); // 异步执行
  271.  
  272. DOM_observer_observe();
  273. });
  274.  
  275. // 移除当前页面的spm
  276. // 当然,实际上spm已经在userscript加载前被发送到服务器,
  277. // 所以该功能仅美化url.
  278. // 如果要禁用该功能,删除下面一行开头的斜杠。
  279. //if(0)
  280. // Remove spm from current page
  281. // Of course, in fact, spm has been sent to the server
  282. // before userscript is loaded, so this function only beautifies the URL.
  283. // If you want to disable this feature, remove the slash
  284. // at the beginning of the following line:
  285. //if(0)
  286. if (test_spm(location.href)) {
  287. _realhistoryreplaceState.call(window.history,
  288. {}, document.title,
  289. remove_spm(location.href));
  290. }
  291.  
  292. /*
  293. // 测试代码
  294. var test_urls = [
  295. 'https://www.bilibili.com/video/BV18X4y1N7Yh',
  296. 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm_id_from=114514',
  297. 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm=114514.1919810',
  298. 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm_id_from=114514.123',
  299.  
  300. 'https://www.bilibili.com/video/av170001',
  301. 'https://www.bilibili.com/video/av170001?spm_id_from=114514',
  302. 'https://www.bilibili.com/video/av170001?spm=114514.1919810',
  303. 'https://www.bilibili.com/video/av170001?spm_id_from=114514.123',
  304.  
  305. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514',
  306. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810',
  307. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514.123',
  308. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514',
  309. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810',
  310. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514.123',
  311.  
  312. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514&query2=data3',
  313. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810&query2=data3',
  314.  
  315. 'https://www.bilibili.com/video/av170001?spm_id_from=114514#hash',
  316. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514#hash1',
  317. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810#hash1',
  318.  
  319. 'https://www.bilibili.com/video/av170001?spm_id_from=114514&query2=data3#hash1',
  320. 'https://www.bilibili.com/video/av170001?spm=114514.1919810&query2=data3#hash1',
  321. ];
  322. for(let i=0;i<test_urls.length;++i){
  323. let el=document.createElement('a');
  324. el.href=test_urls[i];
  325. el.innerHTML=i+1 + '';
  326. document.documentElement.appendChild(el);
  327. }
  328. for(let i=0;i<test_urls.length;++i){
  329. let el=document.createElement('a');
  330. el.href=test_urls[i];
  331. el.innerHTML=i+1 + ' blank';
  332. el.target='_blank';
  333. document.documentElement.appendChild(el);
  334. }
  335. */
  336.  
  337. })(window);
  338.  
  339. })();