您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
remove google tracking
当前为
// ==UserScript== // @namespace jp.sceneq.rgtuwaaa // @name remove google tracking UWAA // @description remove google tracking // @homepageURL https://github.com/sceneq/RemoveGoogleTracking // @version 0.20 // @include https://www.google.*/* // @grant none // @run-at document-body // @author sceneq // @license MIT // ==/UserScript== const yes = () => true; const doNothing = () => {}; const $ = (s, n = document) => n.querySelector(s); const $$ = (s, n = document) => [...n.querySelectorAll(s)]; const sleep = millis => new Promise(resolve => setTimeout(resolve, millis)); const zip = rows => rows[0].map((_, c) => rows.map(row => row[c])); const rewriteProperties = props => { for (const p of props) { Object.defineProperty(p[0] || {}, p[1], { value: p[2], writable: false, }); } }; const waitUntilDeclare = ({ obj, property, interval }) => { console.debug('waitUntilDeclare', obj.toString(), property); return new Promise(async (resolve, reject) => { const propertyNames = property.split('.'); let currObj = obj; for (const propertyName of propertyNames) { while (!(propertyName in currObj) || currObj[propertyName] === null) { await sleep(interval); } currObj = currObj[propertyName]; } console.debug('waitUntilDeclare', obj.toString(), property, 'Done'); resolve(currObj); }); }; const untrackBuilder = arg => { const r = arg.badParamsRegex; return a => { const href = a.getAttribute('href'); if(href === null) return; if (href.startsWith('/url?')) { a.href = new URLSearchParams(href.slice(5)).get('q'); } else { a.removeAttribute('ping'); a.href = a.href.replace(r, ''); } }; }; const Types = { search: Symbol('search'), isch: Symbol('image'), shop: Symbol('shop'), nws: Symbol('news'), vid: Symbol('video'), bks: Symbol('book'), maps: Symbol('maps'), fin: Symbol('finance'), toppage: Symbol('toppage'), // flights: Symbol('flights'), }; const tbmToType = tbm => { if (tbm === null) { return Types.search; } const t = { isch: Types.isch, shop: Types.shop, nws: Types.nws, vid: Types.vid, bks: Types.bks, fin: Types.fin, }[tbm]; if (t === undefined) { return null; } else { return t; } }; const BadParamsBase = [ 'biw', // offsetWidth 'bih', // offsetHeight 'ei', 'sa', 'ved', 'source', 'prds', 'bvm', 'bav', 'psi', 'stick', 'dq', 'ech', 'gs_gbg', 'gs_rn', 'cp', 'ictx', 'cshid', 'gs_lcp', ]; const BadParams = (() => { const o = {}; o[Types.search] = [ 'pbx', 'dpr', 'pf', 'gs_rn', 'gs_mss', 'pq', 'cp', 'oq', 'sclient', 'gs_l', 'aqs', ]; o[Types.isch] = [ 'scroll', 'vet', 'yv', 'iact', 'forward', 'ndsp', 'csi', 'tbnid', 'sclient', 'oq', ]; o[Types.shop] = ['oq']; o[Types.nws] = ['oq']; o[Types.vid] = ['oq']; o[Types.bks] = ['oq']; o[Types.fin] = ['oq']; o[Types.toppage] = ['oq', 'sclient', 'uact']; o[Types.maps] = ['psi']; return o; })(); const searchFormUriuri = async arg => { let form = null; if (arg.pageType.mobileOld) { form = $('#sf'); } else if (arg.pageType.ty === Types.isch) { form = await waitUntilDeclare({ obj: window, property: 'sf', interval: 30, }); } else { form = await waitUntilDeclare({ obj: window, property: 'tsf', interval: 30, }); } if (form === null) { console.warn('form === null'); return; } for (const i of form) { if (i.tagName !== 'INPUT') continue; if (arg.badParams.includes(i.name)) { i.parentElement.removeChild(i); } } const orig = form.appendChild.bind(form); form.appendChild = e => { if (!arg.badParams.includes(e.name)) { orig(e); } }; }; const untrackAnchors = (untrack, arg) => { return waitUntilDeclare({ obj: window, property: arg.pageType.mobile ? 'topstuff' : arg.pageType.mobileOld ? 'rmenu' : 'search', interval: 30, }).then(_ => { for (const a of $$('a')) { untrack(a); } }); }; const gcommon = async arg => { const untrack = untrackBuilder(arg); const p1 = waitUntilDeclare({ obj: window, property: 'google', interval: 30, }).then(google => { rewriteProperties([ [google, 'log', yes], [google, 'logUrl', doNothing], [google, 'getLEI', yes], [google, 'ctpacw', yes], [google, 'csiReport', yes], [google, 'report', yes], [google, 'aft', yes], //[google, 'kEI', '0'], //[google, 'getEI', yes], or ()=>"0" ]); }); rewriteProperties([ [window, 'rwt', doNothing], [window.gbar_, 'Rm', doNothing], ]); const p2 = untrackAnchors(untrack, arg); const p3 = searchFormUriuri(arg); await Promise.all([p1, p2, p3]); if (arg.pageType.mobile) { const sel = arg.pageType.ty === Types.search ? '#bres + h1 + div > div + div' : '#bres + div > div + div'; new MutationObserver(mutations => { const nodes = []; for (const m of mutations) { nodes.push(...m.addedNodes); } for (const n of nodes) { new MutationObserver((_, obs) => { console.debug('untrack', n); for (const a of $$('a', n)) { untrack(a); } obs.disconnect(); }).observe(n, { childList: true }); } }).observe($(sel), { childList: true, }); } }; const fun = {}; // TODO mobile, mobileOld fun[Types.toppage] = searchFormUriuri; fun[Types.search] = gcommon; fun[Types.vid] = gcommon; fun[Types.nws] = gcommon; fun[Types.bks] = gcommon; fun[Types.fin] = gcommon; fun[Types.isch] = async arg => { // TODO desktop, mobile, mobileOld const untrack = untrackBuilder(arg); const p1 = waitUntilDeclare({ obj: window, property: 'islmp', interval: 30, }).then(() => { for (const a of $$('a')) { untrack(a); } }); const p2 = searchFormUriuri(arg); await Promise.all([p1, p2]); }; fun[Types.shop] = async arg => { // TODO mobile, mobileOld const untrack = untrackBuilder(arg); const p1 = waitUntilDeclare({ obj: window, property: 'google.pmc.spop.r', interval: 30, }).then(shopObj => { // Rewrite to original link const tmp = $$("div[class$='__content'] a[jsaction='spop.c']"); const [anchors, thumbs] = [0, 1].map(m => tmp.filter((_, i) => i % 2 === m) ); const shops = Object.values(shopObj); const links = shops.map(a => a[34][6]); if (anchors.length === links.length) { for (const [anchor, link, thumb, shop] of zip([ anchors, links, thumbs, shops, ])) { anchors.href = thumb.href = link; shop[3][0][1] = link; shop[14][1] = link; shop[89][16] = link; shop[89][18][0] = link; shop[85][3] = link; } } else { console.warn('length does not match', anchors.length, links.length); } }); const p2 = untrackAnchors(untrack, arg); const p3 = searchFormUriuri(arg); await Promise.all([p1, p2, p3]); }; // TODO fun[Types.maps] = async arg => {} (async () => { 'use strict'; console.debug('rgt: init'); console.time('rgt'); const ty = (() => { if (location.pathname.startsWith('/maps')) { return Types.maps; } if (location.pathname === '/' || location.pathname === '/webhp') { return Types.toppage; } const tbm = new URLSearchParams(location.search).get('tbm'); if (location.pathname === '/search') { return tbmToType(tbm); } return null; })(); if (ty === null) { console.debug('ty === null'); return; } const badParams = (() => { return [...BadParamsBase, ...BadParams[ty]]; })(); const badParamsRegex = new RegExp( '&(?:' + badParams.join('|') + ')=.*?(?=(&|$))', 'g' ); const badImageSrcRegex = /\/(?:(?:gen(?:erate)?|client|fp)_|log)204|(?:metric|csi)\.gstatic\.|(?:adservice)\.(google)/; Object.defineProperty(window.Image.prototype, 'src', { set: function(url) { if (!badImageSrcRegex.test(url)) { this.setAttribute('src', url); } }, }); Object.defineProperty(window.HTMLScriptElement.prototype, 'src', { set: function(url) { this.setAttribute('src', url.replace(badParamsRegex, '')); }, }); const badPaths = [ 'imgevent', 'shopping\\/product\\/.*?\\/popout', 'async/ecr', 'async/bgasy', ]; const regBadPaths = new RegExp('^/(?:' + badPaths.join('|') + ')'); const origOpen = XMLHttpRequest.prototype.open; window.XMLHttpRequest.prototype.open = function(act, path) { if (path === undefined) return; if (path.startsWith('https://aa.google.com/')) return; if (path.startsWith('https://play.google.com/log')) return; if (path.startsWith('https://www.google.com/log')) return; if (regBadPaths.test(path)) return; origOpen.apply(this, [act, path.replace(badParamsRegex, '')]); }; if ('navigator' in window) { const origSendBeacon = navigator.sendBeacon.bind(navigator); navigator.sendBeacon = (path, data) => { if (path.startsWith('https://play.google.com/log')) return; if (badImageSrcRegex.test(path)) return; origSendBeacon(path, data); }; } if (ty in fun) { const mobileOld = $("html[itemtype]") === null; // &liteui=2 const mobile = !mobileOld && $('meta[name=viewport]') !== null; const arg = { pageType: { ty, mobile, mobileOld, }, badParams, badParamsRegex, }; console.debug('arg', arg); await fun[ty](arg); } else { console.warn(`key not found in fun: ${ty.toString()}`); } console.timeEnd('rgt'); })();