之前介紹了街景數據抓取的核心思想,采用畫格網的方式查詢街景數據是否存在。
該方法在數據抓取過程漫長一次難以完全抓取數據信息,且按照格網查詢街景時由於查詢接口是按半徑進行搜索難免出現重復街景的現象。為克服以上兩個難題,本文采用斷點續爬解決爬蟲中斷后需從頭開始的問題,采用將街景ID存入mysql數據庫進行街景去重,極大的提高了工作效率。
1.數據來源
之前街景數據的抓取采用的是騰訊官網的接口,實用性不強,多數情況下請求數據不成功,且需要配置key等一系列操作。本文將采用城市吧街景數據,發送請求獲取街景id,根據id獲取全景圖。
傳入經緯度獲取街景id的接口: (key可不傳參)
https://sv.map.qq.com/xf?lat=23.129577531346&lng=113.33253497386&r=1000&key=ba56844b2a30311fe2149b485ca2a031&output=jsonp&pf=jsapi&ref=jsapi&cb=qq.maps._svcb3.cbk3sqht1y3
根據id獲取街景的接口:
http://sv1.map.qq.com/thumb?svid=10061047140101161900600&x=0&y=0&from=web&level=0&size=0
街景全景圖:
2. 斷點續爬
核心代碼說明:利用logo.txt記錄上次坐標格網的索引,判斷是否需要斷點續爬,需要的話則會計算原有記錄索引值與默認值差值,利用索引差值動態修正索引。值得注意的是,索引值雖被修正但循環次數依舊不變,需要防止索引超限。

1 # 斷點續爬 (初始化) 2 filePath='logo.txt' 3 indexArr=read_file(filePath) 4 goFlag='flase' 5 jinFlag='flase' 6 weiFlag='flase' 7 if len(indexArr)!=2: 8 print('無需進行斷點續爬') 9 else: 10 print('准備進行斷點續爬!!!') 11 goFlag='true' 12 jins_c=0 13 weis_c=0 14 for jins_i in range(jins_num): 15 if goFlag=='true': 16 if len(indexArr)==2: 17 jins_c=int(indexArr[0])-jins_i# 索引差值 18 if jinFlag!='true': 19 jins_i=jins_c+jins_i# 修正經度索引 20 if jins_i>jins_num-jins_c:# 保證索引不越界 21 jinFlag='true' 22 break 23 jin = round(jins[jins_i],3) 24 for weis_i in range(weis_num): 25 if goFlag=='true': 26 if len(indexArr)==2: 27 weis_c=int(indexArr[1])-weis_i# 索引差值 28 indexArr=[]# 清空記錄 29 if weiFlag!='true': 30 weis_i=weis_c+weis_i# 修正緯度索引 31 if weis_i>weis_num-weis_c:# 保證索引不越界 32 weiFlag='true' 33 break 34 wei = round(weis[weis_i],3) 35 # 斷點續爬 (記錄經緯度索引) 36 saveText(filePath,str(jins_i)+'_'+str(weis_i)) 37 # 這里要注意下,對應的經緯度沒有街景圖的地方,輸出的會是無效圖片 38 print(jin, wei) 39 img_name = "E:\\dataTest\\streetImgData\\"+cityName+"\\" + str(wei) + "_" + str(jin) +".jpg" 40 getPanoBylocation_(str(wei)+","+str(jin), img_name)
3. 街景ID去重
3.1引入自定義mysql類(源碼):
import MySql #自定義mysql類
# 連接數據庫
myCon=MySql.connect({'host':'','user':'','password':'','port':'','database':'','charset':''})
3.2數據匹配
核心代碼說明:先去數據庫查詢id是否存在,存在則不再下載圖片,否則下載圖片保存記錄。
# 查詢記錄是否存在,存在則無需再次下載
sql="SELECT * FROM streeImg WHERE id ='"+pano+"'" resData=MySql.query(myCon,sql) if len(resData)==0: # 插入記錄 sql = "INSERT INTO streeImg (id,name,lat,lng,detail) VALUES (%s, %s, %s, %s, %s)" val = (pano,name,float(lat) ,float(lng) ,text) MySql.add(myCon,sql,val) # 全景圖url url = "http://sv1.map.qq.com/thumb?svid="+pano+"&x=0&y=0&from=web&level=0&size=0" # 分級瓦片 download_(url, img_name,pano) else: print('圖片已下載完成,無需再下載%s'%(pano))
3.3數據庫結構
說明:先去數據庫表結構以及字段類型如下圖所示。
4.(源碼):

1 # coding=utf-8 2 import math 3 import json 4 import requests 5 import urllib 6 from urllib.request import urlopen 7 from optparse import OptionParser 8 # PIL Python Imaging Library 已經是Python平台事實上的圖像處理標准庫了。PIL功能非常強大,但API卻非常簡單易用 9 # 安裝步驟 1.cmd 2.進入python的安裝目錄中的Scripts目錄:3.輸入命令:pip install pillow -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 10 from PIL import Image 11 from io import BytesIO 12 import numpy as np 13 import MySql #自定義mysql類 14 15 # 連接數據庫 16 myCon=MySql.connect({'host':'','user':'','password':'','port':'','database':'','charset':''}) 17 18 def getPanoBylocation_(location,img_name): 19 # 將user_agent,referer寫入頭信息 20 headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36','Referer':'http://ditu.city8.com/gz/canyinfuwu/2716097_w3gd6'} 21 lat=location.split(',')[0] 22 lng=location.split(',')[1] 23 url='https://sv.map.qq.com/xf?lat='+lat+'&lng='+lng+'&r=1000&key=ba56844b2a30311fe2149b485ca2a031&output=jsonp&pf=jsapi&ref=jsapi&cb=qq.maps._svcb3.cbk3rdpp035' 24 text = requests.get(url, headers=headers).text 25 text=text.split("qq.maps._svcb3.cbk3rdpp035&&qq.maps._svcb3.cbk3rdpp035(")[1].split(")")[0] 26 jsonMess =json.loads(text) 27 28 if jsonMess['detail']: 29 pano=str(jsonMess['detail']['svid'])# 街景ID 30 name=jsonMess['detail']['road_name']# 道路名稱 31 print('查詢成功%s'%(pano)) 32 # 查詢記錄是否存在,存在則無需再次下載 33 sql="SELECT * FROM streeImg WHERE id ='"+pano+"'" 34 resData=MySql.query(myCon,sql) 35 if len(resData)==0: 36 # 插入記錄 37 sql = "INSERT INTO streeImg (id,name,lat,lng,detail) VALUES (%s, %s, %s, %s, %s)" 38 val = (pano,name,float(lat) ,float(lng) ,text) 39 MySql.add(myCon,sql,val) 40 # 全景圖url 41 url = "http://sv1.map.qq.com/thumb?svid="+pano+"&x=0&y=0&from=web&level=0&size=0" 42 # 分級瓦片 43 download_(url, img_name,pano) 44 else: 45 print('圖片已下載完成,無需再下載%s'%(pano)) 46 47 #發送請求保存照片 48 def download_(url, name,pano): 49 # 將user_agent,referer寫入頭信息 50 headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36','Referer':'http://ditu.city8.com/gz/canyinfuwu/2716097_w3gd6'} 51 images = requests.get(url, headers=headers) 52 53 if images.status_code == 200: 54 try:# 請求街景數據失敗 55 jsonMess=images.json() 56 print('請求圖片失敗 原因:%s'%(jsonMess)) 57 except json.JSONDecodeError:# json 編碼異常捕獲(街景請求成功) 58 img = images.content 59 print('圖片: %s%s 正在下載..' % ('panoID ',pano)) 60 image = Image.open(BytesIO(img)) 61 image.save(r'' + name ) 62 # 63 64 # wgs84轉高德 65 def wgs84togcj02(lng, lat): 66 PI = 3.1415926535897932384626 67 ee = 0.00669342162296594323 68 a = 6378245.0 69 dlat = transformlat(lng - 105.0, lat - 35.0) 70 dlng = transformlng(lng - 105.0, lat - 35.0) 71 radlat = lat / 180.0 * PI 72 magic = math.sin(radlat) 73 magic = 1 - ee * magic * magic 74 sqrtmagic = math.sqrt(magic) 75 dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI) 76 dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI) 77 mglat = lat + dlat 78 mglng = lng + dlng 79 return [mglng, mglat] 80 # GCJ02/谷歌、高德 轉換為 WGS84 gcj02towgs84 81 def gcj02towgs84(localStr): 82 lng = float(localStr.split(',')[0]) 83 lat = float(localStr.split(',')[1]) 84 PI = 3.1415926535897932384626 85 ee = 0.00669342162296594323 86 a = 6378245.0 87 dlat = transformlat(lng - 105.0, lat - 35.0) 88 dlng = transformlng(lng - 105.0, lat - 35.0) 89 radlat = lat / 180.0 * PI 90 magic = math.sin(radlat) 91 magic = 1 - ee * magic * magic 92 sqrtmagic = math.sqrt(magic) 93 dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI) 94 dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI) 95 mglat = lat + dlat 96 mglng = lng + dlng 97 return str(lng * 2 - mglng) + ',' + str(lat * 2 - mglat) 98 def transformlat(lng, lat): 99 PI = 3.1415926535897932384626 100 ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * \ 101 lat + 0.1 * lng * lat + 0.2 * math.sqrt(abs(lng)) 102 ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 * 103 math.sin(2.0 * lng * PI)) * 2.0 / 3.0 104 ret += (20.0 * math.sin(lat * PI) + 40.0 * 105 math.sin(lat / 3.0 * PI)) * 2.0 / 3.0 106 ret += (160.0 * math.sin(lat / 12.0 * PI) + 320 * 107 math.sin(lat * PI / 30.0)) * 2.0 / 3.0 108 return ret 109 def transformlng(lng, lat): 110 PI = 3.1415926535897932384626 111 ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \ 112 0.1 * lng * lat + 0.1 * math.sqrt(abs(lng)) 113 ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 * 114 math.sin(2.0 * lng * PI)) * 2.0 / 3.0 115 ret += (20.0 * math.sin(lng * PI) + 40.0 * 116 math.sin(lng / 3.0 * PI)) * 2.0 / 3.0 117 ret += (150.0 * math.sin(lng / 12.0 * PI) + 300.0 * 118 math.sin(lng / 30.0 * PI)) * 2.0 / 3.0 119 return ret 120 121 #獲取經緯坐標 122 def getPoint(_points): 123 point = _points.split(',') 124 point_jin = point[0] 125 point_wei = point[1] 126 transOpints=wgs84togcj02(float(point_jin),float(point_wei)) 127 return transOpints 128 129 # 以txt文件格式存儲 130 def saveText(filePath,str_): 131 fo = open(filePath, "w+")# 打開一個文件 132 fo.write(str_); #內容寫入 133 fo.write('\n') 134 fo.close()# 關閉打開的文件 135 # 讀取txt內容 136 def read_file(filePath): 137 try: 138 with open(filePath, 'r')as f: 139 LIST = f.readlines() 140 for line in LIST: 141 return line.split('_') 142 except FileNotFoundError: 143 return [] 144 145 # 輸入左下以及右上角坐標 根據兩點形成等差坐標組 進而獲取圖片 146 def getImage(start_point,end_point,cityName): 147 # 取得起始坐標 148 start_point_jin = start_point[0] 149 start_point_wei = start_point[1] 150 end_point_jin = end_point[0] 151 end_point_wei = end_point[1] 152 #創建等差數組 153 jins = np.arange(float(start_point_jin)*1000, float(end_point_jin)*1000, 1)*0.001 154 jins_num = len(jins) 155 weis = np.linspace(float(start_point_wei)*1000, float(end_point_wei)*1000, jins_num)*0.001 156 weis_num = len(weis) 157 # 斷點續爬 (初始化) 158 filePath='logo.txt' 159 indexArr=read_file(filePath) 160 goFlag='flase' 161 jinFlag='flase' 162 weiFlag='flase' 163 if len(indexArr)!=2: 164 print('無需進行斷點續爬') 165 else: 166 print('准備進行斷點續爬!!!') 167 goFlag='true' 168 jins_c=0 169 weis_c=0 170 for jins_i in range(jins_num): 171 if goFlag=='true': 172 if len(indexArr)==2: 173 jins_c=int(indexArr[0])-jins_i# 索引差值 174 if jinFlag!='true': 175 jins_i=jins_c+jins_i# 修正經度索引 176 if jins_i>jins_num-jins_c:# 保證索引不越界 177 jinFlag='true' 178 break 179 jin = round(jins[jins_i],3) 180 for weis_i in range(weis_num): 181 if goFlag=='true': 182 if len(indexArr)==2: 183 weis_c=int(indexArr[1])-weis_i# 索引差值 184 indexArr=[]# 清空記錄 185 if weiFlag!='true': 186 weis_i=weis_c+weis_i# 修正緯度索引 187 if weis_i>weis_num-weis_c:# 保證索引不越界 188 weiFlag='true' 189 break 190 wei = round(weis[weis_i],3) 191 # 斷點續爬 (記錄經緯度索引) 192 saveText(filePath,str(jins_i)+'_'+str(weis_i)) 193 # 這里要注意下,對應的經緯度沒有街景圖的地方,輸出的會是無效圖片 194 print(jin, wei) 195 img_name = "E:\\dataTest\\streetImgData\\"+cityName+"\\" + str(wei) + "_" + str(jin) +".jpg" 196 getPanoBylocation_(str(wei)+","+str(jin), img_name) 197 198 #定義數據字典 根據起始點坐標推算內容坐標 199 cityJinweiArr=[{"start":"115.442845,39.464988","end":"117.498766,40.978318","city":"beiJing"},{"start":"112.681398,34.269097","end":"114.226897,34.958295","city":"zhengZhou"},{"start":"113.692462,29.971956","end":"115.082138,31.362241","city":"wuHan"}] 200 for city in cityJinweiArr: 201 start_point=getPoint(city['start']) 202 end_point=getPoint(city['end']) 203 cityName=city['city'] 204 getImage(start_point,end_point,cityName)