支持断点续爬的腾讯街景数据抓取

  之前介绍了街景数据抓取的核心思想,采用画格网的方式查询街景数据是否存在。

  该方法在数据抓取过程漫长一次难以完全抓取数据信息,且按照格网查询街景时由于查询接口是按半径进行搜索难免出现重复街景的现象。为克服以上两个难题,本文采用断点续爬解决爬虫中断后需从头开始的问题,采用将街景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)
所有源码

  

  

原文地址:https://www.cnblogs.com/giserjobs/p/11990766.html