支持斷點續爬的騰訊街景數據抓取


  之前介紹了街景數據抓取的核心思想,采用畫格網的方式查詢街景數據是否存在。

  該方法在數據抓取過程漫長一次難以完全抓取數據信息,且按照格網查詢街景時由於查詢接口是按半徑進行搜索難免出現重復街景的現象。為克服以上兩個難題,本文采用斷點續爬解決爬蟲中斷后需從頭開始的問題,采用將街景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)
所有源碼

 

 

  

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM