remove google tracking UWAA

remove google tracking

  1. // ==UserScript==
  2. // @namespace jp.sceneq.rgtuwaaa
  3. // @name remove google tracking UWAA
  4. // @description remove google tracking
  5. // @homepageURL https://github.com/sceneq/RemoveGoogleTracking
  6. // @version 0.21
  7. // @include https://www.google.*/*
  8. // @grant none
  9. // @run-at document-body
  10. // @author sceneq
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. const yes = () => true;
  15. const doNothing = () => {};
  16. const $ = (s, n = document) => n.querySelector(s);
  17. const $$ = (s, n = document) => [...n.querySelectorAll(s)];
  18. const sleep = (millis) => new Promise((resolve) => setTimeout(resolve, millis));
  19. const zip = (rows) => rows[0].map((_, c) => rows.map((row) => row[c]));
  20.  
  21. const rewriteProperties = (arg) => {
  22. for (const [obj, prop, value] of arg) {
  23. if (!obj) continue;
  24. Object.defineProperty(obj, prop, {
  25. value,
  26. writable: false,
  27. });
  28. }
  29. };
  30.  
  31. const waitUntilDeclare = (obj, property, option = { interval: 100 }) => {
  32. console.debug('waitUntilDeclare Start', obj.toString(), property);
  33. return new Promise(async (resolve, reject) => {
  34. const propertyNames = property.split('.');
  35. let currObj = obj;
  36. for (const propertyName of propertyNames) {
  37. while (!(propertyName in currObj) || currObj[propertyName] === null) {
  38. await sleep(option.interval);
  39. }
  40. currObj = currObj[propertyName];
  41. }
  42. console.debug('waitUntilDeclare Done', obj.toString(), property);
  43. resolve(currObj);
  44. });
  45. };
  46.  
  47. const waitUntilNode = (sel, option = { interval: 100 }) => {
  48. console.debug('waitUntilNode Start', sel);
  49. return new Promise(async (resolve, reject) => {
  50. let d = null;
  51. while (d === null) {
  52. d = $(sel);
  53. await sleep(option.interval);
  54. }
  55. console.debug('waitUntilNode Done', sel);
  56. resolve(d);
  57. });
  58. };
  59.  
  60. const untrackBuilder = (arg) => {
  61. const badParams = arg.badParams;
  62. return (a) => {
  63. const href = a?.href;
  64. if (!href) return;
  65. const url = new URL(href);
  66. if (a.getAttribute('href') === '/url') {
  67. a.href = url.searchParams.get('url'); // todo q?
  68. } else {
  69. a.removeAttribute('ping');
  70. a.href = delParams(href, badParams);
  71. }
  72. };
  73. };
  74.  
  75. const delParams = (sUrl, params) => {
  76. if (sUrl.startsWith('/')) {
  77. sUrl = location.origin + sUrl;
  78. }
  79. const url = new URL(sUrl);
  80. const keys = [...url.searchParams.keys()];
  81. for (const k of keys) {
  82. if (!params.includes(k)) continue;
  83. url.searchParams.delete(k);
  84. }
  85. return url.toString();
  86. };
  87.  
  88. const Types = {
  89. search: Symbol('search'),
  90. isch: Symbol('image'),
  91. shop: Symbol('shop'),
  92. nws: Symbol('news'),
  93. vid: Symbol('video'),
  94. bks: Symbol('book'),
  95. maps: Symbol('maps'),
  96. fin: Symbol('finance'),
  97. toppage: Symbol('toppage'),
  98. // flights: Symbol('flights'),
  99. };
  100.  
  101. const tbmToType = (tbm) => {
  102. if (tbm === null) {
  103. return Types.search;
  104. }
  105. const t = {
  106. isch: Types.isch,
  107. shop: Types.shop,
  108. nws: Types.nws,
  109. vid: Types.vid,
  110. bks: Types.bks,
  111. fin: Types.fin,
  112. }[tbm];
  113. if (t === undefined) {
  114. return null;
  115. } else {
  116. return t;
  117. }
  118. };
  119.  
  120. const BadParamsBase = [
  121. 'biw', // offsetWidth
  122. 'bih', // offsetHeight
  123. 'ei',
  124. 'sa',
  125. 'ved',
  126. 'source',
  127. 'prds',
  128. 'bvm',
  129. 'bav',
  130. 'psi',
  131. 'stick',
  132. 'dq',
  133. 'ech',
  134. 'gs_gbg',
  135. 'gs_rn',
  136. 'cp',
  137. 'ictx',
  138. 'cshid',
  139. 'gs_lcp',
  140. ];
  141.  
  142. const BadParams = (() => {
  143. const o = {};
  144. o[Types.search] = [
  145. 'vet',
  146. 'pbx',
  147. 'dpr',
  148. 'pf',
  149. 'gs_rn',
  150. 'gs_mss',
  151. 'pq',
  152. 'cp',
  153. 'oq',
  154. 'sclient',
  155. 'gs_l',
  156. 'aqs',
  157. 'sxsrf',
  158. ];
  159. o[Types.isch] = [
  160. 'vet',
  161. 'scroll',
  162. 'yv',
  163. 'iact',
  164. 'forward',
  165. 'ndsp',
  166. 'csi',
  167. 'tbnid',
  168. 'sclient',
  169. 'oq',
  170. ];
  171. o[Types.shop] = ['oq'];
  172. o[Types.nws] = ['oq'];
  173. o[Types.vid] = ['oq'];
  174. o[Types.bks] = ['oq'];
  175. o[Types.fin] = ['oq'];
  176. o[Types.toppage] = ['oq', 'sclient', 'uact'];
  177. o[Types.maps] = ['psi'];
  178. return o;
  179. })();
  180.  
  181. const overwrite = (arg) => {
  182. const badImageSrcRegex = /\/(?:(?:gen(?:erate)?|client|fp)_|log)204|(?:metric|csi)\.gstatic\.|(?:adservice)\.(google)/;
  183. if(arg.pageType.ty !== Types.maps){
  184. Object.defineProperty(window.Image.prototype, 'src', {
  185. set: function (url) {
  186. //console.debug('img send', url);
  187. if (badImageSrcRegex.test(url)) return;
  188. this.setAttribute('src', url);
  189. },
  190. });
  191. }
  192.  
  193. Object.defineProperty(window.HTMLScriptElement.prototype, 'src', {
  194. set: function (url) {
  195. //console.debug('script send', url);
  196. if(typeof(url) === "string"){
  197. this.setAttribute('src', delParams(url, arg.badParams));
  198. } else {
  199. this.setAttribute('src', url);
  200. }
  201. },
  202. });
  203.  
  204. const blockOnOpenPaths = [
  205. '/imgevent',
  206. '/async/ecr',
  207. '/async/bgasy',
  208. '/shopping/product/.+?/popout',
  209. '/_/VisualFrontendUi/browserinfo',
  210. '/_/VisualFrontendUi/jserror',
  211. ];
  212.  
  213. // todo fakeXHR?
  214. const blockOnSendPaths = ['/log'];
  215.  
  216. const regBlockOnOpenPaths = new RegExp(
  217. '^(?:' + blockOnOpenPaths.join('|') + ')'
  218. );
  219. const regBlockOnSendPaths = new RegExp(
  220. '^(?:' + blockOnSendPaths.join('|') + ')'
  221. );
  222.  
  223. const origOpen = window.XMLHttpRequest.prototype.open;
  224. window.XMLHttpRequest.prototype.open = function (act, path) {
  225. try {
  226. this.__path = path;
  227. this.__url = null;
  228. if (path === undefined) return;
  229. if (path.startsWith('https://')) {
  230. const url = new URL(path);
  231. this.__url = url;
  232. if (this.__url.origin === 'aa.google.com') return;
  233. if (regBlockOnOpenPaths.test(this.__url.pathname)) return;
  234. } else if (regBlockOnOpenPaths.test(this.__path)) {
  235. return;
  236. }
  237. const new_path = delParams(path, arg.badParams);
  238. } catch (e) {
  239. console.error(e);
  240. return;
  241. }
  242. //console.debug('xhr open', this.__path);
  243. return origOpen.apply(this, [act, this.__path]);
  244. };
  245.  
  246. const origSend = window.XMLHttpRequest.prototype.send;
  247. window.XMLHttpRequest.prototype.send = function (body) {
  248. try {
  249. if (this.__url !== null) {
  250. if (regBlockOnSendPaths.test(this.__url.pathname)) return;
  251. } else if (regBlockOnOpenPaths.test(this.__path)) {
  252. return;
  253. }
  254. } catch (e) {
  255. console.error(e);
  256. return;
  257. }
  258. //console.debug('xhr send', this.__path);
  259. return origSend.apply(this, [body]);
  260. };
  261.  
  262. if ('navigator' in window) {
  263. const origSendBeacon = navigator.sendBeacon.bind(navigator);
  264. navigator.sendBeacon = (path, data) => {
  265. if (path === undefined) return;
  266. if (path.startsWith('https://play.google.com/log')) return;
  267. if (badImageSrcRegex.test(path)) return;
  268. //console.debug('nav send', path);
  269. origSendBeacon(path, data);
  270. };
  271. }
  272. };
  273.  
  274. const searchFormUriuri = async (arg) => {
  275. // TODO mobile, mobileOld
  276. let form = null;
  277. if (arg.pageType.mobileOld) {
  278. form = $('#sf');
  279. } else if (arg.pageType.ty === Types.isch) {
  280. form = await waitUntilDeclare(window, 'sf');
  281. } else {
  282. form = await waitUntilDeclare(window, 'tsf');
  283. }
  284.  
  285. if (form === null) {
  286. console.warn('form === null');
  287. return;
  288. }
  289.  
  290. for (const i of form) {
  291. if (i.tagName !== 'INPUT') continue;
  292. if (arg.badParams.includes(i.name)) {
  293. i.parentElement.removeChild(i);
  294. }
  295. }
  296. const orig = form.appendChild.bind(form);
  297. form.appendChild = (e) => {
  298. if (!arg.badParams.includes(e.name)) {
  299. orig(e);
  300. }
  301. };
  302. };
  303.  
  304. const untrackAnchors = (untrack, arg) => {
  305. let property = null;
  306. if (arg.pageType.mobile) {
  307. property = 'topstuff';
  308. } else if (arg.pageType.mobileOld) {
  309. property = 'main'; // 'rmenu';
  310. } else if (arg.pageType.ty === Types.search) {
  311. property = 'hdtb-msb';
  312. } else if (arg.pageType.ty === Types.bks) {
  313. property = 'lr_';
  314. } else if (arg.pageType.ty === Types.vid) {
  315. property = 'hdtb-msb';
  316. } else if (arg.pageType.ty === Types.nws) {
  317. property = 'hdtb-msb';
  318. } else if (arg.pageType.ty === Types.isch) {
  319. property = 'i4';
  320. } else {
  321. property = 'search';
  322. }
  323.  
  324. return waitUntilDeclare(window, property).then((_) => {
  325. for (const a of $$('a')) {
  326. untrack(a);
  327. }
  328. });
  329. };
  330.  
  331. const gcommon = async (arg) => {
  332. const untrack = untrackBuilder(arg);
  333. const p1 = waitUntilDeclare(window, 'google').then((google) => {
  334. rewriteProperties([
  335. [google, 'log', yes],
  336. [google, 'logUrl', doNothing],
  337. [google, 'getLEI', yes],
  338. [google, 'ctpacw', yes],
  339. [google, 'csiReport', yes],
  340. [google, 'report', yes],
  341. [google, 'aft', yes],
  342. //[google, 'kEI', '0'],
  343. //[google, 'getEI', yes], or ()=>"0"
  344. ]);
  345. });
  346. rewriteProperties([
  347. [window, 'rwt', doNothing],
  348. [window.gbar_, 'Rm', doNothing],
  349. ]);
  350.  
  351. const p2 = untrackAnchors(untrack, arg);
  352. const p3 = searchFormUriuri(arg);
  353. await Promise.all([p1, p2, p3]);
  354.  
  355. if (arg.pageType.mobile) {
  356. const sel =
  357. arg.pageType.ty === Types.search
  358. ? '#bres + h1 + div > div + div'
  359. : '#bres + div > div + div';
  360. new MutationObserver((mutations) => {
  361. const nodes = mutations.flatMap((d) => [...d.addedNodes]);
  362. for (const n of nodes) {
  363. new MutationObserver((_, obs) => {
  364. console.debug('untrack', n);
  365. for (const a of $$('a', n)) {
  366. untrack(a);
  367. }
  368. obs.disconnect();
  369. }).observe(n, { childList: true });
  370. }
  371. }).observe($(sel), { childList: true });
  372. }
  373. };
  374.  
  375. const fun = {};
  376.  
  377. fun[Types.toppage] = searchFormUriuri;
  378.  
  379. fun[Types.search] = gcommon;
  380. fun[Types.vid] = gcommon;
  381. fun[Types.nws] = gcommon;
  382. fun[Types.bks] = gcommon;
  383. fun[Types.fin] = gcommon;
  384.  
  385. fun[Types.isch] = async (arg) => {
  386. // TODO mobile, mobileOld
  387. const untrack = untrackBuilder(arg);
  388. const p1 = untrackAnchors(untrack, arg);
  389. const p2 = searchFormUriuri(arg);
  390. const p3 = waitUntilDeclare(window, 'islrg').then((islrg) => {
  391. const imagesWrapper = islrg.children[0];
  392. const uuuu = (div) => {
  393. if (div.children.length !== 2) return;
  394. const a = div.children[1];
  395. untrack(a);
  396. a.removeAttribute('jsaction');
  397. };
  398. for (const div of $$(':scope > div', imagesWrapper)) {
  399. uuuu(div);
  400. }
  401. new MutationObserver((mutations) => {
  402. for (const div of mutations.flatMap((m) => [...m.addedNodes])) {
  403. uuuu(div);
  404. }
  405. }).observe(imagesWrapper, { childList: true });
  406. });
  407.  
  408. /*
  409. const p4 = waitUntilNode('.ZsbmCf').then(_ => {
  410. for (const a of $$('.ZsbmCf, .Beeb4e')) {
  411. const ee = e => untrack(a);
  412. a.addEventListener('click', ee);
  413. a.addEventListener('contextmenu', ee);
  414. }
  415. });
  416. */
  417.  
  418. /*
  419. const safeurl = a => {
  420. const s = a.j ?? a;
  421. if (s.startsWith('/imgres?')) {
  422. return delParams(location.origin + s, [
  423. 'vet',
  424. 'w',
  425. 'h',
  426. 'ved',
  427. // q imgurl imgrefurl tbnid docid
  428. ]);
  429. }
  430. if (s.startsWith('/')) return s;
  431. return new URLSearchParams(s).get('url') ?? s;
  432. };
  433. const p4 = waitUntilDeclare(window, 'default_VisualFrontendUi').then(_ => {
  434. rewriteProperties([[_, 'zd', safeurl]]);
  435. });
  436. */
  437. await Promise.all([p1, p2, p3]);
  438. };
  439.  
  440. fun[Types.shop] = async (arg) => {
  441. // TODO desktop, mobile, mobileOld
  442. const untrack = untrackBuilder(arg);
  443. const p1 = untrackAnchors(untrack, arg);
  444. const p2 = searchFormUriuri(arg);
  445. const p3 = waitUntilDeclare(window, 'google.pmc.spop.r', {
  446. interval: 30,
  447. }).then((shopObj) => {
  448. for (const result of $$('.sh-dlr__list-result')) {
  449. const shop = shopObj[result.dataset.docid];
  450. const link = shop[34][6];
  451. result.querySelector("a[class$='__merchant-name']").href = link;
  452. result.querySelector('a.translate-content').href = link;
  453. result.querySelector('div.sh-dlr__thumbnail > a').href = link;
  454. shop[3][0][1] = link;
  455. shop[14][1] = link;
  456. shop[89][16] = link;
  457. shop[89][18][0] = link;
  458. if (shop[85] !== null) {
  459. shop[85][3] = link;
  460. }
  461. }
  462. });
  463. await Promise.all([p1, p2, p3]);
  464. };
  465.  
  466. fun[Types.maps] = async (arg) => {
  467. // TODO desktop, mobile, mobileOld
  468. const untrack = (a) => {
  469. a.addEventListener('click', (e) => e.stopPropagation());
  470. for (const n of [...a.attributes]
  471. .map((at) => at.name)
  472. .filter((n) => ['href', 'class'].indexOf(n) === -1)) {
  473. a.removeAttribute(n);
  474. }
  475. };
  476. const main = await waitUntilNode('div[role=main]', { interval: 30 });
  477. new MutationObserver((mutations) => {
  478. console.log(mutations);
  479. }).observe(main.children[1].children[0], { childList: true });
  480. };
  481.  
  482. (async () => {
  483. 'use strict';
  484. console.debug('rgt: init');
  485. console.time('rgt');
  486.  
  487. const ty = (() => {
  488. if (location.pathname.startsWith('/maps')) {
  489. return Types.maps;
  490. }
  491. if (location.pathname === '/' || location.pathname === '/webhp') {
  492. return Types.toppage;
  493. }
  494. const tbm = new URLSearchParams(location.search).get('tbm');
  495. if (location.pathname === '/search') {
  496. return tbmToType(tbm);
  497. }
  498. return null;
  499. })();
  500.  
  501. if (ty === null) {
  502. console.debug('ty === null', location.href);
  503. return;
  504. }
  505.  
  506. const badParams = (() => {
  507. return [...BadParamsBase, ...BadParams[ty]];
  508. })();
  509.  
  510. if (ty in fun) {
  511. const mobileOld = $('html[itemtype]') === null; // &liteui=2
  512. const mobile = !mobileOld && $('meta[name=viewport]') !== null;
  513. const arg = {
  514. pageType: {
  515. ty,
  516. mobile,
  517. mobileOld,
  518. },
  519. badParams,
  520. };
  521. console.debug('arg', arg);
  522. overwrite(arg);
  523. await fun[ty](arg);
  524. } else {
  525. console.warn(`key not found in fun: ${ty.toString()}`);
  526. }
  527. console.timeEnd('rgt');
  528. })();

QingJ © 2025

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