X (Twitter) Feed Text Extractor

Extract and monitor formatted text content from X (Twitter) feed

  1. // ==UserScript==
  2. // @name X (Twitter) Feed Text Extractor
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.7
  5. // @description Extract and monitor formatted text content from X (Twitter) feed
  6. // @match https://x.com/*
  7. // @grant none
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. let isMonitoring = false;
  15. let collectedTweets = new Set();
  16. let observer;
  17.  
  18. const button = document.createElement('button');
  19. button.textContent = '开始监控X内容';
  20. button.style.position = 'fixed';
  21. button.style.top = '10px';
  22. button.style.right = '10px';
  23. button.style.zIndex = '9999';
  24. document.body.appendChild(button);
  25.  
  26. button.addEventListener('click', toggleMonitoring);
  27.  
  28. function formatRelativeTime(dateStr) {
  29. const now = new Date();
  30. const past = new Date(dateStr);
  31. const diffMs = now - past;
  32. const diffSec = Math.floor(diffMs / 1000);
  33. const diffMin = Math.floor(diffSec / 60);
  34. const diffHour = Math.floor(diffMin / 60);
  35. const diffDay = Math.floor(diffHour / 24);
  36. const diffMonth = Math.floor(diffDay / 30);
  37. const diffYear = Math.floor(diffDay / 365);
  38.  
  39. if (diffYear > 0) {
  40. return `${diffYear}年前`;
  41. } else if (diffMonth > 0) {
  42. return `${diffMonth}个月前`;
  43. } else if (diffDay > 0) {
  44. return `${diffDay}天前`;
  45. } else if (diffHour > 0) {
  46. return `${diffHour}小时前`;
  47. } else if (diffMin > 0) {
  48. return `${diffMin}分钟前`;
  49. } else {
  50. return '刚刚';
  51. }
  52. }
  53.  
  54. function toggleMonitoring() {
  55. if (isMonitoring) {
  56. stopMonitoring();
  57. displayCollectedTweets();
  58. } else {
  59. startMonitoring();
  60. }
  61. }
  62.  
  63. function startMonitoring() {
  64. isMonitoring = true;
  65. button.textContent = '停止监控并显示内容';
  66. collectedTweets.clear();
  67.  
  68. const currentTweets = document.querySelectorAll('[data-testid="tweet"]');
  69. currentTweets.forEach(processTweet);
  70.  
  71. const config = { childList: true, subtree: true };
  72. observer = new MutationObserver(mutations => {
  73. mutations.forEach(mutation => {
  74. if (mutation.type === 'childList') {
  75. mutation.addedNodes.forEach(node => {
  76. if (node.nodeType === Node.ELEMENT_NODE) {
  77. const tweets = node.querySelectorAll('[data-testid="tweet"]');
  78. tweets.forEach(processTweet);
  79. }
  80. });
  81. }
  82. });
  83. });
  84.  
  85. observer.observe(document.body, config);
  86. }
  87.  
  88. function stopMonitoring() {
  89. isMonitoring = false;
  90. button.textContent = '开始监控X内容';
  91. if (observer) {
  92. observer.disconnect();
  93. }
  94. }
  95.  
  96. function processTweet(tweet) {
  97. const tweetContent = formatTweet(tweet);
  98. if (tweetContent) {
  99. collectedTweets.add(tweetContent);
  100. }
  101. }
  102.  
  103. function displayCollectedTweets() {
  104. let extractedText = Array.from(collectedTweets).join('\n\n---\n\n');
  105. const newWindow = window.open('', '_blank');
  106. newWindow.document.write('<pre>' + extractedText + '</pre>');
  107. }
  108.  
  109. function formatTweet(tweet) {
  110. const authorElement = tweet.querySelector('div[data-testid="User-Name"]');
  111. if (!authorElement) return null;
  112.  
  113. const authorName = authorElement.querySelector('span')?.textContent || 'Unknown';
  114. const authorId = authorElement.querySelector('a[href^="/"]')?.getAttribute('href')?.slice(1) || 'Unknown';
  115. const timeElement = tweet.querySelector('time');
  116. const tweetTime = timeElement ? formatRelativeTime(timeElement.getAttribute('datetime')) : 'Unknown time';
  117.  
  118. let formattedTweet = `作者: ${authorName} (@${authorId})\n发布时间: ${tweetTime}\n\n`;
  119.  
  120. const tweetTextElement = tweet.querySelector('[data-testid="tweetText"]');
  121. if (tweetTextElement) {
  122. formattedTweet += `内容: ${tweetTextElement.textContent}\n`;
  123. }
  124.  
  125. const retweetElement = tweet.querySelector('[data-testid="socialContext"]');
  126. if (retweetElement) {
  127. const retweetAuthor = retweetElement.textContent.replace('转推', '').trim();
  128. formattedTweet = `转推自: ${retweetAuthor}\n\n${formattedTweet}`;
  129. }
  130.  
  131. return formattedTweet;
  132. }
  133. })();

QingJ © 2025

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