本篇博文為博主(whgiser)原創,轉載請注明。
城市公交、地鐵數據反映了城市的公共交通,研究該數據可以挖掘城市的交通結構、路網規划、公交選址等。但是,這類數據往往掌握在特定部門中,很難獲取。互聯網地圖上有大量的信息,包含公交、地鐵等數據,解析其數據反饋方式,可以通過Python爬蟲采集。閑言少敘,接下來將詳細介紹如何使用Python爬蟲爬取城市公交、地鐵站點和數據。
首先,爬取研究城市的所有公交和地鐵線路名稱,即XX路,地鐵X號線。可以通過圖吧公交、公交網、8684、本地寶等網站獲取,該類網站提供了按數字和字母划分類別的公交線路名稱。Python寫個簡單的爬蟲就能采集,可參看WenWu_Both的文章,博主詳細介紹了如何利用python爬取8684上某城市所有的公交站點數據。該博主采集了站點詳細的信息,包括,但是缺少了公交站點的坐標、公交線路坐標數據。這就讓人抓狂了,沒有空間坐標怎么落圖,怎么分析,所以,本文重點介紹的是站點坐標、線路的獲取。
以圖吧公交為例,點擊某一公交后,出現該路公交的詳細站點信息和地圖信息。博主頓感興奮,覺得馬上就要成功了,各種抓包,發現並不能解析。可能博主技術所限,如有大神能從中抓到站點和線路的坐標信息,請不吝賜教。這TM就讓人絕望了啊,到嘴的肥肉吃不了。
天無絕人之路,嘗試找找某地圖的API,發現可以調用,通過解析,能夠找到該數據的后台地址。熟悉前端的可以試試,博主前端也就只會個hello world,不獻丑了。這是一種思路,實踐證明是可以的。
地圖API可以,那么通過地圖抓包呢?打開某圖主頁,直接輸入某市公交名稱,通過抓包,成功找到站點和線路信息。具體抓包信息如下圖所示,busline_list中詳細列出了站點和線路的信息,其中有兩條,是同一趟公交不同方向的數據,略有差別,需注意。找到入口過后,接下來爬蟲就要大顯身手了。
主要爬取代碼如下,其實也很簡單,主函數如下。首先需要構建傳入的參數,主要的包括路線名稱,城市編碼,地理范圍,縮放尺度。地理范圍可以通過坐標拾取器獲取,參數經url編碼后,發送請求,判斷返回數據是否符合要求(注:可能該線路地圖上停運或不存在,也可能是訪問速度過快,反爬蟲機制需要人工驗證,博主爬取的時候碰到過,所以后面設置了隨機休眠)。接下來,就是解析json數據了。代碼中的extratStations和extractLine,就是提取需要的字段,怎么樣,是不是很簡單。最后,就是保存了,站點和路線分別存儲。
1 def main(): 2 df = pd.read_excel("線路名稱.xlsx",) 3 BaseUrl = "https://ditu.amap.com/service/poiInfo?query_type=TQUERY&pagesize=20&pagenum=1&qii=true&cluster_state=5&need_utd=true&utd_sceneid=1000&div=PC1000&addr_poi_merge=true&is_classify=true&" 4 for bus in df[u"線路"]: 5 params = { 6 'keywords':'11路', 7 'zoom': '11', 8 'city':'610100', 9 'geoobj':'107.623|33.696|109.817|34.745' 10 } 11 print(bus) 12 paramMerge = urllib.parse.urlencode(params) 13 #print(paramMerge) 14 targetUrl = BaseUrl + paramMerge 15 stationFile = "./busStation/" + bus + ".csv" 16 lineFile = "./busLine/" + bus + ".csv" 17 18 req = urllib.request.Request(targetUrl) 19 res = urllib.request.urlopen(req) 20 content = res.read() 21 jsonData = json.loads(content) 22 if (jsonData["data"]["message"]) and jsonData["data"]["busline_list"]: 23 busList = jsonData["data"]["busline_list"] ##busline 列表 24 busListSlt = busList[0] ## busList共包含兩條線,方向不同的同一趟公交,任選一趟爬取 25 26 busStations = extratStations(busListSlt) 27 busLine = extractLine(busListSlt) 28 writeStation(busStations, stationFile) 29 writeLine(busLine, lineFile) 30 31 sleep(random.random() * random.randint(0,7) + random.randint(0,5)) #設置隨機休眠 32 else: 33 continue
附上博主的解析函數:
1 def extratStations(busListSlt): 2 busName = busListSlt["name"] 3 stationSet = [] 4 stations = busListSlt["stations"] 5 for bs in stations: 6 tmp = [] 7 tmp.append(bs["station_id"]) 8 tmp.append(busName) 9 tmp.append(bs["name"]) 10 cor = bs["xy_coords"].split(";") 11 tmp.append(cor[0]) 12 tmp.append(cor[1]) 13 wgs84cor1 = gcj02towgs84(float(cor[0]),float(cor[1])) 14 tmp.append(wgs84cor1[0]) 15 tmp.append(wgs84cor1[1]) 16 stationSet.append(tmp) 17 return stationSet 18 19 def extractLine(busListSlt): 20 ## busList共包含兩條線,備注名稱 21 keyName = busListSlt["key_name"] 22 busName = busListSlt["name"] 23 fromName = busListSlt["front_name"] 24 toName = busListSlt["terminal_name"] 25 lineSet = [] 26 Xstr = busListSlt["xs"] 27 Ystr = busListSlt["ys"] 28 Xset = Xstr.split(",") 29 Yset = Ystr.split(",") 30 length = len(Xset) 31 for i in range(length): 32 tmp = [] 33 tmp.append(keyName) 34 tmp.append(busName) 35 tmp.append(fromName) 36 tmp.append(toName) 37 tmp.append(Xset[i]) 38 tmp.append(Yset[i]) 39 wgs84cor2 = gcj02towgs84(float(Xset[i]),float(Yset[i])) 40 tmp.append(wgs84cor2[0]) 41 tmp.append(wgs84cor2[1]) 42 lineSet.append(tmp) 43 return lineSet
爬蟲采集原始數據如下:
以下是某一條公交站點和線路的處理后的數據展示。由於不同的地圖商采用不同的坐標系,會有不同程度的偏差,需要坐標糾偏。下一步,博主將詳細介紹如何批量將這些站點和坐標進行坐標糾正和矢量化。