您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add research access buttons to Scholar results (DOI-focused Sci-Hub, Sci-DB, and title-based searches)
// ==UserScript== // @name Scholar Access Enhancer // @namespace http://tampermonkey.net/ // @version 1.0 // @description Add research access buttons to Scholar results (DOI-focused Sci-Hub, Sci-DB, and title-based searches) // @author Adelyn Maisie // @match https://scholar.google.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=google.com // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @license AGPLv3 // ==/UserScript== (function() { 'use strict'; // Configuration const services = { SciDB: { name: 'Sci-DB', color: '#a83293', getUrl: (doi) => doi ? `https://annas-archive.org/scidb/${doi}` : null }, AnnasArchive: { name: "Anna's Archive", color: '#FF5722', getUrl: (title) => title ? `https://annas-archive.org/search?index=journals&q=${encodeURIComponent(title)}` : null }, SciHub: { name: 'Sci-Hub', color: '#2c7bb6', getUrl: (doi) => doi ? `https://sci-hub.st/${doi}` : null }, LibGen: { name: 'LibGen', color: '#4CAF50', getUrl: (title) => title ? `https://libgen.is/search.php?req=${encodeURIComponent(title)}` : null } }; // Settings system let settings = GM_getValue('settings', { SciDB: true, AnnasArchive: true, SciHub: true, LibGen: true }); // Stylish CSS GM_addStyle(` .access-btns { display: flex; flex-wrap: wrap; /* Allow buttons to wrap on smaller screens/results */ gap: 8px; margin-top: 10px; } .access-btn { padding: 6px 12px; border: none; border-radius: 15px; color: white !important; cursor: pointer; font-size: 0.9em; text-decoration: none !important; text-align: center; } .settings-panel { /* Ensure you have styles for this panel, e.g.: */ /* position: fixed; top: 20px; right: 20px; background: white; border: 1px solid #ccc; padding: 20px; z-index: 10000; display: none; box-shadow: 0 0 10px rgba(0,0,0,0.1); */ } .settings-icon { /* Ensure you have styles for this icon, e.g., for FontAwesome <i class="fas fa-cog"></i> */ /* position: fixed; top: 25px; right: 25px; font-size: 24px; cursor: pointer; z-index: 10001; color: #555; */ /* If using innerHTML for icon, you might need to style the <i> tag if FontAwesome isn't available */ } `); // Extract DOI const extractDOIFromText = (text) => { if (!text) return null; const doiRegex = /(?:doi:|(?:https?:\/\/)?(?:dx\.)?doi\.org\/)?(10\.\d{4,9}\/(?:[-._;()/:A-Za-z0-9]|%[0-9a-fA-F]{2})+)/i; const match = text.match(doiRegex); if (match && match[1]) { let potentialDoi = match[1]; potentialDoi = potentialDoi.replace(/%252F/gi, '%2F'); try { potentialDoi = decodeURIComponent(potentialDoi); } catch (e) { potentialDoi = potentialDoi.replace(/%2F/gi, '/'); } potentialDoi = potentialDoi.split(/[?#]/)[0]; potentialDoi = potentialDoi.replace(/(\/meta|\/pdf|\.pdf|\/html|\/full|\/fulltext|\/epdf|\/abstract|\/summary|\/xml)$/i, ''); if (/^10\.\d{4,9}\/.+$/i.test(potentialDoi)) { return potentialDoi; } } return null; }; // New comprehensive DOI finder for a Google Scholar result item const findDOIForResultItem = (resultItem) => { const explicitDoiLink = resultItem.querySelector('a[href*="doi.org/10."], a[href*="dx.doi.org/10."]'); if (explicitDoiLink && explicitDoiLink.href) { const doi = extractDOIFromText(explicitDoiLink.href); if (doi) return doi; } const links = resultItem.querySelectorAll('a[href]'); for (const link of links) { if (link.href) { const doi = extractDOIFromText(link.href); if (doi) return doi; } } const titleText = resultItem.querySelector('.gs_rt')?.innerText || ''; const abstractText = resultItem.querySelector('.gs_rs')?.innerText || ''; const metadataText = resultItem.querySelector('.gs_a')?.innerText || ''; const combinedText = `${titleText} ${metadataText} ${abstractText}`; const doiFromTextContent = extractDOIFromText(combinedText); if (doiFromTextContent) return doiFromTextContent; const resourceLink = resultItem.querySelector('.gs_ggsd a, .gs_or_ggsm a'); if (resourceLink && resourceLink.href) { const doi = extractDOIFromText(resourceLink.href); if (doi) return doi; } return null; }; // Create settings panel using service keys const createSettingsPanel = () => { const panel = document.createElement('div'); panel.className = 'settings-panel'; Object.assign(panel.style, { position: 'fixed', top: '70px', right: '20px', background: 'white', border: '1px solid #ccc', padding: '20px', zIndex: '10000', display: 'none', boxShadow: '0 0 10px rgba(0,0,0,0.1)' }); panel.innerHTML = `<h3 style="margin: 0 0 15px 0; font-size: 1.2em; color: #333;">Research Access Settings</h3>`; Object.entries(services).forEach(([key, service]) => { const div = document.createElement('div'); div.style.margin = '10px 0'; div.innerHTML = ` <label style="display: flex; align-items: center; gap: 8px; font-size: 0.95em;"> <input type="checkbox" ${settings[key] ? 'checked' : ''} data-service="${key}" style="margin-right: 5px; transform: scale(1.1);"> ${service.name} </label> `; panel.appendChild(div); }); document.body.appendChild(panel); return panel; }; // Toggle settings panel const createSettingsIcon = () => { const icon = document.createElement('div'); icon.className = 'settings-icon'; Object.assign(icon.style, { position: 'fixed', top: '20px', right: '20px', background: '#f0f0f0', border: '1px solid #ccc', padding: '8px 10px', cursor: 'pointer', zIndex: '10001', borderRadius: '5px', userSelect: 'none', color: '#333' }); icon.innerHTML = '⚙️ Settings'; icon.addEventListener('click', (e) => { e.stopPropagation(); settingsPanel.style.display = settingsPanel.style.display === 'block' ? 'none' : 'block'; }); document.body.appendChild(icon); return icon; }; const settingsPanel = createSettingsPanel(); const settingsIcon = createSettingsIcon(); // Handle settings changes settingsPanel.querySelectorAll('input[type="checkbox"]').forEach(input => { input.addEventListener('change', (e) => { settings[e.target.dataset.service] = e.target.checked; GM_setValue('settings', settings); document.querySelectorAll('.access-btns').forEach(btnSet => btnSet.remove()); addAccessButtons(); }); }); // Main function to add buttons const addAccessButtons = () => { document.querySelectorAll('.gs_ri').forEach(result => { if (result.querySelector('.access-btns')) return; const titleElement = result.querySelector('.gs_rt a'); const title = titleElement ? titleElement.innerText.trim() : (result.querySelector('.gs_rt')?.innerText.trim() || ''); const doi = findDOIForResultItem(result); const btnContainer = document.createElement('div'); btnContainer.className = 'access-btns'; Object.entries(services).forEach(([key, service]) => { if (!settings[key]) return; let identifier; if (key === 'SciHub' || key === 'SciDB') { identifier = doi; } else { identifier = title; } if (!identifier) return; const url = service.getUrl(identifier); if (!url) return; const btn = document.createElement('a'); btn.className = 'access-btn'; btn.style.backgroundColor = service.color; btn.href = url; btn.target = '_blank'; btn.rel = 'noopener noreferrer'; btn.textContent = service.name; btnContainer.appendChild(btn); }); if (btnContainer.hasChildNodes()) { result.appendChild(btnContainer); } }); }; let resultsContainer = document.getElementById('gs_res_ccl_mid') || document.getElementById('gs_res_ccl'); if (!resultsContainer) { console.warn("Scholar Access Enhancer: Could not find specific results container. Observing document body."); resultsContainer = document.body; } const observer = new MutationObserver(mutations => { let needsButtonUpdate = false; for (const mutation of mutations) { if (mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.matches && (node.matches('.gs_ri') || node.querySelector('.gs_ri'))) { needsButtonUpdate = true; break; } } } } if (needsButtonUpdate) { break; } } if (needsButtonUpdate) { addAccessButtons(); } }); observer.observe(resultsContainer, { childList: true, subtree: true }); addAccessButtons(); document.addEventListener('click', (e) => { if (settingsPanel.style.display === 'block' && !settingsPanel.contains(e.target) && !settingsIcon.contains(e.target)) { settingsPanel.style.display = 'none'; } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址