码云 Gitee IDE 全新上线——支持 Git 管理的轻量在线编码环境
Watch Star Fork

netnr / zoningJavaScriptMIT

加入码云
与超过 300 万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
中华人民共和国行政区划:省级(省份直辖市自治区)、 地级(城市)、 县级(区县)、 乡级(乡镇街道)、 村级(村委会居委会) https://ss.netnr.com/zoning
netnr 最后提交于 update
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README.md 10.94 KB

zoning

中华人民共和国行政区划:省级、地级、县级、乡级和村级


Gitee https://gitee.com/netnr/zoning

GitHub https://github.com/netnr/zoning


Demo https://ss.netnr.com/zoning


来源

国家统计局 - 统计用区划和城乡划分代码

统计数据截止2017-10-31 于 2018-06-20发布

http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/

http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2017


使用

  • 打开页面 http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2017/index.html

  • 打开浏览器控制台(推荐Chrome、Firefox,请不要用IE系列,谢谢)

  • 拷贝脚本zoning.js的内容粘贴到控制台运行

  • 首次抓取会出现大量失败请求,再次抓取会从浏览器缓存获取,非常快

  • Chrome比较快,会出现几个链接抓取失败

  • Firefox比较稳定,抓取有保障,内存占用高(推荐,测试版本:61.0.2 64-bit)

  • build用于导出的JSON文件生成SQLiteCSV


发布

  • 支持的格式有:JSON文件、SQLite数据库、CSV文件

  • 0.json根数据, zoning-*.json所有数据, zoning-*.dbSQLite数据库, zoning-*.csvCSV文件

  • dist/zoning-5 五级(全部数据)

  • dist/zoning-4 四级

  • dist/zoning-3 三级


代码

/*
 * https://github.com/netnr/zoning
 * 
 * zoning 1.0.0
 * 
 * 2018-08-19
 * netnr
 */

var zoning = {
    //版本号
    version: "1.0.0",
    //载入js脚本
    getScript: function (src, success) {
        var ele = document.createElement("SCRIPT");
        ele.src = src;
        ele.type = "text/javascript";
        document.getElementsByTagName("HEAD")[0].appendChild(ele);
        //加载完成回调
        if (success != undefined) {
            ele.onload = ele.onreadystatechange = function () {
                if (!this.readyState || this.readyState == "loaded" || this.readyState == "complete") { success(); }
            }
        }
    },
    //参数配置
    config: {
        //jszip CDN
        urljszip: "https://lib.baomitu.com/jszip/3.1.4/jszip.min.js",
        //fileSaver CDN
        urlfilesaver: "https://lib.baomitu.com/FileSaver.js/2014-11-29/FileSaver.min.js",
        //抓取首页
        urlprefix: "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2017/",
        //起始深度
        deep: 1,
        //最大深度
        //5 村 约46800
        //4 街道 约3380
        deepmax: 5,
        //抓取过程信息
        item: {
            //父级编码
            id: "0",
            //请求相对地址
            href: "index"
        }
    },
    //fetch 抓取
    grab: function (urlprefix, deep, item) {
        if (item.href == null) {
            return false;
        }
        var url = urlprefix;
        switch (deep) {
            case 4:
                url += item.id.substr(0, 2) + "/";
                break;
            case 5:
                url += item.id.substr(0, 2) + "/" + item.id.substr(2, 2) + "/";
                break;
        }
        url += item.href + ".html";

        zoning.taskcount += 1;

        //fetch 抓取 gb2312
        fetch(url).then(res => res.blob()).then(blob => {
            var reader = new FileReader();
            reader.onload = function () {
                zoning.taskcount -= 1;
                var list = zoning.matcharray(reader.result, item, deep);
                zoning.taskdefer[item.id] = setInterval(function () {
                    //终止
                    if (zoning.stop) {
                        clearInterval(zoning.taskdefer[item.id]);
                        return false;
                    }
                    //暂停、任务量限制
                    if (!zoning.pause && zoning.taskcount < 200) {
                        clearInterval(zoning.taskdefer[item.id]);
                        if (list.length > 0 && deep < zoning.config.deepmax) {
                            for (var i = 0; i < list.length; i++) {
                                var li = list[i];
                                deep += 1;
                                zoning.grab(urlprefix, deep, li);
                                deep -= 1;
                            }
                        }
                    }
                }, 10);
            }
            reader.readAsText(blob, 'GBK');
        }).catch(function (e) {
            var obj = {};
            obj.item = item;
            obj.url = url;
            obj.href = item.href;
            obj.deep = deep;
            obj.error = e + "";
            zoning.catchdata.push(obj);
            zoning.taskcount -= 1;
        });
    },
    //异常记录重新抓取
    grabcatch: function (catchdata) {
        for (var i = 0; i < catchdata.length; i++) {
            var cdi = catchdata[i];
            cdi.item.href = cdi.href;
            zoning.grab(zoning.config.urlprefix, cdi.deep, cdi.item);
        }
    },
    //任务延时记录
    taskdefer: {},
    //任务量
    taskcount: 0,
    //抓取数量
    matchcount: 0,
    //抓取异常记录
    catchdata: [],
    //抓取结果数据
    matchdata: {},
    //匹配抓取内容
    matcharray: function (data, item, deep) {
        var arr = [];
        if (deep != 5) {
            //匹配 市辖区 无链接 项
            data.replace(/<td>[0-9]{12}<\/td><td>.*?<\/td>/g, function (x) {
                var mat = x.split('</td><td>');
                var obj = {};
                obj.href = null;
                obj.id = mat[0].split('>')[1];
                obj.text = mat[1].split('<')[0];
                arr.push(obj);
            });
        }
        data = data.replace(/'/g, '"').replace(/<br\/>/g, "");
        //匹配所有的A标签
        var reg = /<a[^>]*href=['"]([^"]*)['"][^>]*>(.*?)<\/a>/g;
        var matchs = data.match(reg);
        var filename = "0";
        switch (deep) {
            //首页
            case 1:
                if (!matchs) {
                    return [];
                }
                for (var i = 0; i < matchs.length; i++) {
                    var mat = matchs[i];
                    var obj = {};
                    obj.id = mat.split('"')[1].split('.')[0];
                    obj.href = obj.id;
                    obj.text = mat.split('>')[1].split('<')[0];
                    arr.push(obj);
                }
                break;
            case 2:
            case 3:
            case 4:
                if (!matchs) {
                    return [];
                }
                for (var i = 0; i < matchs.length; i++) {
                    var mat = matchs[i];
                    var obj = {};
                    obj.href = mat.split('"')[1].split('.')[0];
                    obj.id = mat.split('>')[1].split('<')[0];
                    mat = matchs[++i];
                    obj.text = mat.split('>')[1].split('<')[0];
                    arr.push(obj);
                }
                break;
            case 5:
                //匹配 村委会 无连接
                data.replace(/<td>[0-9]{12}<\/td><td>[0-9]{3}<\/td><td>.*?<\/td>/g, function (x) {
                    var mat = x.split('</td><td>');
                    var obj = {};
                    obj.href = null;
                    obj.id = mat[0].split('>')[1];
                    obj.text = mat[2].split('<')[0];
                    arr.push(obj);
                });
                break;
        }
        //根据深度 得到文件名(编码)
        switch (deep) {
            case 2:
                filename = item.id;
                break;
            case 3:
                filename = item.id.substr(0, 4);
                break;
            case 4:
                filename = item.id.substr(0, 6);
                break;
            case 5:
                filename = item.id.substr(0, 9);
                break;
        }
        zoning.matchdata[filename] = arr;
        //记录请求结果数量
        zoning.matchcount += 1;
        return arr;
    },
    //外部调用生成下载
    zip: function () {
        zoning.ziping(zoning.matchdata, zoning.catchdata);
    },
    //内部调用生成下载
    ziping: function (matchdata, catchdata) {
        zoning.getScript(zoning.config.urljszip, function () {
            zoning.getScript(zoning.config.urlfilesaver, function () {
                var zip = new JSZip();

                var data = {};
                for (var i in matchdata) {
                    var di = matchdata[i];
                    for (var j = 0; j < di.length; j++) {
                        delete di[j].href;
                        switch (i.length) {
                            case 2:
                                di[j].id = di[j].id.substr(0, 4);
                                break;
                            case 4:
                                di[j].id = di[j].id.substr(0, 6);
                                break;
                            case 6:
                                di[j].id = di[j].id.substr(0, 9);
                                break;
                        }
                    }
                    data[i] = di;
                    if (i.length > 1) {
                        zip.file(i.substr(0, 2) + "/" + i + ".json", JSON.stringify(di));
                    }
                    else {
                        zip.file(i + ".json", JSON.stringify(di));
                    }
                }
                zip.file("zoning-" + zoning.config.deepmax + ".json", JSON.stringify(data));
                if (catchdata.length) {
                    zip.file("catch-" + zoning.config.deepmax + ".json", JSON.stringify(catchdata));
                }
                zip.generateAsync({ type: "blob" }).then(function (content) {
                    saveAs(content, "zoning-" + zoning.config.deepmax + ".zip");
                });
            });
        });
    },
    //开始运行
    run: function () {
        zoning.startTime = new Date().valueOf();
        zoning.taskdefer.run = setInterval(function () {
            console.log("count: " + zoning.matchcount + "  taskcount: " + zoning.taskcount);
            if (!zoning.pause && zoning.taskcount == 0) {
                clearInterval(zoning.taskdefer.run);
                zoning.zip();
            }
        }, 1000 * 4);
        console.log('fetching ... please see the network tab');
        zoning.grab(zoning.config.urlprefix, zoning.config.deep, zoning.config.item);
    }
};

//开始运行 可手动调用
zoning.run();

//下载zip,抓取完成后
//zoning.zip();

/*
 * 注意:
 * 
 * 首次抓取会出现大量失败请求,再次抓取会从浏览器缓存获取,非常快。
 * 
 * 文件:
 * 0.json 根数据
 * 12.json 二级数据
 * 1234.json 三级数据
 * 123456.json 四级数据
 * 123456789.json 五级数据
 * 
 * 其他:
 * zoning-*.json 所有数据,* 代表级数
 * catch-*.json 抓取异常记录(有异常时)
 * 
 * 测试:
 * Chrome比较快,会出现几个链接抓取失败;
 * Firefox比较稳定,抓取有保障,内存高
 * 
 */

联系打赏

项目点评 ( 0 )

你可以在登录后,发表评论

搜索帮助