Greasy Fork

Greasy Fork is available in English.

京东评论合并工具

try to take over the world!

当前为 2020-05-01 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         京东评论合并工具
// @namespace    https://github.com/ClericPy/somethings
// @version      2.2
// @description  try to take over the world!
// @author       Clericpy
// @match        https://item.jd.com/*
// @grant        GM_setClipboard
// ==/UserScript==



(function () {
    'use strict';
    var pages = {}


    window.auto_pager_running = false
    var css_to_hidden = {}
    window.backup_mc_innerHTML = ''
    var custom_css_to_hidden = ''
    var button_style = `font: inherit; margin: 3px; overflow: visible; text-transform: none; -webkit-appearance: button; letter-spacing: 0.01em; zoom: 1; line-height: normal; white-space: nowrap; vertical-align: middle; text-align: center; cursor: pointer; -webkit-user-drag: none; user-select: none; box-sizing: border-box; font-size: 100%; padding: .5em 1em; color: rgba(0,0,0,.8); border: none transparent; background-color: #e6e6e6; text-decoration: none; border-radius: 2px; font-family: inherit;`

    var new_style = document.createElement("style");
    new_style.setAttribute('type', 'text/css')
    new_style.setAttribute('id', 'new_style')
    document.getElementsByTagName('body')[0].appendChild(new_style)

    function alert_doc() {
        var doc = `
主要用途:
1. 京东的评论每次点翻页太麻烦了, 需要找关键词的时候又不能搜索, 只好全复制出来了
2. 偶尔需要语料

使用方法:
1. 加载网页以后, 在商品介绍 tab 位置会出现 [收集评论] 按钮, 点击
2. 可以手动点 [手动翻页], 一页页收集, 也可以配置好间隔秒数(默认2秒)点右边 checkbox 自动翻页
3. 采集结束(或自行停止)后, 点击 [展示全部] 按钮, 则评论会在原来位置对多页合并
4. 自行去噪过滤(可以通过选项, 也可以自己指定 css), 然后点击 [复制 TEXT] 即可复制到剪贴板
5. emoji [文本] 点击可以打开 Ubuntu 的 pastebin 来粘贴刚才复制了的文本    
        `
        alert(doc)
    }

    function check_missing_pages() {
        var exist_page_nums = Object.keys(pages).sort()
        var max_num = exist_page_nums[exist_page_nums.length - 1]
        var missing_page_nums = []
        var i = 1
        while (i < max_num) {
            if (!(i in exist_page_nums)) {
                missing_page_nums.push(i)
            }
            i++;
        }
        if (missing_page_nums.length > 0) {
            var span = document.getElementById('commenter_state')
            span.innerText = '页码缺失: ' + missing_page_nums
        } else {
            var span = document.getElementById('commenter_state')
            span.innerText = ''
        }

    }

    function add_class_for_filter() {
        document.querySelectorAll('#comment [style="display: block;"][data-tab="item"] a.comment-plus-icon').forEach(item => {
            item.parentElement.parentElement.parentElement.classList.add('commenter_plus_vip_item')
        });
        document.querySelectorAll('.comment-op').forEach(item => {
            if (item && !/\s*举报\s*\d+\s*0\s*/.test(item.innerText)) {
                item.parentElement.parentElement.parentElement.classList.add('commenter_with_reply')
            }
            if (item && !/\s*举报\s*0\s*\d+\s*/.test(item.innerText)) {
                item.parentElement.parentElement.parentElement.classList.add('commenter_with_like')
            }
        });

    }


    function collect_dom_to_pages() {
        var curr_page = document.querySelector('#comment [style="display: block;"][data-tab="item"] [class="ui-page-curr"]')
        let commenter_crawl_button = document.getElementById('commenter_crawl_button')
        if (!curr_page) {
            return
        }
        if (!commenter_crawl_button) {
            return
        }
        var page = curr_page.innerText
        console.log(Object.keys(pages).length + ' pages: ' + Object.keys(pages))

        commenter_crawl_button.innerText = '手动翻页 ' + page
        var node = document.querySelectorAll('#comment [style="display: block;"][data-tab="item"]>div.comment-item')
        if (node.length > 0) {
            let num = 0
            let page_num = pages.length
            for (const items of Object.values(pages)) {
                num += items.length
            }
            document.getElementById('commenter_status_bar').innerText = ' 已采集 ' + Object.keys(pages).length + ' 页 ' + num
            pages[page] = node
        }
        if (Object.keys(pages).length == 1) {
            let show_button = document.getElementById('commenter_show_button')
            show_button.disabled = false
            show_button.style.color = 'black'
        }
        check_missing_pages()
        // 反爬导致崩溃, 放上备用页面
        if (document.querySelector('#comment [style="display: block;"][data-tab="item"]')) {
            window.backup_mc_innerHTML = document.querySelector('#comment .mc').innerHTML
        } else {
            document.getElementById('commenter_status_bar').innerText = '没有评论了, 点击[展示全部]进行复制'
        }
        add_class_for_filter()
    }

    function filt_by_text() {
        let commenter_text_filter = document.getElementById('commenter_text_filter')
        let text_filters = []
        if (commenter_text_filter) {
            text_filters = commenter_text_filter.value.split(' ')
            if (text_filters == ['']) {
                text_filters = []
            }
        }
        for (const item of document.querySelectorAll('#comment [style="display: block;"][data-tab="item"]>.comment-item')) {
            var item_html = item.innerHTML
            if (!text_filters) {
                item.classList.remove('commenter_filt_by_text')
            }
            for (const text of text_filters) {
                if (text[0] == '-') {
                    if (item_html.includes(text.slice(1, text.length))) {
                        item.classList.add('commenter_filt_by_text')
                        break
                    }
                } else if (!item_html.includes(text)) {
                    item.classList.add('commenter_filt_by_text')
                    break
                } else {
                    item.classList.remove('commenter_filt_by_text')
                }
            }
        }
    }

    function show_pages() {
        var container = document.querySelector('#comment [style="display: block;"][data-tab="item"]')
        if (!container) {
            // 备用 container
            var mc_node = document.querySelector('#comment .mc')
            if (window.backup_mc_innerHTML) {
                mc_node.innerHTML = window.backup_mc_innerHTML
            }
        }
        var backup_np_node = document.getElementsByClassName('com-table-footer')[0]
        container.innerHTML = ''
        Object.keys(pages).sort().forEach(function (key) {
            let items = pages[key]
            items.forEach(item => {
                container.appendChild(item)
            });
        });
        document.getElementById('commenter_copy_button').style.display = 'inline-block'
        document.getElementById('commenter_copy_text_button').style.display = 'inline-block'
        if (backup_np_node) {
            container.appendChild(backup_np_node)
        }
        add_class_for_filter()
        filt_by_text()
    }

    function copy_html() {
        let items = document.querySelectorAll('#comment [style="display: block;"][data-tab="item"]>.comment-item')
        let text = ''
        let filt_plus = document.getElementById('commenter_non_plus_vip').checked
        let filt_reply = document.getElementById('commenter_non_reply').checked
        let filt_like = document.getElementById('commenter_non_like').checked
        items.forEach(item => {
            if (filt_plus && !item.classList.contains('commenter_plus_vip_item')) {
                return
            }
            if (filt_reply && !item.classList.contains('commenter_with_reply')) {
                return
            }
            if (filt_like && !item.classList.contains('commenter_with_like')) {
                return
            }
            if (item.classList.contains('commenter_filt_by_text')) {
                return
            }
            text += item.outerHTML
        });
        GM_setClipboard(text, 'text')
    }

    function copy_text() {
        let items = document.querySelectorAll('#comment [style="display: block;"][data-tab="item"]>.comment-item')
        let text = ''
        let filt_plus = document.getElementById('commenter_non_plus_vip').checked
        let filt_reply = document.getElementById('commenter_non_reply').checked
        let filt_like = document.getElementById('commenter_non_like').checked
        items.forEach(item => {
            if (filt_plus && !item.classList.contains('commenter_plus_vip_item')) {
                return
            }
            if (filt_reply && !item.classList.contains('commenter_with_reply')) {
                return
            }
            if (filt_like && !item.classList.contains('commenter_with_like')) {
                return
            }
            if (item.classList.contains('commenter_filt_by_text')) {
                return
            }
            text += item.innerText.replace('\n', ' ') + '\n'
        });
        GM_setClipboard(text, 'text')
    }

    function auto_pager_with_interval() {
        collect_next_page()
    }

    function shutdown_auto_pager() {
        if (window.auto_pager_running) {
            window.clearInterval(window.current_autopager)
            window.auto_pager_running = false
        }
    }

    function auto_next_page() {
        shutdown_auto_pager()
        var commenter_auto_np_node = document.getElementById('commenter_auto_np')
        if (commenter_auto_np_node.checked) {
            window.auto_pager_running = true
            var interval = document.getElementById('commenter_auto_np_interval').value
            window.current_autopager = setInterval(() => {
                auto_pager_with_interval()
            }, interval * 1000);

        } else {
            shutdown_auto_pager()
            return
        }
    }

    function update_new_style() {
        let hidden_list = ['.commenter_filt_by_text']
        Object.keys(css_to_hidden).forEach(key => {
            let value = css_to_hidden[key]
            if (value) {
                hidden_list.push(key)
            }
        });
        if (custom_css_to_hidden) {
            hidden_list.push(custom_css_to_hidden)
        }
        document.getElementById('new_style').innerHTML = hidden_list.join(',') + '{display: none;}\n'
    }

    function commenter_clear(checked, css_value) {
        css_to_hidden[css_value] = checked
        update_new_style()
    }

    function commenter_collect_layouts() {

        var head = document.getElementById('comment')
        var mc_node = document.querySelector('#comment .mc')

        var hr = document.createElement("hr");
        head.insertBefore(hr, mc_node)


        var show_button = document.createElement("button");
        show_button.innerText = '展示全部'
        show_button.setAttribute('id', 'commenter_show_button')
        show_button.setAttribute('style', button_style)
        show_button.disabled = true
        show_button.style.color = 'grey'
        show_button.addEventListener('click', show_pages)
        head.insertBefore(show_button, mc_node)

        var copy_button = document.createElement("button");
        copy_button.innerText = '复制 HTML'
        copy_button.setAttribute('id', 'commenter_copy_button')
        copy_button.setAttribute('style', button_style)
        copy_button.style.display = 'none'
        copy_button.addEventListener('click', copy_html)
        head.insertBefore(copy_button, mc_node)

        var copy_text_button = document.createElement("button");
        copy_text_button.innerText = '复制 TEXT'
        copy_text_button.setAttribute('id', 'commenter_copy_text_button')
        copy_text_button.setAttribute('style', button_style)
        copy_text_button.style.display = 'none'
        copy_text_button.addEventListener('click', copy_text)
        head.insertBefore(copy_text_button, mc_node)


        var pastebin = document.createElement("a");
        pastebin.innerText = '📋'
        pastebin.href = 'https://paste.ubuntu.com/'
        pastebin.target = '_blank'
        pastebin.style.margin = '2px'
        head.insertBefore(pastebin, mc_node)

        var commenter_state = document.createElement("span");
        commenter_state.setAttribute('id', 'commenter_state')
        commenter_state.setAttribute('style', 'padding: 0.5em;')
        head.insertBefore(commenter_state, mc_node)

        var commenter_auto_np_interval = document.createElement("input");
        commenter_auto_np_interval.setAttribute('id', 'commenter_auto_np_interval')
        commenter_auto_np_interval.setAttribute('value', '2')
        commenter_auto_np_interval.setAttribute('size', 1)
        commenter_auto_np_interval.setAttribute('style', 'text-align: center;')
        head.insertBefore(commenter_auto_np_interval, mc_node)

        var filter_node = document.createElement("div")
        filter_node.setAttribute('id', 'commenter_filter')
        filter_node.style.display = 'inline'
        filter_node.innerHTML = `
<label for="commenter_auto_np">
    <span style="weight: 1000; margin: 3px;">(s) <b>自动翻页</b>:</span>
    <input type="checkbox" id="commenter_auto_np">
</label>
<button id="commenter_crawl_button" disabled style="font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; margin: 3px; overflow: visible; text-transform: none; -webkit-appearance: button; letter-spacing: 0.01em; zoom: 1; line-height: normal; white-space: nowrap; vertical-align: middle; text-align: center; cursor: pointer; -webkit-user-drag: none; user-select: none; box-sizing: border-box; font-size: 100%; padding: 0.5em 1em; color: grey; border: none transparent; background-color: rgb(230, 230, 230); text-decoration: none; border-radius: 2px; font-family: inherit; weight: 1000">手动翻页</button> | 
<button id="commenter_get_help">帮助</button>
<span id="commenter_status_bar"></span>
<hr><span><b>去噪:</b></span>
<label for="commenter_user"><input type="checkbox" checked name=".user-info" id="commenter_user">用户</label>
<label for="commenter_production"><input type="checkbox" checked name=".order-info>span:first-child" id="commenter_production">产品</label>
<label for="commenter_time"><input type="checkbox" checked name=".order-info>span:last-child" id="commenter_time">时间</label>
<label for="commenter_append"><input type="checkbox" checked name=".append-comment" id="commenter_append">追评</label>
<label for="commenter_append_time"><input type="checkbox" checked name=".append-comment>.append-time" id="commenter_append_time">追评时间</label>
<label for="commenter_other"><input type="checkbox" checked name=".comment-op,.user-level,.comment-star,.comment-message" id="commenter_other">冗余</label>
<label for="commenter_comment"><input type="checkbox" name=".comment-con" id="commenter_comment">文本</label>
<label for="commenter_comment_pics"><input type="checkbox" checked name=".pic-list" id="commenter_comment_pics">图片</label>
<label for="commenter_comment_video"><input type="checkbox" checked name=".J-video-view-wrap" id="commenter_comment_video">视频</label>
<label for="commenter_comment_tags"><input type="checkbox" checked name=".comment-info" id="commenter_comment_tags">标签</label>
<label for="commenter_seller_comment"><input type="checkbox" checked name=".recomment-con" id="commenter_seller_comment">卖家回复</label>
<label for="commenter_non_plus_vip"><input type="checkbox" name=".comment-item:not(.commenter_plus_vip_item)" title="如果选中, 则只显示 Plus 会员的评论" id="commenter_non_plus_vip">非 PLUS 会员</label>
<br>
<label for="commenter_non_reply"><input type="checkbox" name=".comment-item:not(.commenter_with_reply)" title="如果选中, 则只显示有回复的评论" id="commenter_non_reply">无回复</label>
<label for="commenter_non_like"><input type="checkbox" name=".comment-item:not(.commenter_with_like)" title="如果选中, 则只显示有点赞的评论" id="commenter_non_like">无赞</label>
<label for="commenter_comment_custom"><input type="text" style="width:10em;" value="" placeholder="自定义 CSS 过滤" id="commenter_comment_custom"></label>
<label for="commenter_text_filter"><input type="text" style="width:15em;" value="" placeholder="过滤词, 如'手机 -三星'" title="过滤词, 如'手机 -三星'" id="commenter_text_filter"></label>


<hr>
        `
        head.insertBefore(filter_node, mc_node)

        document.getElementById('commenter_get_help').addEventListener('click', alert_doc)
        document.getElementById('commenter_text_filter').addEventListener('input', filt_by_text)
        document.getElementById('commenter_crawl_button').addEventListener('click', collect_next_page)
        document.getElementById('commenter_auto_np').addEventListener('change', function () {
            auto_next_page()
        }, false)

        var filter_ids = ['commenter_user', 'commenter_production', 'commenter_time', 'commenter_append', 'commenter_append_time', 'commenter_other', 'commenter_comment', 'commenter_comment_pics', 'commenter_comment_video', 'commenter_comment_tags', 'commenter_seller_comment', 'commenter_non_plus_vip', 'commenter_non_reply', 'commenter_non_like']
        filter_ids.forEach(eid => {
            let node = document.getElementById(eid)
            css_to_hidden[node.name] = node.checked
            update_new_style()
            node.addEventListener('change', function () {
                commenter_clear(this.checked, this.name)
            })
        });
        let commenter_comment_custom = document.getElementById('commenter_comment_custom')

        commenter_comment_custom.addEventListener('input', function () {
            custom_css_to_hidden = this.value
            this.title = this.value
            update_new_style()
        })
        var tries = 0
        var checkExist = setInterval(function () {
            tries += 1
            if (document.getElementById('commenter_crawl_button')) {
                document.querySelectorAll('ul.filter-list>li').forEach(element => {
                    element.addEventListener('click', function () {
                        pages = {}
                    })
                });
                commenter_crawl_button.disabled = false
                commenter_crawl_button.style.color = 'black'
                collect_dom_to_pages()
                clearInterval(checkExist);
                return
            }
            if (tries > 20) {
                clearInterval(checkExist);
                return
            }
        }, 200);

    }

    function collect_next_page() {
        var np = document.querySelector('#comment [style="display: block;"][data-tab="item"] .ui-pager-next')
        if (!np) {
            shutdown_auto_pager()
            var commenter_auto_np_node = document.getElementById('commenter_auto_np')
            commenter_auto_np_node.checked = false
            document.getElementById('commenter_status_bar').innerText = '没有下一页, 点击[展示全部]进行复制'
            alert('没有下一页')
            return false
        } else {
            np.removeAttribute('href')
            np.click()
        }
        return true
    }

    function setup_commenter_collect_layouts() {
        if (document.getElementById('commenter_auto_np_interval')) {
            return
        }
        document.querySelector('[data-anchor="#comment"]').click()
        let observer = new MutationObserver(collect_dom_to_pages);
        let options = {
            'childList': true,
            'subtree': true,
            // 'characterData': true,
            // 'attributes': true,
        };
        // observer.observe(document.querySelector('#comment>.mc'), options)
        var tries = 0
        var checkExist = setInterval(function () {
            tries += 1
            var c0 = document.getElementById('comment-0')
            if (c0) {
                // console.log("Exists!");
                c0.setAttribute('style', 'display: block;')
                observer.observe(document.querySelector('#comment .mc >.comments-list>.tab-con'), options);
                commenter_collect_layouts()
                clearInterval(checkExist);
                return
            }
            if (tries > 20) {
                clearInterval(checkExist);
                return
            }
        }, 200);

    }

    function setup() {
        var tab = document.querySelector('#detail .tab-main>ul')
        var button = document.createElement("button");
        button.innerHTML = '<b>收集评论</b>'
        button.setAttribute('id', 'commenter_collect')
        button.setAttribute('style', button_style)
        button.style.zoom = '1.3'
        button.addEventListener('click', setup_commenter_collect_layouts)
        tab.appendChild(button)
    }
    window.onload = setup
})();