慕课网笔记导出

导出慕课网的笔记,分为点赞和采集两种(本人编写的也会被导出)。导出的格式为json。导出过程可以暂停,下次在进行导出。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         慕课网笔记导出
// @namespace    https://github.com/yeomanye
// @version      0.2
// @description  导出慕课网的笔记,分为点赞和采集两种(本人编写的也会被导出)。导出的格式为json。导出过程可以暂停,下次在进行导出。
// @author       Ming Ye
// @match        http://www.imooc.com/*/*
// @include      https://www.imooc.com/*/*
// @require      https://greasyfork.org/scripts/34143-debug/code/debug.js?version=232648
// @require      http://cdn.bootcss.com/jquery/1.8.3/jquery.min.js
//  @require https://greasyfork.org/scripts/27104-filesaver/code/FileSaver.js?version=173518
// @require      https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var log = window.myDebugger.consoleFactory('慕课网导出笔记脚本:', 'color:brown;font-size:13px'),
        debugTrue = window.myDebugger.debugTrue(true);
    var $btnContainer = $('#main .course-info-main .content-wrap .mod-tab-menu .course-menu'),
        $collectBtn = $('<a id="exportNoteBtn" style="margin:10px;padding:10px;border-radius:5px;background-color:orange;color:white;cursor:pointer;">导出笔记</a>'),//采集按钮
        $exportColCheck = $('<div style="display:inline-block;margin-right:10px"><input type="checkbox" id="exportColCheck"/><span style="color:white;">采集</span>'),
        $exportPraCheck = $('<input type="checkbox" id="exportPraCheck"/><span style="color:white;">点赞</span>'),
        $curPageLabel = $('<p id="curPage" style="margin-top:10px;margin-left:30px;color:white;"><span>当前采集页数:</span><span id="curPageNum">0</span></p>');
    
    $collectBtn.click(btnClickHandler).appendTo($('#main .course-infos .hd'));
    $exportColCheck.appendTo($('#main .course-infos .hd'));
    $exportPraCheck.appendTo($('#main .course-infos .hd'));
    $curPageLabel.appendTo($('#main .course-infos .hd'));
    
    var isPause = true,
        courseId = window.location.pathname.split("/")[2], //课程ID
        baseUrl = window.location.protocol + '//' + window.location.hostname + '/note/' + courseId + '?sort=hot&page=',
        pageNum = localStorage.getItem('pageNum-'+courseId), //笔记页码
        noteTimeout = null,
        userObj = fetchUser().data, //用户对象,保存用户相关信息
        notes = JSON.parse(localStorage.getItem('notes-'+courseId)), //
        isCol = false,//是否收集采集的笔记
        isPra = false;//是否收集点赞的笔记
    log('课程ID', courseId);
    log('基准url', baseUrl);
    pageNum = pageNum ? pageNum : 1;

    /**
     * 导出笔记按钮点击事件
     */
    function btnClickHandler(){
        isPause = !isPause;
        if(isPause){
            $("#exportNoteBtn").text('导出笔记');
            clearTimeout(noteTimeout)
        }else{
            isCol = $('#exportColCheck')[0].checked;
            isPra = $('#exportPraCheck')[0].checked;
            $("#exportNoteBtn").text('暂停导出');
            fetchNote();
        }
    }
    /**
     * 采集笔记
     */
    function fetchNote(){
        var htmlText = $.ajax({
            url: baseUrl + pageNum,
            async: false
        }).responseText;
        //最后导出笔记
        if(htmlText.indexOf('此课程暂无同学记录过笔记')>=0){
            var blob = new Blob([JSON.stringify(notes)], {type: "text/plain;charset=utf-8"});
            saveAs(blob, "notes.json");
            return;
        }

        log('pageNum',pageNum);
        $("#curPageNum").html(pageNum);
        ++pageNum;
        var obj = getNoteElem(htmlText);
        var noteStatus = getUserNoteStatus(obj.noteIds);
        var colObj = getColObj(noteStatus);
        var colIdArr = []; //需要采集的笔记的id数组
        if(isCol){
            colIdArr = colObj.col;
        }else if(isPra){
            for(var i=0,len=colObj.pra.length;i<len;i++){
                colIdArr.push(colObj.pra[i]);
            }
        }
        var dataArr = fetchColContent(colIdArr,obj.noteStrArr);
        
        notes = notes || [];
        for (var i = 0, len = dataArr.length; i < len; i++) {
            notes.push(dataArr[i]);
        }
        log('pageNum',pageNum);
        // debugTrue();
        localStorage.setItem('notes-'+courseId, JSON.stringify(notes));
        localStorage.setItem('pageNum-'+courseId,pageNum);
        //当查询不到笔记容器id表示已经获取完所有的笔记了;
        noteTimeout = setTimeout(fetchNote,100);
        return ;
    }
    
    /**
     * 获取采集对象
     * @param  {array} colArr 采集参照的id数组
     * @return {object}        采集对象
     */
    function fetchColContent(colArr,liStrArr) {
        var retDatas = [];
        for(var i=0,len=colArr.length;i<len;i++){
            var id = colArr[i];
            for(var j=0,len2=liStrArr.length;j<len2;j++){
                var liStr = liStrArr[j];
                if(liStr.indexOf('id="'+id+'"')>=0 || liStr.indexOf('authorid="'+userObj.uid+'"')>=0){
                    var patternNote = /<div class="js-notelist-content notelist-content mynote">[\s\S]*?<pre class="autowrap">([\s\S]*?)<\/pre>/g,
                        patternUser = /<div class="tit">[\s\S]*?<a [\s\S]*? target="_blank">([\s\S]*?)<\/a>/g,
                        patternTime = /<div class="footer clearfix">[\s\S]*?<span [\s\S]*?>时间:([\s\S]*?)<\/span>/g,
                        patternChapter = /<div class="footer clearfix">[\s\S]*?<a [\s\S]*?>源自:([\s\S]*?)<\/a>/g,
                        patternAuthLink = /<div class="media">[\s\S]*?<a href="([\s\S]*?)">/,
                        patternScreenshot = /<div class="js-toggle-notelist answerImg">[\s\S]*?<img [\s\S]*? data-src="([\s\S]*?)"/,
                        patternCourseLink = /<div class="footer clearfix">[\s\S]*?<a href="[\s\S]*?"/g,
                        patternFileId = /<div class="disscus-code-icon-wrap">[\s\S]*?<i class="disscus-code-icon js-show-node-code" data-id="([\s\S]*?)"/g;
                    var noteStr = patternNote.exec(liStr)[1],
                        user = patternUser.exec(liStr)[1],
                        time = patternTime.exec(liStr)[1],
                        chapter = patternChapter.exec(liStr)[1],
                        authLink = window.location.origin + patternAuthLink.exec(liStr)[1],
                        courseLink = window.location.origin + patternCourseLink.exec(liStr)[1],
                        matchScreenshotLink = patternScreenshot.exec(liStr),
                        matchFileId = patternFileId.exec(liStr);
                    var data = {
                        note:noteStr,
                        time:time,
                        user:user,
                        chapter:chapter,
                        authLink:authLink,
                        courseLink:courseLink
                    };
                    //如果存在截图,则获取截图url
                    if(matchScreenshotLink && matchScreenshotLink[1]){
                        data.screenshotLink = window.location.protocol+matchScreenshotLink[1]
                    }
                    //如果存在代码快照,则获取代码快照
                    if(matchFileId && matchFileId[1]){
                        var retStr = $.ajax({
                            url: 'https://www.imooc.com/course/viewnotecode?id='+matchFileId[1],
                            async: false,
                        }).responseText;
                        data.codeFiles = JSON.parse(retStr).files;
                    }
                    log.logObj('data',data);
                    retDatas.push(data);
                }
            }
        }

        log.logArr('fetchColContent', retDatas);
        return retDatas;
    }
    /**
     * 使用jsonp方式获取用户对象,获得对象后保存在window.usr中
     */
    function fetchUser() {
        var retStr = $.ajax({
            url: window.location.protocol+'//www.imooc.com/u/card%20?jsonpcallback=getUser&_=' + new Date().getTime(),
            async: false,
        }).responseText;
        log('fetchUser', retStr);
        retStr = retStr.replace('getUser(', '');
        retStr = retStr.substr(0, retStr.length - 1);
        var usr = JSON.parse(retStr);
        log.logObj('fetchUser', usr);
        return usr;
    }
    /**
     * 获取采集的id数组
     * @param  {object} noteStatus 笔记状态对象
     * @return {object}            返回采集和点赞的数组{col:[],pra:[]}
     */
    function getColObj(noteStatus) {
        var tmp = noteStatus.data.collections,
            retObj = {
                col: [],
                pra: []
            };
        for (var i = 0, len = tmp.length; i < len; i++) {
            retObj.col.push(tmp[i].src_note_id);
        }
        tmp = noteStatus.data.praises;
        for (var i = 0, len = tmp.length; i < len; i++) {
            retObj.pra.push(tmp[i].note_id);
        }
        log.logObj('getColIds', retObj);
        return retObj;
    }
    /**
     * 获取笔记元素的id和字符串数组
     * @return {object} 笔记id和笔记元素字符串数组
     */
    function getNoteElem(htmlText){
        var pattern = /<ul id="js-note-container" class="mod-post">([\s\S]*)?<\/ul>/g;
        var pattern1 = /<li id="([\d]+)" class="post-row js-find-txt" courseid="[\d]+" noteId="[\d]+" authorid="[\d]+">[\s\S]*?<\/li>/g;
        var matchArr = pattern.exec(htmlText),
            liIds = [],
            liStrArr = [],
            regInput = matchArr[0];
        while(matchArr = pattern1.exec(regInput)){
            log.logObj('matchArr',matchArr);
            liIds.push(matchArr[1]);
            liStrArr.push(matchArr[0]);
        }
        log.logObj('liStrArr',liStrArr);
        log.logArr('liIds',liIds);
        return {noteIds:liIds,noteStrArr:liStrArr};
    }
    /**
     * 获取用户笔记状态
     * @param  {array} ids 用户笔记id数组
     * @return {object} 用户笔记操作状态对象
     */
    function getUserNoteStatus(ids) {
        var retStr = $.ajax({
                url: window.location.protocol+'//www.imooc.com/course/AjaxUserNotesStatus?ids=' + ids.join('%2C'),
                async: false
            }).responseText,
            retObj = JSON.parse(retStr);
        log('url',window.location.protocol+'//www.imooc.com/course/AjaxUserNotesStatus?ids=' + encodeURI(ids.join('%2C')));
        log.logObj('getUserNoteStatus', retObj);
        return retObj;
    }
    
})();