偉大的國(guó)家統(tǒng)計(jì)局為我們提供了省市區(qū)的信息[1],可惜不是json格式的方灾,并不能真正用起來(lái)牛哺。為了得到我們所需要格式化的數(shù)據(jù),筆者開發(fā)了一個(gè)將txt格式的省市區(qū)轉(zhuǎn)化為json格式的python腳本议忽。(結(jié)尾有小驚喜哦 _ )
操作系統(tǒng):ubuntu mate[2],python版本:python3.4十减,編輯器:pluma栈幸。
國(guó)家統(tǒng)計(jì)局網(wǎng)站上的省市區(qū)信息愤估,代碼中都沒(méi)有層級(jí)結(jié)構(gòu),用BeautifulSoup[3]解析也是浪費(fèi)時(shí)間速址,筆者直接把它復(fù)制粘貼到了city.txt中玩焰。
下面開始開發(fā)腳本:
定義模型
省市區(qū)本身就是三個(gè)現(xiàn)成的對(duì)象,不難想到:
# province of china
class Province:
def __init__(self, code, name):
self.code = code
self.name = name
# city of province
class City:
def __init__(self, code, name):
self.code = code
self.name = name
# coutry of city
class Country:
def __init__(self, code, name):
self.code = code
self.name = name
對(duì)于省市而言芍锚,它們還有各自的下屬行政區(qū)昔园,所以要加上一個(gè)sub屬性來(lái)把它們的下屬囊括進(jìn)來(lái)。
class Province:
def __init__(self, code, name):
# ...
self.sub = []
class City:
def __init__(self, code, name):
# ...
self.sub = []
這樣并炮,我們的省市區(qū)模型就算初步定義完成了默刚。
創(chuàng)建方法
有了模型還不夠,只有加上合適的方法渣触,才能顯示出面向?qū)ο蟮膬?yōu)勢(shì)羡棵。
我們需要一個(gè)返回對(duì)象的方法 __repr__()
,這是一個(gè)常見的方法嗅钻,只要我們提供對(duì)象的名稱皂冰,就能返回必要的對(duì)象信息,便于開發(fā)和調(diào)試养篓。
class Province:
# ...
def __repr__(self):
return '''{"code":%r ,"name": %r, "sub":%r}\n''' % (self.code, self.name, self.sub)
class City:
# ...
def __repr__(self):
return '''{"code":%r ,"name": %r, "sub":%r}\n''' % (self.code, self.name, self.sub)
class Country:
# ...
def __repr__(self):
return '''{"code":%r, "name": %r}\n''' % (self.code, self.name)
當(dāng)所有的省級(jí)行政區(qū)構(gòu)建完成時(shí)秃流,輸出這個(gè)對(duì)象的字符串形式就是最終的json數(shù)據(jù)。為了保證json的格式柳弄,我們的 __repr__()
方法是自定義的舶胀,輸出的結(jié)果既沒(méi)有類的名稱,也沒(méi)有 用<
和 >
括起來(lái)碧注。
為了構(gòu)建一個(gè)完整的省市級(jí)行政區(qū)嚣伐,我們還需要一種歸屬的方法,將下屬市和區(qū)加到sub數(shù)組里萍丐⌒耍可以用數(shù)組的 append()
方法來(lái)實(shí)現(xiàn):
class Province:
# ...
def consist(self, city):
self.sub.append(city)
class City:
# ...
def consist(self, country):
self.sub.append(country)
此外,還需要一個(gè)函數(shù)來(lái)判斷行政區(qū)的等級(jí)逝变。簡(jiǎn)單觀察一下city.txt基茵,就不難找到這個(gè)規(guī)律。
所有的省級(jí)行政區(qū)的代碼結(jié)尾都是
0000
壳影,所有的地級(jí)市的代碼結(jié)尾都是00
拱层,其它都是區(qū)縣級(jí)的行政區(qū)劃。
舉個(gè)栗子:江蘇的代碼是:320000宴咧,南京的代碼是:320100根灯,秦淮區(qū)的代碼是:320104。
所以,筆者寫了一個(gè)根據(jù)結(jié)尾的0來(lái)判定級(jí)別的方法:
# judge the type by code
def zero(n):
i = 1
if n <= 0:
return 0
while n % i == 0:
i *= 10
return i/10
解析文本
接下來(lái)箱吕,就按行讀取city.txt中的數(shù)據(jù)芥驳,簡(jiǎn)單粗暴見效快。
def get_data(filename):
arr = []
# open the city.txt
with open(filename, "r", encoding="utf-8") as read:
print("file has been open.")
for line in read:
arr.append(line.strip())
print("reading finished")
return arr
這里唯一值得注意的是:city.txt的編碼是什么茬高,打開文件指定的編碼就是什么。這里的編碼是:utf-8假抄。
輸出json
還是要觀察city.txt怎栽,除了省級(jí)行政區(qū)是頂行的,其它的都有數(shù)量不等的空格宿饱。get_data()
方法得到的實(shí)際上是一個(gè)數(shù)組熏瞄,其中的元素是類似這樣110100 市轄區(qū)
。從中我們也可以看到空格的存在谬以∏恳空格的utf-8編碼是 \u3000
,使用 strip("\u3000")
來(lái)除去首尾的空格为黎。然后將數(shù)字保存為code邮丰,有效字符保存為name。然后根據(jù) zero()
方法返回的結(jié)果構(gòu)建相應(yīng)的對(duì)象铭乾。最后將省級(jí)的對(duì)象輸出就得到了省市區(qū)json數(shù)據(jù)剪廉。
最后,揭曉這個(gè)小驚喜:源代碼和抓取到的json數(shù)據(jù)可以到我的 github 上獲取炕檩。鏈接:https://github.com/zhancongc/city_parse
附錄
- 統(tǒng)計(jì)局網(wǎng)址:http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201703/t20170310_1471429.html
- 本程序在ubuntu mate下運(yùn)行正常斗蒋,輸出正常;window 10下運(yùn)行笛质,輸出的中文會(huì)亂碼泉沾,原因尚不明確。
- Beautiful Soup 是一個(gè)可以從HTML或XML文件中提取數(shù)據(jù)的Python庫(kù)妇押。它能夠通過(guò)你喜歡的轉(zhuǎn)換器實(shí)現(xiàn)慣用的文檔導(dǎo)航跷究、查找、修改文檔的方式舆吮。