zoukankan      html  css  js  c++  java
  • 支持断点续爬的腾讯街景数据抓取

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

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

      

      

  • 相关阅读:
    Assetbundle资源单一打包,以及加载方法
    VS2010 Chromium编译
    一道思考题
    Windbg源码调试
    C++ static_cast dynamic_cast reinterpret_cast const_cast转换
    条件断点设置
    FFmpeg 2.0编译配置
    error LNK2001: 无法解析的外部符号 _IID_IDirectDraw7
    Windbg常用命令
    DDraw绘图
  • 原文地址:https://www.cnblogs.com/giserjobs/p/11990766.html
Copyright © 2011-2022 走看看