樊登提取器(简洁版)

樊登所有听书信息导出 json。(打造自己读书阅读网站)

// ==UserScript==
// @name         樊登提取器(简洁版)
// @namespace    http://tampermonkey.net/
// @version      1.0.5
// @description  樊登所有听书信息导出 json。(打造自己读书阅读网站)
// @author       qqlcx5
// @match        https://www.dushu365.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=dushu365.com
// @grant        GM_setClipboard
// @run-at       document-end
// @license MIT
// ==/UserScript==

;(function () {
  'use strict'

  /**
   * 提取所有卡片的信息
   * Extracts information from all cards
   */
  function extractAllCardsInfo() {
    // 选择所有卡片的容器
    const cards = document.querySelectorAll('a.css-o3rmpk')

    if (cards.length === 0) {
      console.warn('未找到卡片信息 (No cards found)')
      return
    }

    // 存储所有卡片的信息
    const allCardsInfo = []

    cards.forEach((card) => {
      // 提取封面图片URL
      const coverImg = card.querySelector('.chakra-image.css-cquex')
      const coverUrl = coverImg ? coverImg.src : null

      // 提取标题
      const titleElement = card.querySelector('.css-fekyw2')
      const title = titleElement ? titleElement.textContent.trim() : null

      // 提取副标题(介绍)
      const subtitleElement = card.querySelector('.css-10uyroi')
      const subtitle = subtitleElement ? subtitleElement.textContent.trim() : null

      // 提取上新日期
      const dateElement = card.querySelector('.css-bokfi4')
      const date = dateElement ? dateElement.textContent.trim() : null

      // 提取播放量
      const playCountElement = card.querySelector('.css-166fvu2')
      const playCount = playCountElement ? playCountElement.textContent.trim() : null

      // 提取 <a> 标签的 href 属性
      const href = card.getAttribute('href')

      // 拼接完整的 source URL
      const source = href ? `https://www.dushu365.com${href}` : null

      // 将提取的信息存储为对象
      const cardInfo = {
        coverUrl,
        title,
        subtitle,
        date,
        playCount,
        source, // 添加 source 属性
      }

      allCardsInfo.push(cardInfo)
    })

    // 根据 playCount 进行排序
    allCardsInfo.sort((a, b) => {
      const aCount = parsePlayCount(a.playCount)
      const bCount = parsePlayCount(b.playCount)
      return bCount - aCount
    })

    console.info('提取到的所有卡片信息:', allCardsInfo)

    // 导出信息为JSON格式
    const jsonInfo = JSON.stringify(allCardsInfo, null, 2)
    console.info('导出的JSON信息:', jsonInfo)

    // 将信息复制到剪贴板
    GM_setClipboard(jsonInfo)
    notifyUser('所有卡片信息已复制到剪贴板。')
  }

  /**
   * 解析 playCount 字符串为数字
   * Parses the playCount string into a number
   * @param {string} playCount - 播放次数字符串 (Play count string)
   * @returns {number} - 解析后的数字 (Parsed number)
   */
  function parsePlayCount(playCount) {
    if (!playCount) return 0

    // 处理 "播放量" 的情况
    if (playCount.includes('播放量')) {
      const count = parseFloat(playCount.replace('播放量', '').replace('万', '')) * 10000
      return count
    }

    return 0
  }

  /**
   * 显示提示通知给用户
   * Displays a notification to the user
   * @param {string} message - 要显示的消息 (Message to display)
   */
  function notifyUser(message) {
    // 创建通知元素
    const notification = document.createElement('div')
    notification.textContent = message

    // 样式设计 (Styling)
    Object.assign(notification.style, {
      position: 'fixed',
      bottom: '80px',
      right: '20px',
      backgroundColor: '#333',
      color: '#fff',
      padding: '10px 15px',
      borderRadius: '5px',
      opacity: '0',
      transition: 'opacity 0.5s',
      zIndex: '10000',
      fontSize: '13px',
    })

    document.body.appendChild(notification)

    // 触发动画
    setTimeout(() => {
      notification.style.opacity = '1'
    }, 100) // Slight delay to allow transition

    // 自动淡出和移除
    setTimeout(() => {
      notification.style.opacity = '0'
      setTimeout(() => {
        document.body.removeChild(notification)
      }, 500) // 等待淡出完成
    }, 3000) // 显示3秒
  }

  /**
   * 自动滚动到页面底部
   * Automatically scrolls to the bottom of the page
   */
  function autoScrollToBottom() {
    // 监听页面内容的变化
    const observer = new MutationObserver(() => {
      // 滚动到页面底部
      window.scrollTo({
        top: document.body.scrollHeight,
        behavior: 'smooth', // 平滑滚动
      })
    })

    // 配置观察选项:监听子节点的变化
    const config = {
      childList: true,
      subtree: true,
    }

    // 开始观察页面主体
    observer.observe(document.body, config)

    // 初始滚动到底部
    window.scrollTo({
      top: document.body.scrollHeight,
      behavior: 'smooth',
    })
  }

  /**
   * 初始化脚本
   * Initializes the userscript
   */
  function initializeScript() {
    // 创建一个按钮来触发信息提取
    const button = document.createElement('button')
    button.textContent = '提取所有卡片信息'

    // 样式设计 (Styling)
    Object.assign(button.style, {
      position: 'fixed',
      right: '20px',
      bottom: '100px',
      padding: '12px 20px',
      backgroundColor: '#FF4D4F',
      color: '#fff',
      border: 'none',
      borderRadius: '8px',
      boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
      cursor: 'pointer',
      fontSize: '14px',
      zIndex: '10000',
      transition: 'background-color 0.3s, transform 0.3s',
    })

    // 鼠标悬停效果 (Hover Effects)
    button.addEventListener('mouseenter', () => {
      button.style.backgroundColor = '#FF7875'
      button.style.transform = 'scale(1.05)'
    })

    button.addEventListener('mouseleave', () => {
      button.style.backgroundColor = '#FF4D4F'
      button.style.transform = 'scale(1)'
    })

    // 点击事件 (Click Event)
    button.addEventListener('click', extractAllCardsInfo)

    // 添加按钮到页面主体 (Append button to the body)
    document.body.appendChild(button)

    console.info('卡片信息提取脚本已启用。')

    // 启动自动滚动
    autoScrollToBottom()
  }

  // 等待页面内容加载完毕后初始化脚本
  // Wait for the DOM to be fully loaded before initializing
  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    initializeScript()
  } else {
    document.addEventListener('DOMContentLoaded', initializeScript)
  }
})()

QingJ © 2025

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