K12教育小初高各个版本教材内的章节数据

K12教育小初高各个版本教材内的章节数据

月光魔力鸭

2018-10-19 08:47 阅读 905 喜欢 2 K12教材章节 node爬虫

一直在做K12教育方面的产品,不过大都是学校管理层面的,对于教学一直很少触及。当然,这类的产品已经存在不少了,在功能、设计方面我们都会借鉴一些进行改进,其中有一些基础数据,如果也是从头自己收集的话,我想,可能会死的吧...比如:各个学段内的教材和章节信息。

在这里,找了一个“相对”权威的资源网站,然后把这个网站上的所有章节、教材全部爬下来,进行保存数据库,用于我们后续教学方面的基础数据。

这里先说下结果:算上小学、初中、高中三个学段,所有学科下共计5151本册别,所有册别下所包括的章节共计187123个章节。

简单记录下,一是为了后续可能还会有类似的情况而数据丢了,二是为了其他人可能有类似的情况,方便实现。

相关准备

思路

  1. 首先了解下目标网址下章节信息是如何获得的,如果是异步的话,尝试看能否直接通过调用获取,否则的话就通过html解析内容获得数据。
  2. 经过确认后,发现章节信息是从html内的js写死的,根据不同的地址返回不同的章节,所以,我们只需要把所有的地址拿到,然后访问获得章节即可。
  3. 根据学段、学科、出版社进行循环遍历获得所有的册别地址,然后用后续进行轮询。

大体实现

以下主要列一下核心代码,

//根据该函数获得学科、教材、册别等信息
class getHtml {
    constructor(url){
        this.url = url;
    }
    getContent(){
        return new Promise((resolve,reject)=>{
            request.get(this.url)
            .then( res=>{
                resolve(res.text);
            }).catch(err=>{
                reject(err);
            })
        });
    }
    //根据内容获得某一部分的地址
    getGradeHref(text,index){
        var $ = cheerio.load(text);
        var grades = $('.check_choice_list>li').eq(index).find('p a');
        var arr = [];
        grades.each( (index,item)=>{
            arr.push($(item).attr('href'));
        });
        return arr;
    }
    //获得
}
//然后通过该函数进行循环获取所有的地址,然后存储到数据库中,目前共计5151条记录。

然后根据数据库存储的册别地址,一个一个的获取就好了。

//从国家教育资源公共服务平台获取章节目录信息并进行存储

var request = require('superagent');

var cheerio = require('cheerio');

var fs = require('fs');

var query = require('simple-mysql-query');
query({
    host : '127.0.0.1',
    port : '3306',
    user : 'root',
    password : 'root',
    database : 'test'
});

class Fetch{
    constructor(){
        this.suc = 0;
        this.current = null;
        this.err = 0;
    }
    //获得下一个未完成的数据并继续
    getNext(){
        var that = this;
        return new Promise((resolve,reject) => {
            query({
                sql : 'select * from chapterurl where done =0 limit 0,1',params : []
            }).then( rs => {
                var rst = rs[0];
                if(rst.length == 0){
                    that.current = null;
                    resolve(null);
                }else{
                    that.current = rst[0].id;
                    resolve(rst[0].url);
                }
            }).catch(err=>{
                reject(err);
            })
        });
    }
    //从页面获得章节信息
    fetchArr( url ){
        var that = this;
        return new Promise((resolve,reject)=> {
            if(url){
                request.get(host+url)
                .set({
                    'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                    'Accept-Encoding': 'gzip, deflate',
                    'Accept-Language': 'en,zh-CN;q=0.9,zh;q=0.8',
                    'Cache-Control': 'no-cache',
                    'Connection': 'keep-alive',
                    'Cookie': 'UM_distinctid=1667c2776e2ba4-053f6c4dfa889e-8383268-1fa400-1667c2776e367c; USER=40be8854-6a4a-4cdc-95ec-f863add22572; name=value; JSESSIONID=12574E63722E3472694692F8E8F83529; soweb123=so-web06; CNZZDATA1255309935=1698827792-1539679457-http%253A%252F%252Fwww.eduyun.cn%252F%7C1539823023; Hm_lvt_d251d7d8815cdb16dd7b3407d1e80eba=1539681324,1539681583,1539735613,1539824040; CNZZDATA1254936525=38783214-1539681033-http%253A%252F%252Fwww.eduyun.cn%252F%7C1539819962; Hm_lpvt_d251d7d8815cdb16dd7b3407d1e80eba=1539824492',
                    'Host': 'so.eduyun.cn',
                    'Pragma': 'no-cache',
                    'Upgrade-Insecure-Requests': '1',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
                })
                .then( res=> {
                    var text = res.text;
                    var rs = /booktree.loadtree\((.*)}]\);/.exec(text);
                    var get = rs[1]+'}]';
                    var arr = JSON.parse(get);
                    var arr2 = that.flatt(arr);
                    console.log('共计章节数量:'+arr2.length);
                    resolve(arr2);
                }).catch(err=>{
                    resolve([]);
                })
            }else{
                resolve(null);//null代表结束
            }
        });
    }
    //数据持久-数据库
    store (arr) {
        var that = this;
        //直接一次全部存储
        var sql = 'insert into chapterinfo (bookCatelogId,bookCatelogName,bookId,bookName,isKe,parentId,pubVerID,pubVerName,schoolPhaseId,schoolPhaseName,seqNo,subjectId,subjectName,vNo,zhouCi) values ',
            params = [];
        if(arr === null){
            return -1;//结束
        }else if(arr.length ===  0){
            return new Promise((resolve,reject)=>{
                console.log('本章节虽然可能会成功,但是没有章节数据')
                query({
                    sql : 'update chapterurl set done=2 where id=? ',params : [that.current]
                }).then( rs => {
                    resolve(1);
                }).catch(err=>{
                    resolve(2);
                })
            });
        }
        for(var i =0;i<arr.length;i++){
            var item = arr[i];
            sql += '( ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)'+(i == arr.length-1 ? '' : ',');
            params = params.concat([item.bookCatelogId,item.bookCatelogName,item.bookId,item.bookName,item.isKe,item.parentId,item.pubVerID,item.pubVerName,item.schoolPhaseId,item.schoolPhaseName,item.seqNo,item.subjectId,item.subjectName,item.vNo,item.zhouCi]);
        }
        return new Promise((resolve,reject) => {
            query([{sql : sql,params : params},{sql : 'update chapterurl set done=1 where id=?',params : [that.current]}]).then( rs => {
                resolve(1);
            }).catch(err=>{
                console.log(err);
                resolve(2)
            });
        });
    }
    //数组扁平化
    flatt (arr) {
        var that = this;
        var newarr = [],flag = true;
        arr.forEach( item => {
            //如果有children就提取出来,放在arr中
            if(item.children && item.children.length > 0){
                var children = item.children;
                item.children = null;
                newarr.push(item);
                newarr = newarr.concat(children);
                flag = false;
            }else{
                newarr.push(item);
            }
        })
        if(flag){
            return newarr;
        }else{
            return that.flatt(newarr);
        }
    }
}

var ins = new Fetch();

function gogogo(){
    ins.getNext()
    .then( url => {
        return ins.fetchArr(url);
    })
    .then( arr => {
        return ins.store(arr);
    })
    .then( rs => {
        if(rs === 1){
            console.log('章节存储成功一个');
            gogogo();
        }else if(rs === 2){
            console.log('章节存储失败,切换下一个')
            gogogo();
        }else if(rs === 0){
            console.log('本章节无数据,切换下一个?如果连续出现请检查')
            gogogo();
        }else if(rs === -1){
            console.log('数据库已经清空,章节全部存储结束')
        }
    })
    .catch(err=> {
        console.log(err);
    })
}

gogogo()

由于是一次性工作,并没有对代码进行细致检查,大体实现到结果就完了。 如果对实现完全不感兴趣,只想得到结果的,那么请访问这里:百度网盘地址 , 密码是: 46ba


对于教育,我们一直在努力变好!

转载请注明出处: https://chrunlee.cn/article/k12-education-chapter-info.html


感谢支持!

赞赏支持
提交评论
评论信息 (请文明评论)
暂无评论,快来快来写想法...
推荐
新高考改革早已成大趋势,对于广大考生家长和考生来说,赋分一直难以理解,不理解赋分规则,为什么要赋分? 为此,百年育才莱西校区特对赋分进行介绍,供参考
记录下部署php项目的时候的经过,查了很多资料,最后才定出的这个,中间些许问题可能已经记不清楚了,趁着时间步长,赶紧回忆一下。
参与1+X证书制度试点的前四批职业技能等级证书标准(2021年更新版)
本来这是一个很小的事,按照邮箱设置来一下就OK了.. 可是我密码忘记了,死活重置不了,没办法,微信扫码吧,然后按照设置来又是不行,找了下度娘才行.. 我就纳闷了,这么大一企业,不能用点心么.. 还有昨天那个互联,愁死!
好久配置了,最近重新安装了一回,有忘了.. 果然还是得记录下来啊,起码以后不用到处找了对吧。
最近入手了华为鲲鹏服务器... 作为一个菜鸡前端,去部署服务器,亚历山大.. 即便是各种抄抄抄也无济于事.
这不是之前做了一个磁力小站么.. 发现之前好多记录都是重复的(不要问我怎么发现的),就删除下,感觉不难,但总是写不对.. 这里纯粹记下sql.
虽说docker已经大名鼎鼎,但在之前一直都未上手使用过,即便是前一阵子想挂下京东的京豆(使用docker)也给耽搁了,一直的感觉就是这货应该跟vm没啥区别吧,应该就是更方便更好用更适合开发者。