// ==UserScript== // @name 微信文章自动加载图片 (防盗链穿透版) // @namespace http://tampermonkey.net/ // @version 1.2 // @description 解决微信防盗链和URL参数问题 // @author You // @match *://mp.weixin.qq.com/* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; // 1. 强制设置 Referrer Policy,骗过微信防盗链 // 这能让图片在被外部工具抓取时也认为是“自己人” function setReferrerPolicy() { if (!document.head || document.querySelector('meta[name="referrer"]')) { return; } const meta = document.createElement('meta'); meta.name = 'referrer'; meta.content = 'no-referrer-when-downgrade'; // 或者 'unsafe-url' document.head.appendChild(meta); } function cleanWeChatImageUrl(src) { let cleanSrc = src.split('#')[0]; try { const url = new URL(cleanSrc); ['tp', 'wxfrom', 'from'].forEach(param => url.searchParams.delete(param)); cleanSrc = url.toString(); } catch (error) { // Keep the original fallback behavior for non-standard URLs. if (cleanSrc.includes('&tp=')) { cleanSrc = cleanSrc.split('&tp=')[0]; } if (cleanSrc.includes('&wxfrom=')) { cleanSrc = cleanSrc.split('&wxfrom=')[0]; } if (cleanSrc.includes('&from=')) { cleanSrc = cleanSrc.split('&from=')[0]; } } return cleanSrc; } function isWeChatPlaceholderImage(img) { return img.src.includes('mmbiz.qpic.cn') && ( img.classList.contains('js_img_placeholder') || img.classList.contains('wx_img_placeholder') || img.hasAttribute('data-imgfileid') || img.hasAttribute('data-aistatus') ); } function makeImageVisible(img) { img.style.setProperty('visibility', 'visible', 'important'); img.style.setProperty('height', 'auto', 'important'); if (!img.style.width && img.dataset.w) { img.style.setProperty('width', `${img.dataset.w}px`, 'important'); } } function makeImageEager(img, src) { img.loading = 'eager'; img.decoding = 'sync'; img.setAttribute('data-src', src); img.setAttribute('data-original', src); img.setAttribute('data-original-src', src); img.removeAttribute('data-index'); img.removeAttribute('data-original-style'); img.removeAttribute('_width'); } function wrapImageForClipper(img) { if (img.closest('figure[data-wechat-img-loader]')) { return; } const parent = img.parentElement; if (!parent) { return; } const figure = document.createElement('figure'); figure.setAttribute('data-wechat-img-loader', ''); figure.style.setProperty('margin', '15px 0'); figure.style.setProperty('visibility', 'visible'); const link = document.createElement('a'); link.href = img.src; link.target = '_blank'; link.rel = 'noreferrer'; parent.insertBefore(figure, img); figure.appendChild(link); link.appendChild(img); } function normalizeImageOnlyParagraph(img) { if (img.closest('section[nodeleaf]')) { return; } const paragraph = img.closest('p'); if (!paragraph) { return; } const images = paragraph.querySelectorAll('img'); if (images.length !== 1 || images[0] !== img || paragraph.textContent.trim()) { return; } const section = document.createElement('section'); section.setAttribute('nodeleaf', ''); section.style.setProperty('visibility', 'visible'); section.appendChild(img); paragraph.replaceWith(section); } function replaceSrc() { document.querySelectorAll('img[data-src]').forEach(img => { // 2. 提取真实链接 let trueSrc = img.dataset.src; // 3. 【强力清洗】去除所有可能干扰的参数和锚点 trueSrc = cleanWeChatImageUrl(trueSrc); // 4. 只有链接发生变化时才替换 if (img.src !== trueSrc) { img.src = trueSrc; // 修改 referrerPolicy 属性,告诉浏览器不要隐藏来源 img.referrerPolicy = 'no-referrer-when-downgrade'; // 移除可能导致冲突的 data-src img.removeAttribute('data-src'); } makeImageVisible(img); makeImageEager(img, trueSrc); normalizeImageOnlyParagraph(img); wrapImageForClipper(img); }); document.querySelectorAll('img[src]').forEach(img => { if (!isWeChatPlaceholderImage(img)) { return; } const trueSrc = cleanWeChatImageUrl(img.src); if (img.src !== trueSrc) { img.src = trueSrc; } img.referrerPolicy = 'no-referrer-when-downgrade'; img.classList.remove('js_img_placeholder', 'wx_img_placeholder'); makeImageVisible(img); makeImageEager(img, trueSrc); normalizeImageOnlyParagraph(img); wrapImageForClipper(img); }); } function run() { setReferrerPolicy(); if (!document.body) { return; } replaceSrc(); // 监听动态加载 const observer = new MutationObserver(replaceSrc); observer.observe(document.body, { childList: true, subtree: true }); } // 立即运行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', run); } else { run(); } })();