- // ==UserScript==
- // @name spm_Track_Block_Tool
- // @namespace _s7util__
- // @version 0.7.5
- // @description:en Remove [spm] track paramter in URL
- // @description 移除链接中的spm跟踪参数
- // @author shc0743
- // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
- // @grant none
- // @license GPL-3.0
- // @supportURL https://github.com/shc0743/MyUtility/issues/new
- // @run-at document-start
- // @match http*://*.bilibili.com/*
- // @match http*://*.baidu.com/*
- // @match http*://*.cctv.com/*
- // @match http*://*.taobao.com/*
- // @match http*://*.alibaba.com/*
- // @exclude http*://*.paypal.com/*
- // @exclude http*://*.alipay.com/*
- // ==/UserScript==
-
- /*
- Description:
- 说明:
-
- This user script removes the spm paramter in <a href> elements.
- 此脚本移除 <a href> 元素中的spm参数。
-
- If it doesn't work, try refreshing it a few times or wait a while.
- 若无法生效,请尝试刷新几次或等一会。
-
- Examples:
- 示例:
-
- https://www.bilibili.com/video/av170001?spm_id_from=114514
- -> https://www.bilibili.com/video/av170001
-
- https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514&query2=data3
- -> https://www.bilibili.com/video/av170001?query1=arg2&query2=data3
-
- https://www.bilibili.com/video/av170001?spm=114514.1919810#hash
- -> https://www.bilibili.com/video/av170001#hash
-
- https://www.bilibili.com/video/av170001?spm=114514.1919810&query2=data3#hash1
- -> https://www.bilibili.com/video/av170001?query2=data3#hash1
- */
-
- (function () {
- 'use strict';
-
- // Your code here...
-
- var track_args_list = [
- // 以下是本脚本检测的跟踪参数,检测到后自动去除
- // 格式: { 'domain': '应用到哪个网站,只要填顶级域名即可,例如www.baidu.com只要写baidu.com', 'keyword': '跟踪参数的名称' }
- { 'domain': '*', 'keyword': 'spm' },
- { 'domain': '*', 'keyword': 'spm_id_from' },
- { 'domain': '*', 'keyword': 'from_source' },
- { 'domain': 'bilibili.com', 'keyword': 'from' },
- { 'domain': 'bilibili.com', 'keyword': 'seid' },
- { 'domain': 'bilibili.com', 'keyword': 'vd_source' },
- { 'domain': 'bilibili.com', 'keyword': 'search_source' },
- { 'domain': 'baike.baidu.com', 'keyword': 'fr' },
- { 'domain': 'alibaba.com', 'keyword': 'tracelog' },
- ];
- var unwritable_list = [
- //// https://gf.qytechs.cn/zh-CN/scripts/443049/discussions/132536
- //{ object: window, key: 'goldlog' },
- ];
- try {
- for (let i of unwritable_list) {
- Object.defineProperty(i.object, i.key, {
- get() { return undefined },
- set(value) { return !void (value) },
- enumerable: false,
- configurable: true
- });
- }
- }
- catch (error) {
- console.warn(error);
- }
-
- return (function (global) {
-
- //var expr = /\?[\s\S]*spm/i;
-
- /**
- * 检测域名是否匹配,支持域名层级
- * @param {String} pattern 主域名
- * @param {String} str 要检测的域名,可以和主域名一样,也可以是主域名的子域名
- * @returns 匹配则返回true,否则false
- * @example DomainCheck('baidu.com', 'yiyan.baidu.com') === true
- * 注意:请自行进行类型检查,如果参数不是String则行为未知
- */
- const DomainCheck = function (pattern, str) {
- if (str === pattern) return true;
- pattern = '.' + pattern;
- if (str.endsWith(pattern)) return true;
- return false;
- };
-
- /**
- * 去除字符串中的spm参数
- * @param {String} str URL to remove spm
- * @returns 去除spm后的结果
- */
- const remove_spm = function (str) {
- // 新版方案,使用 URL 相关api操作
- try {
- const url = new URL(str, globalThis.location.href); // 构造URL对象
- // 改用此法后,可轻松处理 //search.bilibili.com/ 之类的不以http(s)开头的链接
- // 对于url构造失败的(无效url之类的),回退到旧版基于字符串的方案
-
- const hostname = url.hostname;
- for (const i of track_args_list) {
- if (!(i.domain === '*' || DomainCheck(i.domain, hostname))) continue;
- if (url.searchParams.has(i.keyword)) { // 存在track参数!!!
- url.searchParams.delete(i.keyword); // 去掉
- }
- }
- return url.href; // 优雅
-
- } catch (error) {
- // 回退到旧版方案
- return OLD__remove_spm(str);
- }
- };
- // 以下是旧版方案
- // 古语云:代码能跑就不要删
- var OLD__remove_spm = function (str) {
- if (typeof (str) != 'string') return str;
- var newstr = '';
- var len = str.length;
- // 只去除查询参数部分,避免正常url被替换而导致404
- var hash_part_begin = str.indexOf('#');
- var query_part_begin = str.indexOf('?');
- if (query_part_begin == -1 ||
- (hash_part_begin != -1 && query_part_begin > hash_part_begin))
- { return str; } // 没有查询参数或?在#后面,直接返回
- newstr = str.substring(0, query_part_begin);
- var domain = '';
- {
- let index = str.indexOf('://');
- if (index + 1) {
- index = str.indexOf('/', index + 3);
- if (index + 1) {
- domain = str.substring(0, index);
- }
- }
- }
-
- for (let i = query_part_begin, need_break; i < len; ++i) {
- for (let j = 0; j < track_args_list.length; ++j) {
- if (!(track_args_list[j].domain == '*' ||
- domain.indexOf(track_args_list[j].domain) != -1)) {
- need_break = false;
- break;
- }
- need_break = true;
- if (track_args_list[j].keyword == str.substring(i,
- i + track_args_list[j].keyword.length - 0)) {
- // 检测到
- while ((++i) < len) {
- if (str[i] == '&') { // 不能单独保留一个 & 号
- i++;
- break; // 去掉
- }
- if (str[i] == '#') break; // 保留hash部分
- }
- if (i == len) break; // 越界,直接break,以免url出现undefined
- }
- need_break = false;
- }
- if (need_break) break;
- newstr += str[i];
- }
-
- var _lastchar;
- for (let i = 0; i < newstr.length; ++i) {
- _lastchar = newstr[newstr.length - 1];
- if (_lastchar == '?' || _lastchar == '&') { // 如果移除后只剩下 ? 或 &
- newstr = newstr.substring(0, newstr.length - 1); // 去掉
- } else break;
- }
- // Bug-Fix:
- // https://example.com/example?q1=arg&spm=123#hash1
- // -> https://example.com/example?q1=arg&#hash1
- // Invalid URL syntax at ^^
- newstr = newstr.replace(/\&\#/igm, '#');
- newstr = newstr.replace(/\?\#/igm, '#');
- return newstr;
- }
- var test_spm = function (str) {
- const currentDomain = window.location.hostname;
- for (let tracker of track_args_list) {
- if (currentDomain.endsWith(tracker.domain) && new RegExp(tracker.keyword, 'i').test(str)) {
- return true;
- }
- }
- return false;
- };
- var _realwindowopen = window.open;
- var _realhistorypushState = window.history.pushState;
- var _realhistoryreplaceState = window.history.replaceState;
-
- /*var _link_click_test = function (val) {
- if (/\#/.test(val)) return true;
- if (/javascript\:/i.test(val)) return true;
- return false;
- };
- var _link_click = function (event) {
- if (_link_click_test(this.href)) return;
- event.preventDefault();
- // 防止被再次加入spm
- this.href = remove_spm(this.href);
- _realwindowopen(this.href, this.target || '_self');
- return false;
- };*/
- var _link_mouseover = function () {
- if (test_spm(this.href)) this.href = remove_spm(this.href);
- };
- var link_clean_worker = function (el) {
- if (test_spm(el.href)) {
- // 链接已经被加入spm , 需要移除
- el.href = remove_spm(el.href);
- }
- }
- var linkclickhandlerinit = function () {
- var el = document.querySelectorAll('a[href]');
- for (let i = el.length - 1; i >= 0; --i) {
- link_clean_worker(el[i]);
- }
- };
-
- try {
- let wopen = function (url, target, features) {
- return _realwindowopen.call(window,
- remove_spm(url),
- target,
- features);
- };
- let hp = function (data, title, url) {
- return _realhistorypushState.call(
- window.history, data, title,
- (typeof url === 'string') ? remove_spm(url) : url);
- };
- let hr = function (data, title, url) {
- return _realhistoryreplaceState.call(
- window.history, data, title,
- (typeof url === 'string') ? remove_spm(url) : url);
- };
- wopen.toString =
- hp.toString =
- hr.toString =
- ({ toString() { return 'function () { [native code] }' } }.toString);
- // 必须定义成 writable 否则一些网站(例如B站收藏夹页面)会出错
- Object.defineProperty(window, 'open', {
- value: wopen,
- writable: true,
- enumerable: true,
- configurable: true
- }); // 重定义window.open 以阻止弹出窗口中的spm
- Object.defineProperty(window.history, 'pushState', {
- value: hp,
- writable: true,
- enumerable: true,
- configurable: true
- }); // 重定义history.pushState
- Object.defineProperty(window.history, 'replaceState', {
- value: hr,
- writable: true,
- enumerable: true,
- configurable: true
- }); // 重定义history.replaceState
-
- }
- catch (error) {
- console.warn("This browser doesn't support redefining" +
- " window.open , so [SpmBlockTool] cannot remove" +
- " spm in popup window.\nError:", error);
- }
-
- var DOM_observer;
- let DOM_observer_observe = function () {
- DOM_observer.observe(document.body, {
- attributes: true,
- childList: true,
- subtree: true
- });
- };
- DOM_observer = new MutationObserver(function (args) {
- //debugger
- // console.log('DOM changed: ', args);
- DOM_observer.disconnect();
- for (let i of args) {
- if (i.type == 'attributes') {
- link_clean_worker(i.target);
- }
- else if (i.type == 'childList') {
- for (let j of i.addedNodes) {
- link_clean_worker(j);
- }
- }
- }
- DOM_observer.takeRecords();
- DOM_observer_observe();
- });
-
- window.addEventListener('DOMContentLoaded', function () {
- // window.setInterval(linkclickhandlerinit, 5000);
- new Promise(o => { linkclickhandlerinit(); o() }); // 异步执行
-
- DOM_observer_observe();
- });
-
- // 移除当前页面的spm
- // 当然,实际上spm已经在userscript加载前被发送到服务器,
- // 所以该功能仅美化url.
- // 如果要禁用该功能,删除下面一行开头的斜杠。
- //if(0)
- // Remove spm from current page
- // Of course, in fact, spm has been sent to the server
- // before userscript is loaded, so this function only beautifies the URL.
- // If you want to disable this feature, remove the slash
- // at the beginning of the following line:
- //if(0)
- if (test_spm(location.href)) {
- _realhistoryreplaceState.call(window.history,
- {}, document.title,
- remove_spm(location.href));
- }
-
- // https://gf.qytechs.cn/zh-CN/scripts/443049/discussions/156657
- // https://gf.qytechs.cn/zh-CN/scripts/443049/discussions/132536
- setInterval(function () {
- // 确认过了,只是检查页面有没有跟踪参数,不进行大范围DOM访问,性能开销可以忽略
- if (test_spm(location.href)) {
- // 2023.12.31改:鬼知道test_spm里面是什么屎山代码,元旦有时间了重构一下才发现一堆问题,
- // 又不敢删,斟酌后决定把此处延时由800改为5000
- _realhistoryreplaceState.call(window.history,
- {}, document.title,
- remove_spm(location.href));
- }
- }, 5000);
-
- // 修复cctv等网站去除goldlog后部分功能异常的bug
- const fakeGoldlog = {
- afterRecord() { },
- afterSendPV() { },
- aplusBridgeName() { },
- aplus_pubsub: { handlers: [], pubs: {} },
- appendMetaInfo() { },
- beforeRecord() { },
- beforeSendPV() { },
- cdnPath: "//g.alicdn.com",
- cloneDeep() { },
- create() { },
- extend() { },
- getCdnPath() { },
- getCookie() { },
- getMetaInfo() { },
- getParam() { },
- getPvId() { },
- isInternational() { },
- launch() { },
- lver: "8.10.5",
- nameStorage: window.localStorage,
- on() { },
- pv_context: {},
- pvid: "ffffffffffffff",
- record() { },
- recordUdata() { },
- req: window.location.href,
- send() { },
- sendPV() { },
- setMetaInfo() { },
- setPageSPM() { },
- spm_ab: ['X12345', 'xxxxxxxxxxxx']
- };
- const proxiedFakeGoldlog = new Proxy(fakeGoldlog, {
- set(target, p, newValue, receiver) {
- console.warn(new TypeError('Fuck you, alicdn goldlog'));
- Reflect.set(target, p, newValue, receiver);
- return true;
- }
- });
- Object.defineProperty(globalThis, 'goldlog', {
- get() { return proxiedFakeGoldlog },
- set(value) { return true },
- enumerable: true,
- configurable: false
- })
-
- /*
- // 测试代码
- var test_urls = [
- 'https://www.bilibili.com/video/BV18X4y1N7Yh',
- 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm_id_from=114514',
- 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm=114514.1919810',
- 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm_id_from=114514.123',
-
- 'https://www.bilibili.com/video/av170001',
- 'https://www.bilibili.com/video/av170001?spm_id_from=114514',
- 'https://www.bilibili.com/video/av170001?spm=114514.1919810',
- 'https://www.bilibili.com/video/av170001?spm_id_from=114514.123',
-
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514.123',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514.123',
-
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514&query2=data3',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810&query2=data3',
-
- 'https://www.bilibili.com/video/av170001?spm_id_from=114514#hash',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514#hash1',
- 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810#hash1',
-
- 'https://www.bilibili.com/video/av170001?spm_id_from=114514&query2=data3#hash1',
- 'https://www.bilibili.com/video/av170001?spm=114514.1919810&query2=data3#hash1',
- ];
- for(let i=0;i<test_urls.length;++i){
- let el=document.createElement('a');
- el.href=test_urls[i];
- el.innerHTML=i+1 + '';
- document.documentElement.appendChild(el);
- }
- for(let i=0;i<test_urls.length;++i){
- let el=document.createElement('a');
- el.href=test_urls[i];
- el.innerHTML=i+1 + ' blank';
- el.target='_blank';
- document.documentElement.appendChild(el);
- }
- */
-
- })(window);
-
- })();