Greasy Fork

Greasy Fork is available in English.

豆瓣中图法分类

如果存在中图分类,则会在豆瓣信息栏中多显示如“中图分类: B842.1 (B84 心理学 ▸ B842 心理过程、心理状态 ▸ B842.1 认知)”,方便对图书进行分类。

当前为 2020-08-16 提交的版本,查看 最新版本

// ==UserScript==
// @name           豆瓣中图法分类
// @description    如果存在中图分类,则会在豆瓣信息栏中多显示如“中图分类: B842.1 (B84 心理学 ▸ B842 心理过程、心理状态 ▸ B842.1 认知)”,方便对图书进行分类。
// @author         018([email protected])
// @contributor    Rhilip
// @connect        *
// @grant          GM_xmlhttpRequest
// @grant          GM_setClipboard
// @grant          GM_addStyle
// @grant          GM_setValue
// @grant          GM_getValue
// @grant          GM_listValues
// @grant          GM_deleteValue
// @grant          GM_registerMenuCommand
// @require        https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @require        http://greasyfork.icu/scripts/368137-encodeToGb2312/code/encodeToGb2312.js?version=601683
// @include        https://book.douban.com/*
// @version        0.1.3
// @icon           https://img3.doubanio.com/favicon.ico
// @run-at         document-end
// @namespace      http://018.ai
// ==/UserScript==

// This Userscirpt can't run under Greasemonkey 4.x platform
if (typeof GM_xmlhttpRequest === 'undefined') {
    alert('不支持Greasemonkey 4.x,请换用暴力猴或Tampermonkey')
    return
}

// 不属于豆瓣的页面
if (!/douban.com/.test(location.host)) {
    return
}

;(function () {
    'use strict';

    $(document).ready(function () {
        var isbn = /ISBN: .*\n/.exec($('#info').eq(0).text())[0].match(/\d+/g)[0].replace(/\n| /g, '');
        var title = $('#wrapper h1').eq(0).text().replace(/\n| /g, '');
        if( isbn && title ) {
            // superlib 搜索
            requestSuperlib(isbn);
        }
    })

    // 请求图书馆联盟
    function requestSuperlib(isbn) {
        loadDoc('http://book.ucdrs.superlib.net/search?Field=all&channel=search&sw=' + isbn, {isbn: isbn}, function(doc, responseDetail, meta) {
            let found = false;
            let books = doc.querySelectorAll('.book1');
            if (books.length <= 0) {
                $('#info').append('<span class="pl">中图分类:</span> <span style="color:#989B9B">查无此信息</span>');
                return;
            }

            for (let book of books) {
                if (found) break;

                let a = book.querySelector('.book1 td>table a.px14');//
                if (a) {
                    // superlib 单本书籍查看
                    let url = a.href.replace(location.host, 'book.ucdrs.superlib.net').replace('https', 'http');
                    loadDoc(url, {isbn: meta.isbn}, function(doc1, responseDetail1, meta1) {
                        let tubox = doc1.querySelector('.tubox dl').textContent;
                        let isbn1 = opt(/【ISBN号】.*\n/.exec(tubox)).replace(/【ISBN号】|-|\n/g, '');
                        if (eqisbn(meta1.isbn, isbn1)) {
                            let clc = opt(opt(/【中图法分类号】.*\n/.exec(tubox)).match(/[a-zA-Z0-9\.]+/));
                            if (clc) {
                                found = $("#clcText").length > 0;
                                if (!found) {
                                    $('#info').append('<span class="pl">中图分类:</span> <a id="clc" target="_blank" href="https://www.clcindex.com/category/' + clc + '">'
                                                      + clc + '</a> <span id="clcText">(...)</span>');
                                    requestClc(clc);
                                }
                            }
                        }
                    }, function(err, meta) {
                        $('#info').append('<span class="pl">中图分类:</span> <span style="color:#989B9B">查无此信息</span>');
                    });
                }
            }
        }, function(err, meta) {
            $('#info').append('<span class="pl">中图分类:</span> <span style="color:#989B9B">查无此信息</span>');
        });
    }

    // 请求istic,获取分类名称
    function requestClc(clc) {
        doPost('https://ct.istic.ac.cn/site/clc/getByClassName?t=' + new Date().getTime(),
               { "Content-Type": "application/x-www-form-urlencoded" }, 'classname=' + clc, {clc: clc}, function(json, responseDetail, meta) {
            if (json.length == 0 && meta.clc.length > 0) {
                if (meta.clc.includes('.')) {
                    requestClc(meta.clc.replace(/\.\d*$/, ''));
                } else {
                    requestClc(meta.clc.replace(/\d$/, ''));
                }
                return;
            }

            let rets = []
            let jsonMap = {}
            let pid = putMapReturnPid(meta.clc, json, jsonMap, rets);
            clcText(pid, jsonMap, rets);

            htmlclc(rets);
        }, function(err, meta) {
            requestClcB(meta.clc);
        });
    }

    function htmlclc(rets) {
        if (rets.length > 0) {
            let clcText = $('#clcText');
            if (clcText.text() == '(...)' ) {
                clcText.html('(' + rets.join('<span style="color:#989B9B"> ▸ </span>') + ')');
            }
        } else {
            let clcText = $('#clcText');
            if (clcText.text() == '(...)' ) {
                clcText.html('<span style="color:#989B9B">(查无此信息)</span>');
            }
        }
    }

    // 请求clcindex,获取分类名称
    function requestClcB(clc) {
        loadDoc('https://www.clcindex.com/category/' + clc, {clc: clc}, function(doc, responseDetail, meta) {
            let rets = [];
            for (let li of doc.querySelectorAll('.breadcrumb li.active')) {
                if (li && li.textContent.trim().indexOf(' ') == 1) continue;

                rets.push(li.textContent.trim());
            }

            htmlclc(rets);
        }, function(err, meta) {
            if (err.status == 404) {
                if (meta.clc.includes('.')) {
                    requestClcB(meta.clc.replace(/\.\d*$/, ''));
                } else {
                    requestClcB(meta.clc.replace(/\d$/, ''));
                }
                return;
            }

            let clcText = $('#clcText');
            if (clcText.text() == '(...)' ) {
                clcText.html('<span style="color:#989B9B">(无法获取)</span>');
            }
        });

    }

    // 处理成map并返回pid
    function putMapReturnPid(clc, json, jsonMap, rets) {
        if (!json || json.length <= 0 || !jsonMap || !rets) return;

        let pid;
        let level;
        for (let i = 0; i < json.length; i ++) {
            if (rets.length == 0 && json[i].classNum.split('/').includes(clc)) {
                rets.push(json[i].classNum + ' ' + json[i].className);

                pid = json[i].pid;
                level = json[i].level;
            } else if( json[i].level < level) {
                jsonMap[json[i].id + ''] = json[i];
            }
        }

        return pid;
    }

    //  处理clc文字
    function clcText(pid, jsonMap, rets) {
        if (!jsonMap || !rets) return;

        if( jsonMap[pid] ) {
            rets.unshift(jsonMap[pid].classNum + ' ' + jsonMap[pid].className);

            if (jsonMap[pid].level <= 2) return;

            clcText(jsonMap[pid].pid, jsonMap, rets);
        }
    }

    // 判断两个ISBN是否相等,如:9787100005586与7100005582,具体阅读:http://www.banquanye.com/article/id-812
    function eqisbn(val1, val2) {
        if (!val1 || (val1.length != 13 && val1.length != 10) || !val2 || (val2.length != 13 && val2.length != 10)) return false;

        let no1 = getISBNNo(val1);
        let no2 = getISBNNo(val2);
        return no1 == no2;
    }

    // 获取ISBN的No,去掉前三位的978以及最后一位验证码
    function getISBNNo(val) {
        if (!val || (val.length != 13 && val.length != 10)) return;

        if (val.length == 13) {
            return val.substr(3, 9);
        } else if (val.length == 10) {
            return val.substr(0, 9);
        }
    }

    // 判断,空返回空字符串
    function opt(val) {
        if (!val) return '';

        if (val instanceof Array) {
            if (val.length > 0) {
                return val[0];
            }
        } else {
            return val;
        }
    }

    // 对使用GM_xmlhttpRequest返回的html文本进行处理并返回DOM树
    function page_parser(responseText) {
        // 替换一些信息防止图片和页面脚本的加载,同时可能加快页面解析速度
        responseText = responseText.replace(/s+src=/ig, ' data-src='); // 图片,部分外源脚本
        responseText = responseText.replace(/<script[^>]*?>[\S\s]*?<\/script>/ig, ''); //页面脚本
        return (new DOMParser()).parseFromString(responseText, 'text/html');
    }

    // 加载网页
    function loadDoc (url, meta, callback, fail) {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: function (responseDetail) {
                if (responseDetail.status === 200) {
                    let doc = page_parser(responseDetail.responseText)
                    callback(doc, responseDetail, meta)
                } else if (fail){
                    fail(responseDetail, meta);
                }
            },
            onerror: function(err) {
                if (fail) {
                    fail(err, meta);
                }
            }
        })
    }

    // get请求
    function doGet (url, meta, callback, fail) {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: function (responseDetail) {
                if (responseDetail.status === 200) {
                    callback(JSON.parse(responseDetail.responseText), responseDetail, meta)
                } else if (fail){
                    fail(responseDetail, meta);
                }
            },
            onerror: function(err) {
                if (fail) {
                    fail(err, meta);
                }
            }
        })
    }

    // post请求
    function doPost (url, headers, data, meta, callback, fail) {
        GM_xmlhttpRequest({
            method: "POST",
            url: url,
            data: data,
            headers: headers,
            onload: function(responseDetail){
                if (responseDetail.status === 200) {
                    callback(JSON.parse(responseDetail.responseText), responseDetail, meta)
                } else if (fail){
                    fail(responseDetail, meta);
                }
            },
            onerror: function(err) {
                if (fail) {
                    fail(err, meta);
                }
            }
        })
    }
})()

// 测试URL:
// https://book.douban.com/subject/26431646/
// https://book.douban.com/subject/26298597/
// https://book.douban.com/subject/5402711/