使用Python和Perl绘制北京跑步地图
admin
2023-07-31 00:46:40
0

当你在一个城市,穿越大街小巷,跑步跑了几千公里之后,一个显而易见的想法是,如果能把在这个城市的所有路线全部画出来,会是怎样的景象呢?

文章代码比较多,为了不吊人胃口,先看看最终效果,上到北七家,下到南三环,西到大望路,东到首都机场。二环32公里,三环50公里,这是极限,四环先暂时不考虑了。。。。

(本文工程已经托管在Github,https://github.com/ferventdesert/gpx-crawler)

1.数据来源:益动GPS

首先需要原始位置信息,手机上有众多跑步软件,但它们共同的问题是不允许自由导入导出(可能是为了防止用户脱离吧)。因此有一块智能运动手表应该是不二之选。我的是Garmin Fenix3,推荐一下:

image

与此同时,益动GPS算是业界良心了,能够同步咕咚,Garmin手表,悦跑圈的数据,因此我将其作为一个入口,抓取所有的GPS数据。

至于如何同步,可参考网站上的相关介绍,下面是我登录该网站后的截图:

http://edooon.com/user/5699607196/record/15414378

image

随便点进去以后,就可以看到导出路线的按钮:

image

无比坑爹的是,它不提供批量导出的按钮,几百条记录,依次导出都累死了。于是考虑用代码来自动化吧。

2. 获取益动网站上的数据

登录之后,可以看出它是动态加载,当滚轮滚到最下时,自动加载后面的内容。本来是应该嗅探和分析http请求的。后来我懒惰了,采取折中方案,拖到底,全部加载完毕后,保存了当前的html文件。

接下来就是解析这个Html,基本上是通过XPath的来做的。有经验的同学看了下图就都明白了:

image

图中高亮的部分,就是要下载gpx文件的实际地址。我们将其保存在urllist中。同时,元数据被保存在json文件里。

12345678910111213141516171819202122232425262728 folder = u\’D:/buptzym的同步盘/百度云/我的文档/数据分析/datasets/rungps/\’;cookie=\’JSESSIONID=69DF607B71B1F14AFEC090F520B14B55; logincookie=5699607196$6098898D08E533587E82B33DD9D02196; persistent_cookie=5699607196$42C885AD38F59DCA407E09C95BE1A60B; uname_forloginform=\”buptzym@qq.com\”; __utma=54733311.82935663.1447906150.1447937410.1456907433.7; __utmb=54733311.5.10.1456907433; __utmc=54733311; __utmz=54733311.1456907433.7.3.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; cookie_site=auto\’userid=\’5699607196\’;f = codecs.open(folder + \’desert.htm\’, \’r\’, \’utf-8\’);html = f.read();f.close();root = etree.HTML(html)tree = etree.ElementTree(root); listnode=tree.xpath(\’//*[@id=\”feedList\”]\’);numre=re.compile(u\’骑行|跑步|公里|,|耗时|消耗|大卡\’);urllists=[]records=[];for child in listnode[0].iterchildren():    record={};    temp=child.xpath(\’div[2]/div[1]/a[2]\’)    if len(temp)==0:        continue;    source= temp[0].attrib[\’href\’];    record[\’id\’]=source.split(\’/\’)[1];    info=temp[0].text;    numinfo= numre.split(info);    if len(numinfo)<6:        continue;    record[\’type\’]= info[0:2];    record[\’distance\’]= numinfo[1];    record[\’hot\’]=numinfo[6];    urllists.append(\’http://edooon.com/user/%s/record/export?type=gpx&id=%s\’ % (userid, record[\’id\’]));

值得注意的是,因为下载时需要cookie,因此读者需要将自己在益动GPS的userid和登录的cookie都替换掉(这种网站不值得为它开发自动登录)。

接下来就是下载的过程,获取导出数据按钮的URL的XPath,构造一个带cookie的请求,然后保存文件即可,非常容易。

1234567891011121314151617181920212223 opener = urllib.request.build_opener()opener.addheaders.append((\’Cookie\’, cookie));path=\’//*[@id=\”exportList\”]/li[1]/a\’;for everyURL in urllists:    id = everyURL.split(\’=\’)[1];    print(id);    url=\’http://edooon.com/user/%s/record/%s\’ % (userid, id);    f = opener.open(url);    html = f.read();    f.close();    root = etree.HTML(html)    tree = etree.ElementTree(root);    fs = str(tree.xpath(path)[0]);    if fs is None:        continue;    furl = \’http://edooon.com/user/%s/record/%s\’ % (userid, fs);    f = opener.open(furl);    html = f.read();    f.close();    filename=folder+\’id\’+\’.gpx\’;    xmlfile = codecs.open(filename, \’wb\’);    xmlfile.write(html);    xmlfile.close();

之后,我们便保存了大约300多个gpx文件。

3. 解析gpx数据

所谓gpx数据,是一种通用规范的GPS数据格式,详细的资料可自行搜索。

我们需要使用python的gpx解析器, gpxpy是个好选择,使用

pip3 install gpxpy 即可安装。

gpxpy提供了丰富的接口,当然为了统计,我们只需要提取一部分数据:

1234567891011121314151617181920 def readgpx(x):         file= open(dir+x+\’.gpx\’,\’r\’)    txt=file.read()    gpx=gpxpy.parse(txt)    mv=gpx.get_moving_data()    dat= {\’移动时间\’:mv.moving_time,\’静止时间\’:mv.stopped_time,\’移动距离\’:mv.moving_distance,\’暂停距离\’:mv.stopped_distance,\’最大速度\’:mv.max_speed};    dat[\’总时间\’]=(gpx.get_duration())    dat[\’id\’]=str(x)    updown=gpx.get_uphill_downhill()    dat[\’上山\’]=(updown.uphill);    dat[\’下山\’]=(updown.downhill)    timebound=gpx.get_time_bounds();    dat[\’开始时间\’]=(timebound.start_time)    dat[\’结束时间\’]=(timebound.end_time)    p=gpx.get_points_data()[0]    dat[\’lat\’]=p.point.latitude    dat[\’lng\’]=p.point.longitude    file.close()    return dat

readgpx函数会读取文件名x,并将一个字典返回。并得到类似下面的一张表:

image

因为我们只需要绘制北京的区域,因此需要一个坐标表达式筛掉北京之外的地区。筛选代码使用了pandas,在附件里有更详细的代码。

exceptids=详细[(详细.lng116.7)|(详细.lat40.1)].id

1234567 def filtercity(r):    sp=r.split(\’/\’)[1].split(\’.\’)    if sp[1]!=\’gpx\’:        return False;    if sp[0] in exceptids.values:        return False;    return True;

1 bjids= [r for r in gpxs if filtercity(r)]

这样,我们就将所有在北京完成的运动数据筛选了出来。

4.绘制GPS数据

反复造轮子是不好玩的,绘制gpx已经有比较强大的库,地址在http://avtanski.net/projects/gps/

很不幸,这个库使用Perl作为开发语言,并使用了GD作为视觉渲染库。我花费了大量的时间,在安装GD上面。

Ubuntu默认安装Perl, GD是需要libgd的,libgd却在官网上极难下载,下载后却又发现版本不对,这让我在国外互联网上遨游了好几个小时,都要死掉了。。。到最后,我才发现,安装libgd库只要下面这一步就可以了:

apt-get install libgd-gd2-perl

我觉得这就是apt-get方式坑爹的地方,apt get gd 或者libgd根本找不到,如果不去查,谁知道这么写啊! 至于Perl的CPan管理工具,哎,不说了都是泪。

接下来下载gd 2.56,解压之后,

perl ./Makefile.PL

make

make install

即可

这份gpx绘制库是这么介绍自己的:

This folder contains several Perl scripts for processing and plottin  GPS track data in .GPX format.

当然我们不废话,把所有的gpx数据拷贝到sample_gpx文件夹下,然后华丽丽的运行

./runme.sh

如果没有问题的话,应该是下面这样:

287060-20160302192028705-255980492

我假设各位读者对bash都已经很熟悉了,更多的需求可以查看runme.sh。

最后得到的结果如下图:

287060-20160302192030001-1802697907

当时看到这个结果,我都惊呆了!这是自己跑了2000公里左右的结果,北京三环内(主要集中在长安街以北)主要的道路都跑遍了,朝阳公园,天坛公园,尤其北三环和北土城路(10号线北段)被我各种虐。每一段白线都是一段故事,每一个点都是我的一个脚印啊!

5.总结

这文章写得显然不够详细,远远没有hand by hand。而且并没有提供更多的数据分析(显然这些工作我都做了)不过相信跑步的程序员一定都很厉害,我这就权作抛砖引玉了。

其实完全可以做成一个web服务,跑友们上传自己的跑步软件的id,就可以自动渲染出各种漂亮的跑步路径和分析图,应该会很有意义吧!

这件事情花费了我七八个小时,简直吐血,大量的时间用在了如何安装GD上,而不是下载数据上。教训告诉我,一定要读安装包里自带的说明文档,因为库和库之间的版本不同,因此可能造成版本地狱,到时候新版本卸载不了,老版本没法用的时候可别说我没提醒啊!

值得一提的是,益动gps下载的gpx文件不带换行符,这导致gpx_disualization库无法解析它(这货正则表达式写错了),我懒得再去动perl正则,于是通过替换增加了换行符。

GD还需要libpng等一众perl库,在附件里都有提供下载。

附件是GD库和爬取所有gpx数据的python3代码。


相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
python绘图库Matplo... 本文简单介绍了Python绘图库Matplotlib的安装,简介如下: matplotlib是pyt...
Prometheus+Graf... 一,Prometheus概述 1,什么是Prometheus?Prometheus是最初在Sound...