由人工坐标拾取延伸的代码编写和思考

1-起因

在3月下旬,领导突然组织人力登记各省市的收费站坐标。文档共9000+条数据,不仅要确认收费站名称,所在省市区,还要在规定网站查询其对应的经纬度并粘贴下来。这任务砸在谁头上都是一座大山,手头有空闲的我,就成了愚公移山的一员。

2-确定任务

拿到任务后我首先做的是查看查询网站的情况,其网址如下:https://lbs.qq.com/tool/getpoint/index.html
网站为腾讯位置服务,不需登录,即填即查,但是需要选定被查询收费站的所属城市,否则页面不显示坐标信息。

但在实际操作过程中我发现,在搜索框填入收费站名称时,虽然当前城市与其不相符,搜索下拉框还是会出现被搜索收费站的正确名称。这让人怀疑,后台已经搜索到了对应数据,但前台有相关限制导致其不显示。于是我使用fiddler和google开发者工具,打算看看它的后台构造。

使用开发者工具可看出,它在展示页面 lbs.qq.com/getpoint.html 后套了一个实际查询网址 apis.map.qq.com ,见图-嵌套查询网址,而这个网址搜索时会有两个请求分支suggestion和search,请求suggestion分支时,即使城市与收费站不相符,也会获得相应坐标。见图-城市与收费站不相符

嵌套查询网址.png
城市与收费站不相符.png

虽然使用google开发者工具已经有了结果,为了测试该查询网址是否可靠,我使用fiddler_composer对请求进行修改后反复尝试,确认了返回信息的可靠性。图中测试收费站为济南东收费站,城市为北京,可正确返回收费站坐标。见图-composer测试1、composer测试2

composer测试1.png
composer测试2.png

3-代码实现

在具体请求和响应已知的情况下,我决定使用python的requests库编写爬虫爬取收费站信息。

具体代码如下:

  import requests
  import json, re
  import xlrd,xlwt

  *导入excel数据*
  region = {'那曲'}
  wb = xlrd.open_workbook("C:\\Users\\why\\Desktop\\test_io.xlsx")
  sheet3 = wb.sheet_by_index(2)
  nrows = sheet3.nrows
  ncols = sheet3.ncols

  *创建导出excel文件*
  workbook = xlwt.Workbook(encoding = 'utf-8')
  worksheet = workbook.add_sheet("test Output",cell_overwrite_ok=True)
  *建立表头*
  worksheet.write(0,0,"待查询收费站")
  worksheet.write(0,1,"查询结果")
  worksheet.write(0,2,"地址")
  worksheet.write(0,3,"纬度")
  worksheet.write(0,4,"经度")

  *遍历收费站*
  *从excel文件中导入数据*
  for rows in range(1,nrows):
   value = sheet3.cell_value(rows,0)
   worksheet.write(rows, 0, value)
   key = value[:-1]+"收费站"
   print(key)

  *坐标查询网址的构建使用format*
   url_q = "https://apis.map.qq.com/ws/place/v1/suggestion/?keyword={0}&region=({1},0)&key=K76BZ-W3O2Q-RFL5S-GXOPR-3ARIT-6KFE5&output=jsonp&&callback=jQuery19109172698889312632_1586930162787&_=1586930162806".format(key,region)
   headers = {
      'Host': 'apis.map.qq.com',
      'Connection': 'keep-alive',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
      'Sec-Fetch-Dest': 'script',
      'Sec-Fetch-Site': 'same-site',
      'Sec-Fetch-Mode': 'no-cors',
      'Referer': 'https://lbs.qq.com/tool/getpoint/getpoint.html',
      'Cookie': 'mpuv=0dbf808f-b2b9-4a52-cc5c-7f02c7624209; pgv_pvid=8146180802; pgv_pvi=6788941824; pac_uid=0_5d4ce4f3a45e9; eas_sid=l185a6s62976r029G632j169B8; Qs_lvt_323937=1569721323; Qs_pv_323937=4182702269183565000; _ga=GA1.2.1124364354.1569721323; ptcz=1a2f9854c35dd273dbe54d8349cceedee3d4fb18c41cb1efa48538c355478cff; RK=JYr0uKKMGI; tvfe_boss_uuid=128c48d3412095ed; manshenqi=BASE64cXFAQF4oaHR0cDovL2FjXC5xcVwuY29tL0NvbWljVmlldy8oaW5kZXh8Y2hhcHRlcikvaWQvKFswLTldKykvY2lkLyhbMC05XSspKChcP3wjKS4qKT8pJEBAXihodHRwOi8vYWNcLnFxXC5jb20vQ29taWMvQ29taWNJbmZvL2lkLyhbMC05XSspKFw/LiopPykk; XWINDEXGREY=0'
    }

  *使用session使cookie保持有效*
   browser = requests.session()
  *进行get请求*
   res = browser.get(url=url_q, headers=headers)

   jsonp = res.text
  *print("获得的jsonp数据格式是:",type(jsonp))*

  *使用函数将jsonp格式数据转换为json格式,方便处理*
   def loads_jsonp(_jsonp):
     """解析jsonp数据为json格式"""
     try:
       return json.loads(re.match(".*?({.*}).*",_jsonp,re.S).group(1))
     except:
       raise ValueError('Invaild Input')

   js = loads_jsonp(jsonp) * 将jsonp数据变为dict数据*

   if 'data' in js :
    list_js = js['data']   *获取收费站数据*
    if list_js:   * python中{},[],(),都等价于False*
      *仅获取查询排名第一的收费站名称、地址、纬度、经度*
     print(list_js[0]["title"], ",", list_js[0]["address"], ",", list_js[0]["location"]['lat'], ",", list_js[0]["location"]['lng'])
      *导出至excel文件:名称、地址、纬度、经度*
     worksheet.write(rows, 1, list_js[0]["title"])
     worksheet.write(rows, 2, list_js[0]["address"])
     worksheet.write(rows, 3, list_js[0]["location"]['lat'])
     worksheet.write(rows, 4, list_js[0]["location"]['lng'])
    else:
      *在当前数据行的第二列打印Not Found*
       worksheet.write(rows,1,"Not Found")

  *将数据导出至excel文件*
  workbook.save("C:\\Users\\why\\Desktop\\test_out.xls")
  print("查询结束")

4、代码解析

4-1 HTTP请求

因为网站经过fiddler抓包后可看到raw的http请求,其中的url,header和相关参数可以直接拿到,这种情况下,我使用python的requests库进行请求编写,因为请求中带有cookie,为保证在程序运行时cookie均有效,我使用了requests的session方法。

其中需要注意的是,该坐标查询网址是由多个参数拼接而成,在查询过程中有两个参数需要变化,一个是keyword(在本次任务中即收费站名称),一个是region(在本次任务中即查询城市)。

keyword可以从xls文件中直接导入,因为待查询的名称没有“收费”两个字,为了提高查询的正确率,我用了简单的字符串截取和添加,将“站”替换为了“收费站”。

region则可以直接固定,尝试后发现,如果在本身已有收费站的城市进行非该城市收费站搜索,搜索结果有一定几率受该城市收费站的影响,因此最后选定没有收费站的西藏“那曲”作为region固定值。

4-2 返回结果处理

在get请求发送后,得到的响应是jsonp格式,例如callbackFunction(["customername1","customername2"])

这个格式我之前没有接触,但网上搜索方案后发现,可以通过去掉首尾的括号,将其变为可处理的数据。因此我使用 网上搜索的loads_jsonp函数将数据进行转换,最终转换为dict格式。

因为最终返回的数据过多且有嵌套,我先取出返回坐标所在的data列表,再从data列表中取出排名第一的数据字典,最终取得相关key的value,导出到xls文件

4-3 xls文件导入导出

因为查询数据所在的文件格式为xls,最终导出也希望保存为xls格式,因此我使用xlrd和xlwt对数据进行导入导出。

具体代码比较简单,都是表明对象使用读/写方法,因为是自己使用,对表格格式没有要求,只是在最初添加了表头,使用的也是简单写方法。
这部分改进为for循环
表头建立代码如下

 li = ["待查询收费站","查询结果","地址","纬度","经度"]
 for i in range(len(li)):
     worksheet.write(0,i,li[i])

数据导出代码如下

 lit = [list_js[0]["title"],list_js[0]["address"],list_js[0]["location"]['lat'],list_js[0]["location"]['lng']]
     for j in range(len(lit)):
         worksheet.write(rows,j+1,lit[j])

5、最终结果

写了这么多,最重要的就是结果。我跑了2313条数据,覆盖9个省的收费站,最终查询数据分析统计见图-查询结果分析

查询结果分析.png

图中可看出,数据平均正确率在77.76%,对于之前低效的人工查询是很有帮助的。

5-1 问题分析

有些省份(如四川),其正确率低至26.82%,需要进行具体分析。

通过观察四川的原查询数据,可看出他的收费站名称均较长见图-四川省原查询数据,除了必要的省份外,还有附加的市区名或道路名,这使系统在查询时大概率返回多个结果,而在代码中,我限制其只输出排名第一的结果,没有对数据进一步验证,因此导致了大量的不符合数据输出。

四川省原查询数据.png

修改一:原查询数据修改

目前就该问题对代码进行简单,将导入的收费站名称进行字符串截取,保留省份名和收费站名。例如“四川二绕东三星堆站”,保留“四川”和“三星堆”,将“站”替换为“收费站”。

代码如下:

 for rows in range(1,nrows):
  value = sheet3.cell_value(rows,0)
  worksheet.write(rows, 0, value)
  key = value[0:2]+value[-4:-1]+"收费站"
 或key = value[0:2]+value[-3:-1]+"收费站"

因为一般收费站名称长度为2-3个中文字符,所以我截取了倒数2位和3位,但这样也会出现查询错误。

对之前四川错误数据进行测试,将两次返回内容总计,数据量423,正确率84.40%,四川省正确率至88.58%

统计结果见图-错误数据分析

虽然已有进步,但两次查询后再统计还是非常麻烦,于是继续修改代码

修改二:返回数据处理方式

因为之前的返回数据处理中,我只是简单的把排名第一的数据取出,并没有对其进行正确性验证。因此再次修改时,希望从验证返回数据入手,先验证返回数据中是否存在正确数据,再将正确的数据导出。

代码如下

    for num in range(len(list_js)):
    * 获得每一条查询数据的名称*
    title = list_js[num]["title"]
    print(title)
    * 验证返回数据是否包含原查询数据一致,一致则导出,否则NF*
    if check in title:
        print(list_js[num]["title"], ",", list_js[num]["address"], ",", list_js[num]["location"]['lat'], ",",
              list_js[num]["location"]['lng'])
        lit = [list_js[num]["title"], list_js[num]["address"], list_js[num]["location"]['lat'],
               list_js[num]["location"]['lng']]
        for j in range(len(lit)):
            worksheet.write(rows, j + 1, lit[j])
        * 得到正确数据后强制跳出循环*
        break
    else:
        worksheet.write(rows,1,"Not Found")

仍然跑四川错误数据,数据量423,正确率78.96%,四川省正确率至84.60%。

错误数据分析.png

跑这段代码时我截取的是原查询数据倒数两位(即key = value[0:2]+value[-3:-1]+"收费站"),原因有二:一、在上述修改代码中,我发现取后两位的正确率更高;二、虽然后两位和后三位都取才能更好地提高正确率,但是我现在能力有限,如果两种都尝试查询,代码的执行效率不高,现在的办法只能是两种分开查询,再借助excel的选择性粘贴进行统计。

最终结果分析见图-结果分析(总)

结果分析(总).png

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • iOS网络编程读书笔记 Facade Tester客户端门面模式的实例(被动版本化) 被动版本化,所以硬编码URL...
    melouverrr阅读 1,602评论 3 7
  • 2系列200 OK请求已成功,请求所希望的响应头或数据体将随此响应返回。201 Created请求已经被实现,而且...
    Y像梦一样自由阅读 3,571评论 1 5
  • 许久没有这飘飞的思绪,也便无暇顾及这一天的记录。。。 最近陷入一个思维的循环里无法挣脱,而后在朋友圈才被突然的...
    星辰精灵阅读 97评论 0 1
  • 岁月无痕,人心斑驳,世事无常 我们踏着时光的船 穿梭在熙熙攘攘的人海 一路走来 一边认识一些人 一边离开一些人 从...
    缘_6d91阅读 157评论 1 1
  • 我对减负是这样理解的:上大学上好学校拿文凭并不是唯一的出路了。即使拿清华北大文凭也没有多少成功率。追求美好生活光靠...
    尾田粉丝汤阅读 188评论 0 0