從統計局採集最新的省市區縣數據,純js

来源:https://www.cnblogs.com/xiangyuecn/archive/2018/01/27/8366101.html
-Advertisement-
Play Games

18 01 28早上6:30的火車,從三亞回老家,票難買啊。好激動~ 聲明:文中涉及到的數據和第三方介面、url僅供學習使用,請勿它用~ 這幾天都在磨著搭建本地測試環境,看到省市區數據表裡面是空的,想著以前的老數據還是13年採集的,含省市區縣4級數據共4.8萬條,時間久了,使用過程中發現有些新的城市 ...


18-01-28早上6:30的火車,從三亞回老家,票難買啊。好激動~
聲明:文中涉及到的數據和第三方介面、url僅供學習使用,請勿它用~

這幾天都在磨著搭建本地測試環境,看到省市區數據表裡面是空的,想著以前的老數據還是13年採集的,含省市區縣4級數據共4.8萬條,時間久了,使用過程中發現有些新的城市名稱資料庫中沒有,縣級數據從來就沒有用到過,想著還是重新採集一份。

新採集的省市區數據有3589條,這次並沒有把縣級數據採過來,需要的時候再添加也挺好。

數據來源

國家統計局統計標準《2016年統計用區劃代碼和城鄉劃分代碼(截止2016年07月31日)》,這個是2017-05-16發佈的,當前是最新的。

數據採集

對於數據採集,根據工作需要,對於一些小的數據採集功能有些接觸。因為對html和js熟些,很早以前就用IE瀏覽器對本地html文件支持任意跨域ajax請求數據、和支持讀寫Excel文件,就直接寫一個html文件作為採集工具給別人使用,批量查詢人員資料、考試結果什麼的功能。所以採集省市區數據主要用的js。

1. 抓取原始數據

打開網頁http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/index.html省份的數據就有了,進入市級頁面,然後進入區級頁面,還可以進入縣級頁面。整個流程地址結構非常簡單,數據格式也很好提取。

進入網頁後打開瀏覽器控制台,執行下麵代碼,這段代碼僅僅包含採集省市區的,把縣級的閹割掉了,13年的老代碼有縣級的。很早以前寫的代碼,風格有點醜,不過能能正常使用就是好的,這個採集是“單線程的”,因為這些數據少,速度並不慢:

/*
獲取城市名稱http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016/index.html
*/
(function(){
if(!window.URL){
    throw new Error("瀏覽器版本太低");
};
function ajax(url,True,False){
    var ajax=new XMLHttpRequest();
    ajax.timeout=1000;
    ajax.open("GET",url);
    ajax.onreadystatechange=function(){
        if(ajax.readyState==4){
            if(ajax.status==200){
                True(ajax.responseText);
            }else{
                False();
            }
        }
    }
    ajax.send();
}
function msg(){
    console.log.apply(console, arguments);
}

function cityClass(name,url,code){
    this.name=name;
    this.url=url;
    this.code=code;
    this.child=[];
    this.tryCount=0;
}
cityClass.prototype={
    getValue:function(){
        var obj={name:this.name,code:this.code,child:[]};
        for(var i=0;i<this.child.length;i++){
            obj.child.push(this.child[i].getValue());
        }
        return obj;
    }
}

function load_all(True){
    var path="http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2016";
    ajax(path+"/index.html",function(text){
        var reg=/href='(.+?)'>(.+?)<br/ig,match;
        var idx;
        if((idx=text.indexOf("<tr class='provincetr'>"))+1){
            reg.lastIndex=idx;
            while(match=reg.exec(text)){
                var url=match[1];
                if(url.indexOf("//")==-1 && url.indexOf("/")!=0){
                    url=path+"/"+url;
                }
                var name=match[2];
                DATA.push(new cityClass(name,url,0));
            }
            True();
        }else{
            msg("未發現省份數據");
        }
    },function(){
        msg("讀取省份列表出錯","程式終止");
    });
}
function load_shen(True, False){
    var city=DATA[JD.shen];
    city.tryCount++;
    if(city.tryCount>3){
        msg("讀取省份["+city.name+"]超過3次");
        False();
        return;
    };
    
    function get(){
        msg("讀取省份["+city.name+"]", getJD());
        save();
        
        city.child[JD.si].tryCount=0;
        load_si(function(){
            JD.shen++;
            if(JD.shen>=DATA.length){
                JD.shen=0;
                True();
                return;
            };
            DATA[JD.shen].tryCount=0;
            
            load_shen(True,False);
        },function(){
            False();
        });
    }
    
    if(city.child.length){
        get();
    }else{
        ajax(city.url,function(text){
            var reg=/<tr class='citytr'>.+?href='(.+?)'>(.+?)<.+?'>(.+?)</ig;
            var match;
            while(match=reg.exec(text)){
                var url=match[1];
                if(url.indexOf("//")==-1 && url.indexOf("/")!=0){
                    url=city.url.substring(0,city.url.lastIndexOf("/"))+"/"+url;
                }
                var code=match[2];
                var name=match[3];
                city.child.push(new cityClass(name,url,code));
            }
            
            JD.si=0;
            get();
        },function(){
            load_shen(True,False);
        });
    };
}

function load_si(True,False){
    var shen=DATA[JD.shen];
    var city=shen.child[JD.si];
    city.tryCount++;
    if(city.tryCount>3){
        msg("讀取城市["+city.name+"]超過3次");
        False();
        return;
    };
    
    
    function get(){
        msg("___讀取城市["+city.name+"]", getJD());
        
        city.child[JD.xian].tryCount=0;
        JD.si++;
        if(JD.si>=shen.child.length){
            JD.si=0;
            True();
            return;
        };
        shen.child[JD.si].tryCount=0;
        
        load_si(True,False);
    }
    
    if(city.child.length){
        get();
    }else{
        ajax(city.url,function(text){
            var reg=/class='(?:countytr|towntr)'.+?<\/tr>/ig;
            var match;
            while(match=reg.exec(text)){
                var reg2=/class='(?:countytr|towntr)'.+?(?:<td><a href='(.+?)'>(.+?)<.+?'>(.+?)<|<td>(.+?)<.+?<td>(.+?)<)/ig;
                var match2;
                if(match2=reg2.exec(match[0])){
                    var url=match2[1]||"";
                    if(url.indexOf("//")==-1 && url.indexOf("/")!=0){
                        url=city.url.substring(0,city.url.lastIndexOf("/"))+"/"+url;
                    }
                    var code=match2[2]||match2[4];
                    var name=match2[3]||match2[5];
                    city.child.push(new cityClass(name,url,code));
                }else{
                    msg("未知城市模式:");
                    msg(city.url);
                    msg(match[0]);
                    throw new Error("end");
                }
            }
            
            JD.xian=0;
            get();
        },function(){
            load_si(True,False);
        });
    };
}


function getJD(){
    var str="省:"+(JD.shen+1)+"/"+DATA.length;
    var shen=DATA[JD.shen];
    if(shen){
        str+=" 市:"+(JD.si+1)+"/"+shen.child.length;
        var si=shen.child[JD.si];
        if(si){
            str+=" 縣:"+(JD.xian+1)+"/"+si.child.length;
        }else{
            str+=" 縣:"+JD.xian;
        }
    }else{
        str+=" 市:"+JD.si+" 縣:"+JD.xian;
    }
    return str;
}
function save(){
    
}

var DATA=[];
var JD;
window.RunLoad=function(shen,si,xian){
    RunLoad.T1=Date.now();
    JD={
        shen:shen||0
        ,si:si||0
        ,xian:xian||0
    }
    
    function get(){
        DATA[JD.shen].tryCount=0;
        load_shen(function(){
            console.log("完成:"+(Date.now()-RunLoad.T1)/1000+"秒");
            save();
            
            var data=[];
            for(var i=0;i<DATA.length;i++){
                data.push(DATA[i].getValue());
            }
            
            var url=URL.createObjectURL(
                new Blob([
                    new Uint8Array([0xEF,0xBB,0xBF])
                    ,"var CITY_LIST="
                    ,JSON.stringify(data,null,"\t")
                ]
                ,{"type":"text/plain"})
            );
            var downA=document.createElement("A");
            downA.innerHTML="下載查詢好城市的文件";
            downA.href=url;
            downA.download="data.txt";
            document.body.appendChild(downA);
            downA.click();
            
            msg("--完成--");
        },function(){
            save();
            msg("當前進度:", getJD());
        });
    }
    
    var data=localStorage["load_data"];
    if(data){
        DATA=JSON.parse(data);
        get();
    }else{
        load_all(get);
    }
}
})();//@ sourceURL=console.js


//立即執行代碼
RunLoad()

採集截圖:

2. 處理數據和拼音標註

數據處理就簡單些了,比如編號格式化、名稱格式化等。

拼音標註:這個需要找一個介面對文字進行拼音翻譯,只有一個要求:重慶能正常的翻譯成chong qing即可,翻譯成zhong qing的就low了。滿足這個條件,百度上搜索到的翻譯小網站80%就被幹掉了。

瀏覽器中打開找到的翻譯介面http://www.qqxiuzi.cn/zh/pinyin/,截止到目前是能正常調用的,因為要用ajax請求數據,在頁面裡面就沒有跨域的問題,查看網頁源碼,把token值記錄下來,這個網站翻譯請求需要帶這個token,註意~刷新頁面要重新獲取:

拼音這個因為數據量比較多,採用了“4個線程”採集,先把第一步採集到的文件打開,把數據複製到打開的翻譯網站瀏覽器控制台裡面執行(相當於把數據導入),然後執行下麵代碼:

/*
拼音翻譯
http://www.qqxiuzi.cn/zh/pinyin/

http://www.qqxiuzi.cn/zh/pinyin/show.php
POST
t=漢字&d=1&s=null&k=1&b=null&h=null&u=null&v=1&y=null&z=null&token=頁面token請求一次獲取

先載入數據
    控制台輸入data.txt
*/
window.PageToken=window.PageToken||"";
var FixTrim=function(name){
    return name.replace(/^\s+|\s+$/g,"");
};
var CITY_LIST2;
var QueryPinYin=function(end){
    if(!window.PageToken){
        console.error("Need PageToken");
        return;
    };
    var ids=[];
    
    var fixCode=function(o){
        if(o.deep==0){
            o.orgCode="0";
        }else{
            o.orgCode=o.code;
            if(o.deep==1){
                o.code=o.code.substr(o,4);
            }else{
                o.code=o.code.replace(/(000000|000)$/g,"");//有少部分區多3位
            };
        };
        return o;
    };
    var fix=function(o,p){
        var name=o.name;
        if(o.deep==0){
            name=name.replace(/(||(維吾爾|壯族|回族)?自治區)$/ig,"");
        }else if(o.deep==1){
            if(name=="市轄區"){
                name=p.o2.name;
            }else if(/行政區劃$/ig.test(name)){
                name="直轄市";
            }else if(name.length>2){
                name=name.replace(/市$/ig,"");
            };
        }else{
            if(name.length>2 && name!="市轄區"
                && !/(自治.|地區|礦區)$/.test(name)){//直接排除會有同名的
                name=name.replace(/(||||管委會|街道辦事處)$/ig,"");
            };
        };
        var o2={
            name:name
            ,ext_name:o.name
            ,id:+o.code||0
            ,ext_id:+o.orgCode
            ,pid:p&&+p.code||0
            ,deep:o.deep
        };
        o.o2=o2;
        return o2;
    };
    for(var i=0;i<CITY_LIST.length;i++){
        var shen=CITY_LIST[i];
        shen.deep=0;
        for(var i2=0;i2<shen.child.length;i2++){
            var si=shen.child[i2];
            if(!shen.code){
                shen.code=si.code.substr(0,2);
                ids.push(fix(fixCode(shen)));
            };
            si.deep=1;
            ids.push(fix(fixCode(si),shen));
            
            
            for(var i3=0;i3<si.child.length;i3++){
                var qu=si.child[i3];
                qu.deep=2;
                ids.push(fix(fixCode(qu),si));
            };
        };
    };
    CITY_LIST2=ids;
    //console.log(JSON.stringify(ids,null,"\t"))
    //return;
    
    var idx=-1;
    var run=function(stack){
        stack=+stack||0;
        idx++;
        if(idx>=ids.length){
            thread--;
            if(thread==0){
                end();
            };
            return;
        };
        
        var idx_=idx;
        var id=ids[idx];
        if(id.P){
            stack++;
            if(stack%50==0){
                setTimeout(function(){run()});
            }else{
                run(stack);
            };
            return;
        };
        
        var name=id.name;
        var tryCount=0;
        var tryLoad=function(){
            $.ajax({
                url:"/zh/pinyin/show.php"
                ,data:"t="+encodeURIComponent(name)+"&d=1&s=null&k=1&b=null&h=null&u=null&v=1&y=null&z=null&token="+PageToken
                ,type:"POST"
                ,dataType:"text"
                ,timeout:1000
                ,error:function(e){
                    if(tryCount>3){
                        console.error("--QueryPinYin error--"+e);
                        run();
                        return;
                    };
                    tryCount++;
                    tryLoad();
                }
                ,success:function(txt){
                    txt=FixTrim(txt.replace(/<.+?>/g,"").replace(/\s+/g," "));
                    id.P=txt;
                    console.log("--"+idx_+"-QueryPinYin "+name+":"+txt+" --");
                    run();
                }
            });
        };
        tryLoad();
    };
    
    var thread=4;
    run();
    run();
    run();
    run();
};


var ViewDown=function(){
    console.log("完成:"+(Date.now()-RunPinYin.T1)/1000+"秒");
    window.CITY_LIST_PINYIN=CITY_LIST2;
    var url=URL.createObjectURL(
        new Blob([
            new Uint8Array([0xEF,0xBB,0xBF])
            ,"var CITY_LIST_PINYIN="
            ,JSON.stringify(CITY_LIST2,null,"\t")
        ]
        ,{"type":"text/plain"})
    );
    var downA=document.createElement("A");
    downA.innerHTML="下載查詢好城市的文件";
    downA.href=url;
    downA.download="data-pinyin.txt";
    document.body.appendChild(downA);
    downA.click();
};

var RunPinYin=function(){
    RunPinYin.T1=Date.now();
    QueryPinYin(ViewDown);
};


//立即執行代碼
if(window.CITY_LIST){
    if(!PageToken){
        PageToken=prompt("Token");
    };
    RunPinYin();
}else{
    console.error("data.txt未輸入");
};

這時候會提示輸入token,把剛纔找到的token粘貼進去,然後就開始工作了:

還挺快的,2分鐘多點全部翻譯完成。

3. 格式化成CSV

數據全部有了,導出成比較正常使用的格式,CSV最好了。這個導出比較簡單,任意網頁控制台把第二部保存的文件打開,複製數據到任意網頁控制台,然後輸入以下代碼:

/*
格式並且輸出為csv

先載入數據
    控制台輸入data-pinyin.txt

導入資料庫:
    文件格式Unicode,文字為字元流
    檢查id重覆項,修正id
    轉入area_city
    增加港澳台、海外兩個省級
    檢查名稱重覆項,修正名稱
        select * from area_city where len(name)=1
        select pid,name,count(*) from area_city group by pid,name having COUNT(*)>1
*/

var FixTrim=function(name){
    return name.replace(/^\s+|\s+$/g,"");
};
function CSVName(name){
    return '"'+FixTrim(name).replace(/"/g,'""')+'"';
};

var CITY_CSV=["id,pid,deep,name,pinyin_prefix,pinyin,ext_id,ext_name"];
for(var i=0;i<CITY_LIST_PINYIN.length;i++){
    var o=CITY_LIST_PINYIN[i];
    var pf="";
    var pinyin=FixTrim(o.P).toLowerCase();
    var ps=pinyin.split(" ");
    for(var j=0;j<ps.length&&j<3;j++){
        pf+=ps[j].substr(0,j==0?2:1);
    };
    
    CITY_CSV.push(o.id+","+o.pid+","+o.deep+","+CSVName(o.name)
        +","+CSVName(pf)+","+CSVName(o.P)
        +","+CSVName(o.ext_id+"")+","+CSVName(o.ext_name));
};

var url=URL.createObjectURL(
    new Blob([
        new Uint8Array([0xEF,0xBB,0xBF])
        ,CITY_CSV.join("\n")
    ]
    ,{"type":"text/plain"})
);
var downA=document.createElement("A");
downA.innerHTML="下載查詢好城市的文件";
downA.href=url;
downA.download="ok_data.csv";
document.body.appendChild(downA);
downA.click();

OK,數據全部搞完:

數據問題

  1. id編號和國家統計局的編號基本一致,方便以後更新。

  2. id重覆項目前是沒有(已優化過了),不過以前採集後直接對統計局的編號進行簡單縮短後會有重覆現象(算是精度丟失)。

  3. 拼音首碼取的是第一個字前兩個字母和後兩個字首字母,意圖是讓第一個字相同名稱的儘量能排序在一起。排序1:黑龍江helj、湖北hub、湖南hun;排序2:湖北hb、黑龍江hlj、湖南hn,排序一勝出。

  4. 因為區名字是直接去掉市、區尾碼,存在那麼幾對名字變得完全一樣的,需要手動吧市區尾碼加上,不然會產生小問題。

  5. 最終數據已上傳了一份到CSDN,含所有代碼和本文檔:http://download.csdn.net/download/xiangyuecn/10226964


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 優點 1. 運行速度:對於很簡單的sql,存儲過程沒有什麼優勢。對於複雜的業務邏輯,因為在存儲過程創建的時候,資料庫已經對其進行了一次解析和優化。存儲過程一旦執行,在記憶體中就會保留一份這個存儲過程,這樣下次再執行同樣的存儲過程時,可以從記憶體中直接調用,所以執行速度會比普通sql快。 2. 減少網路傳 ...
  • select count(*) '總欄位數', ISNULL(ISNULL(sum(case when isnullable=0 then 1 end),null),null) as '非空欄位數' from syscolumns where id=object_id( 'EmpInfo') --空 ...
  • 原文地址: "一個輕量級的Android資料庫操作工具" 寫了一個輕量級的Android操作資料庫的ORM工具。方便Android定義資料庫,操作資料庫(增刪改查),資料庫更新,實現了Android對象與資料庫對象之間的映射。源碼地址: "輕量級Android操作資料庫ORM工具" 。可以直接gra ...
  • 1.把要導入成Mudle的項目修改成符合Library的格式 修改該項目中bulid.gradle文件中第一行代碼 把 修改為 然後,修改AndroidManifiest.xml文件中配置信息,此處主要是把原來配置的項目Style等配置以及MainActivity配置刪除,這樣處理是為了防止重覆。以 ...
  • Gradle是一個基於Apache Ant和Apache Maven概念的 項目自動化構建工具 。 它使用一種基於Groovy的特定領域語言(DSL)來聲明項目配置,真正起作用的是Plugin,Gradle預設提供了許多常用的Plugin,如構建Java項目的Plugin、War、Ear等。 Gra ...
  • 小程式支持打開移動應用到底是怎麼回事?什麼APP都可以打開麽? ...
  • DreamweaverCS6安裝與破解 一、背景介紹:同學畢業分佈圖項目計劃簡介 哎哎哎,炸麽說呢,對於Web前端設計來說,純手撕html部分代碼實在是難受。 對於想做地圖這類的就“必須”用這個老工具啦: 0、準備把高中畢業同學分佈圖做一下。之前看到的大多數只是一張圖,實在太LOW,太沒實用性,太不 ...
  • 相信你在寫JavaScript代碼的時候也碰到過很頭疼的時刻,比如對象的屬性名或方法名大小寫錯了,或是記不得某個對象有沒有某個屬性,害怕最新的ES標準有的瀏覽器還沒有支持...等等種種問題,那麼你需要使用TypeScript,你會愛上她的(●'◡'●)。 TypeScript是JavaScript的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...