您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Extracts tweets with a real-time board, save as JSON.
// ==UserScript== // @name Text Extraction - navigate // @namespace http://tampermonkey.net/ // @version 1.4.1 // @description Extracts tweets with a real-time board, save as JSON. // @match https://x.com/* // @license MIT // @grant // ==/UserScript== /* MIT License * * Copyright (c) 2024 [955whynot] * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ (function () { 'use strict'; // Add error handling for localStorage function safelyGetFromStorage() { try { return JSON.parse(localStorage.getItem('extractedData')) || []; } catch (e) { console.error('Error reading from localStorage:', e); return []; } } const extractedData = safelyGetFromStorage(); let observer; // To hold the MutationObserver instance // Create floating controls (board, start button, and other UI) const controls = document.createElement('div'); controls.style.position = 'fixed'; controls.style.bottom = '10px'; controls.style.right = '10px'; controls.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; controls.style.color = 'white'; controls.style.padding = '10px'; controls.style.fontSize = '14px'; controls.style.zIndex = '9999'; document.body.appendChild(controls); const clearButton = document.createElement('button'); clearButton.textContent = 'Clear'; clearButton.style.backgroundColor = 'maroon'; clearButton.style.color = 'white'; clearButton.style.fontWeight = "700"; clearButton.style.padding = '6px'; clearButton.style.marginBottom = '10px'; clearButton.style.marginLeft = '8px'; controls.appendChild(clearButton); const display = document.createElement('div'); display.style.maxHeight = '200px'; display.style.overflowY = 'auto'; display.style.border = '1px solid white'; display.style.padding = '5px'; display.style.marginTop = '10px'; display.innerHTML = '<strong>Extracted Data:</strong><br>'; controls.appendChild(display); const saveButton = document.createElement('button'); saveButton.textContent = 'Save as JSON'; saveButton.style.marginTop = '10px'; saveButton.style.backgroundColor = '#FF8C00'; saveButton.style.color = 'white'; saveButton.style.fontWeight = "700"; saveButton.style.padding = '5px'; controls.appendChild(saveButton); // Add counter to display const counter = document.createElement('div'); counter.style.marginTop = '5px'; counter.style.fontWeight = 'bold'; counter.textContent = `Total Items: ${extractedData.length}`; controls.insertBefore(counter, display); // Function to update the display board function updateDisplay() { try { display.innerHTML = '<strong>Extracted Data:</strong><br>' + extractedData.map((item, index) => `<div><strong>Div ${index + 1}:</strong> ${item}</div>`).join(''); counter.textContent = `Total Items: ${extractedData.length}`; localStorage.setItem('extractedData', JSON.stringify(extractedData)); } catch (e) { console.error('Error updating display:', e); } } // Function to extract data from a single div, preserving sequence function extractFromDiv(div) { let result = ''; const children = div.childNodes; children.forEach((child) => { if (child.nodeType === Node.TEXT_NODE) { const text = child.textContent.trim(); if (text) { result += (!result.endsWith(' ') ? ' ' : '') + text; } } else if (child.nodeType === Node.ELEMENT_NODE) { if (child.tagName === 'SPAN') { const text = child.innerText.trim(); if (text) { result += (!result.endsWith(' ') ? ' ' : '') + text; } } else if (child.tagName === 'IMG') { const alt = child.getAttribute('alt'); if (alt) { result += alt; } } else if (child.tagName === 'A' && child.getAttribute('role') === 'link') { const hashtag = child.innerText.trim(); if (hashtag) { result += (!result.endsWith(' ') ? ' ' : '') + hashtag; } } } }); return result.trim(); } // Function to extract from all divs function extractAll() { if (window.location.pathname.startsWith('/search')) { const targetDivs = document.querySelectorAll('div[data-testid="tweetText"]'); targetDivs.forEach((div) => { const divData = extractFromDiv(div); if (divData && !extractedData.includes(divData)) { extractedData.push(divData); updateDisplay(); } }); } else { console.log('Extraction skipped. Not on /search.'); } } // Function to enable the extraction process function enableExtraction() { extractAll(); observer = new MutationObserver(() => { if (window.location.pathname.startsWith('/search')) { extractAll(); } }); observer.observe(document.body, { childList: true, subtree: true }); console.log('Extraction enabled.'); } // Function to disable the extraction process function disableExtraction() { if (observer) observer.disconnect(); console.log('Extraction disabled.'); } // Monitor navigation dynamically function monitorNavigation() { if (window.location.pathname.startsWith('/search')) { enableExtraction(); } else { disableExtraction(); } } // Attach functionality to buttons clearButton.onclick = () => { extractedData.length = 0; updateDisplay(); }; saveButton.onclick = () => { const blob = new Blob([JSON.stringify(extractedData, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'extracted_data.json'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; // Monitor navigation dynamically using popstate and hashchange window.addEventListener('popstate', monitorNavigation); window.addEventListener('hashchange', monitorNavigation); // Initial check for current page monitorNavigation(); })();