
于是乎,領(lǐng)導(dǎo)拍板了,更新!必須更新!
找遍各大網(wǎng)絡(luò)數(shù)據(jù),最新版的中國(guó)行政區(qū)劃圖(省市縣級(jí)),最新版更新到2015年,嗯,還是不知道是否真的2015。然后,爬遍了城墻內(nèi)外,萬(wàn)能寶一份2017的數(shù)據(jù)100+,地理空間數(shù)據(jù)云的,一份縣級(jí)中國(guó)區(qū)劃2014版,報(bào)價(jià)十幾萬(wàn)……
鑒于真實(shí)的2020數(shù)據(jù),大概率小貧民是不可獲得的(涉密),只能從開(kāi)源數(shù)據(jù)上想想辦法了。
鑒于之前爬遍全網(wǎng)的經(jīng)歷,目光直接鎖定OpenStreetMap的數(shù)據(jù),開(kāi)源、免費(fèi)、并且經(jīng)過(guò)驗(yàn)證,矢量邊界比手中現(xiàn)有的行政區(qū)劃更加精細(xì)。缺點(diǎn)則是涉及到領(lǐng)土有爭(zhēng)議的部分領(lǐng)域,OSM是站在對(duì)立面的,童鞋們慎重??!
來(lái)吧,話不多說(shuō),上干貨!
1. 技術(shù)路線介紹
根據(jù)之前掌握的信息,OSM官方是提供矢量圖形下載的。大概是我使用的導(dǎo)出方式不對(duì),一直沒(méi)能下下來(lái)。

經(jīng)過(guò)多方找了GeoJSON中的OpenStreetMap管理邊界
以及提取OSM行政邊界的方法, 確認(rèn)OSM當(dāng)前的行政邊界的下載的方式。整體路線大概是
1. 獲取所有行政區(qū)劃的relation code,
2. 通過(guò)CURL命令下載。(大概率需要科學(xué)上網(wǎng))
備案方法2:通過(guò)[http://polygons.openstreetmap.fr/?id=RELATIONID](http://polygons.openstreetmap.fr/?id=)
獲取到圖形自動(dòng)入庫(kù)。(此方法建議無(wú)法科學(xué)上網(wǎng)的童鞋們使用。)
2. 獲取關(guān)系編碼
通過(guò)觀察網(wǎng)頁(yè)中的相關(guān)信息,咱們可以快速的看到,每一個(gè)區(qū)域的編號(hào),以及與它接壤的外圍編號(hào),以及它的子一級(jí)編號(hào)

鑒于此,中國(guó)34個(gè)省市的關(guān)系網(wǎng)盡在掌握。
數(shù)據(jù)獲取邏輯如下:
- 獲取中國(guó)34個(gè)省市縣的關(guān)系網(wǎng);
- 向下查找每個(gè)省市對(duì)應(yīng)的市級(jí)關(guān)系編碼;
- 根據(jù)市級(jí)關(guān)系編碼,再向下查找縣級(jí)編碼。
基于此,便可獲取全中國(guó)34個(gè)省市縣區(qū)最新的shp矢量行政區(qū)劃了。
一個(gè)簡(jiǎn)單的爬蟲(chóng)小程序便應(yīng)運(yùn)而生。
package com.openstreetmap.reptile;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Node;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author
* @Date: 2020/1/16 13:55
* @Description: 用于處理爬蟲(chóng)邏輯
**/
@Slf4j
public class FileReptileCore {
public static void main(String[] args) {
try {
//34個(gè)省市縣的編碼
String code ="913011;553303;153314;911844;286937;2128285;912998;199073;407492;913106;913073;913012;913109;198590;912942;" +
"153269;913100;913006;913105;913068;913094;553302;153292;153310;286342;161349;" +
"913101;912940;912999;913067;913069;913110;1867188";
String[] codeArr = code.split(";");
Map<String,List<String>> cityLevelCode = new HashMap<>();
//基礎(chǔ)的訪問(wèn)網(wǎng)址
String baseUrl = "https://www.openstreetmap.org/relation/";
for (int i = 0; i < codeArr.length; i++) {
//輸出當(dāng)前正在爬取的省份編碼
log.info("===========開(kāi)始輸出:" + codeArr[i]);
String url = baseUrl + codeArr[i];
org.jsoup.nodes.Document doc = Jsoup.connect(url).ignoreContentType(true).timeout(30000).get();
//獲取節(jié)點(diǎn)數(shù)據(jù)
List<Node> relationNodeList = doc.body().childNodes().get(3).childNodes().get(1).childNodes().get(9).childNodes().get(3).childNodes()
.get(17).childNodes();
int size = relationNodeList.size();
List<String> cityCode = new ArrayList<>();
//循環(huán)讀取市級(jí)節(jié)點(diǎn)
for (int j = 0; j < size; j++) {
Node node = relationNodeList.get(j);
if(node.toString().equals(" ")){
continue;
}
String value = node.childNodes().get(1).attributes().get("href");
if(value.contains("relation")){
String relationCode = value.split("/")[2];
cityCode.add(relationCode);
System.out.println(relationCode);
}
}
cityLevelCode.put(codeArr[i],cityCode);
}
//根據(jù)市級(jí)節(jié)點(diǎn)的code,獲得縣級(jí)的code
for (Map.Entry province :cityLevelCode.entrySet()) {
List<String> cityCodeArr = (List<String>)province.getValue();
int cityCodeArrsize = cityCodeArr.size();
for (int a = 0; a < cityCodeArrsize; a++) {
log.info("===========開(kāi)始輸出:" + cityCodeArr.get(a));
String url = baseUrl + cityCodeArr.get(a);
org.jsoup.nodes.Document doc = Jsoup.connect(url).ignoreContentType(true).timeout(30000).get();
List<Node> relationNodeList = doc.body().childNodes().get(3).childNodes().get(1).childNodes().get(9).childNodes().get(3).childNodes()
.get(17).childNodes();
int size = relationNodeList.size();
for (int j = 0; j < size; j++) {
Node node = relationNodeList.get(j);
if(node.toString().equals(" ")){
continue;
}
String value = node.childNodes().get(1).attributes().get("href");
if(value.contains("relation")){
String relationCode = value.split("/")[2];
System.out.println(relationCode);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
基于此,所有省市縣的編碼獲取完畢。
3. 根據(jù)編碼下載shp
根據(jù)這位大大的文章OSM行政邊界的方法, 使用CURL命令即可
curl -f -o file.zip --url
"https://wambachers-osm.website/boundaries/exportBoundaries?cliVersion=1.0&cliKey=52d97a3b-3fc1-44cd-bd9a-e8c1c9468bab&exportFormat=shp&exportLayout=levels&exportAreas=land&union=false&selected=3287346"
其中:
curl -f -o 代表下載操作;
file.zip: 表示下載的文件名;
--url: 代表將此url下面的文件下載下來(lái)
整個(gè)鏈接:https://wambachers-osm.website/boundaries/exportBoundaries?cliVersion=1.0&cliKey=52d97a3b-3fc1-44cd-bd9a-e8c1c9468bab&exportFormat=shp&exportLayout=levels&exportAreas=land&union=false&selected=3287346中,需要修改的部分便是327346,這個(gè)就是上文獲取的code。多文件下載時(shí),僅需將多個(gè)code使用 , 隔開(kāi)即可
比如:
curl -f -o file.zip --url
"https://wambachers-osm.website/boundaries/exportBoundaries?cliVersion=1.0&cliKey=52d97a3b-3fc1-44cd-bd9a-e8c1c9468bab&exportFormat=shp&exportLayout=levels&exportAreas=land&union=false&selected=3287346,2963917
等等……
備注
至于備選方法,只要拿到了關(guān)系代碼,也是一個(gè)簡(jiǎn)單查詢命令便可轉(zhuǎn)存入庫(kù)。在此便不贅述。
關(guān)于上文介紹的數(shù)據(jù),如有需要,有償私信哈~Q或者郵箱是(344326924@qq.com)