使用Python和Perl绘制北京跑步地图
当你在一个城市,穿越大街小巷,跑步跑了几千公里之后,一个显而易见的想法是,如果能把在这个城市的所有路线全部画出来,会是怎样的景象呢?
文章代码比较多,为了不吊人胃口,先看看最终效果,上到北七家,下到南三环,西到大望路,东到首都机场。二环32公里,三环50公里,这是极限,四环先暂时不考虑了。。。。
(本文工程已经托管在Github,https://github.com/ferventdesert/gpx-crawler)
1.数据来源:益动GPS
首先需要原始位置信息,手机上有众多跑步软件,但它们共同的问题是不允许自由导入导出(可能是为了防止用户脱离吧)。因此有一块智能运动手表应该是不二之选。我的是Garmin Fenix3,推荐一下:
与此同时,益动GPS算是业界良心了,能够同步咕咚,Garmin手表,悦跑圈的数据,因此我将其作为一个入口,抓取所有的GPS数据。
至于如何同步,可参考网站上的相关介绍,下面是我登录该网站后的截图:
http://edooon.com/user/5699607196/record/15414378
随便点进去以后,就可以看到导出路线的按钮:
无比坑爹的是,它不提供批量导出的按钮,几百条记录,依次导出都累死了。于是考虑用代码来自动化吧。
2. 获取益动网站上的数据
登录之后,可以看出它是动态加载,当滚轮滚到最下时,自动加载后面的内容。本来是应该嗅探和分析http请求的。后来我懒惰了,采取折中方案,拖到底,全部加载完毕后,保存了当前的html文件。
接下来就是解析这个Html,基本上是通过XPath的来做的。有经验的同学看了下图就都明白了:
图中高亮的部分,就是要下载gpx文件的实际地址。我们将其保存在urllist中。同时,元数据被保存在json文件里。
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='';
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的请求,然后保存文件即可,非常容易。
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提供了丰富的接口,当然为了统计,我们只需要提取一部分数据:
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,并将一个字典返回。并得到类似下面的一张表:
因为我们只需要绘制北京的区域,因此需要一个坐标表达式筛掉北京之外的地区。筛选代码使用了pandas,在附件里有更详细的代码。
exceptids=详细[(详细.lng<116.1)|(详细.lng>116.7)|(详细.lat<39.9)|(详细.lat>40.1)].id
def filtercity(r):
sp=r.split('/')[-1].split('.')
if sp[1]!='gpx':
return False;
if sp[0] in exceptids.values:
return False;
return True;
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
如果没有问题的话,应该是下面这样:
我假设各位读者对bash都已经很熟悉了,更多的需求可以查看runme.sh。
最后得到的结果如下图:
当时看到这个结果,我都惊呆了!这是自己跑了2000公里左右的结果,北京三环内(主要集中在长安街以北)主要的道路都跑遍了,朝阳公园,天坛公园,尤其北三环和北土城路(10号线北段)被我各种虐。每一段白线都是一段故事,每一个点都是我的一个脚印啊!
5.总结
这文章写得显然不够详细,远远没有hand by hand。而且并没有提供更多的数据分析(显然这些工作我都做了)不过相信跑步的程序员一定都很厉害,我这就权作抛砖引玉了。
其实完全可以做成一个web服务,跑友们上传自己的跑步软件的id,就可以自动渲染出各种漂亮的跑步路径和分析图,应该会很有意义吧!
这件事情花费了我七八个小时,简直吐血,大量的时间用在了如何安装GD上,而不是下载数据上。教训告诉我,一定要读安装包里自带的说明文档,因为库和库之间的版本不同,因此可能造成版本地狱,到时候新版本卸载不了,老版本没法用的时候可别说我没提醒啊!
值得一提的是,益动gps下载的gpx文件不带换行符,这导致gpx_disualization库无法解析它(这货正则表达式写错了),我懒得再去动perl正则,于是通过替换增加了换行符。
GD还需要libpng等一众perl库,在附件里都有提供下载。
附件是GD库和爬取所有gpx数据的python3代码。
使用Python和Perl绘制北京跑步地图的更多相关文章
- Python 绘制全球疫情地图
国内疫情得到控制后,我就没怎么再关心过疫情,最近看到一条新闻,全球疫情累计确诊人数已经突破 500w 大关,看到这个数字我还是有点吃惊的. 思来想去,还是写一篇全球疫情的分析的文章,本文包括网络爬虫. ...
- 配置Notepad++直接运行Python、Perl、C、C++、Java
运行(F5),输入命令并保存 cmd /k python "$(FULL_CURRENT_PATH)" & ECHO. & PAUSE & EXIT cmd ...
- canves绘制北京地铁线路图,包括线路绘制,优先路线,单路径选择。
canves绘制北京地铁线路图,包括线路绘制,优先路线,单路径选择. 即将推出,后台涵盖各种语言,php,C#,java,nodejs等.
- Python使用plotly绘制数据图表的方法
转载:http://www.jb51.net/article/118936.htm 本篇文章主要介绍了Python使用plotly绘制数据图表的方法,实例分析了plotly绘制的技巧. 导语:使用 p ...
- Python入门-散点图绘制
Python入门-散点图绘制 废话不说 直接上代码 import matplotlib.pyplot as plt x_values = list(range(1,1001)) y_values = ...
- JSP,PHP,Python,Ruby,Perl概要及各自特点
JSP,PHP,Python,Ruby,Perl概要及各自特点 博客分类: JSP PHP Python Ruby Perl概要及各自特点 javascript 互联网技术日新月异,编程的语言层出不 ...
- Python——使用matplotlib绘制柱状图
Python——使用matplotlib绘制柱状图 1.基本柱状图 首先要安装matplotlib(http://matplotlib.org/api/pyplot_api.htm ...
- Makefile中怎样调用python和perl文件为自己提供须要的数据
Makefile中怎样调用python和perl文件为自己提供须要的数据,利用print函数对外输出数据 实例代码例如以下 perl.pl #!/usr/bin/perl print("he ...
- 014 Python基本图形绘制小结
目录 一.Python基本语法元素 1.1 温度转换 二.Python基本图形绘制 2.1 Python蟒蛇绘制 一.Python基本语法元素 缩进.注释.命名.变量.保留字 数据类型.字符串. 整数 ...
随机推荐
- 【Postgresql】数据库函数
1.Postgresql查询前几条记录的SQL语句 select * from table where ...... LIMIT N ; 2.SQL limit integer offset int ...
- C# EXCEL导入 混合列文字为空,找不到可安装的 ISAM的解决办法
C# EXCEL导入 混合列文字为空,找不到可安装的 ISAM的解决办法 使用C#导入 Excel数据到 DataTable,如果连接串中只写 Excel 8.0,则正常的字符列,数值列都没有问题,但 ...
- pagination 分页
<!DOCTYPE html> <html> <head> <title>pagination</title> <style type ...
- GiuHub 使用
一 Mac 能不能连接安卓手机 1 USB数据线 设置 > 通用 > 开发人员选项 > USB调试 > 选择"相机PTP模式" 连接后,手机中的照片和视 ...
- 关于ScrollView嵌套ListView问题
Android开发之ScrollView中嵌套ListView的解决方案 原文:http://blog.csdn.net/minimicall/article/details/40983331 ...
- Oracle like查询
查询方式:LIKE '%xx%' 普通: SELECT * FROM TABLE T WHERE T.COLUNM LIKE '%xx%' 优化:使用 INSTR SELECT * FROM TABL ...
- CSS基础篇之了解CSS和它的基本属性
CSS是什么? CSS英文全名是Cascading Style Sheets翻译过来就是层叠样式表,它主是把网页表现与内容分离的一种样式设计语言.这种语言能优化我们编程,把东西简化和优化写法,而且不同 ...
- IOS网络第七天WebView-02WebView和网页的交互2,删除大众点评多余文字,加上蒙版进度
************ #import "HMViewController.h" @interface HMViewController () <UIWebViewDele ...
- Java基础系列——IO流
---恢复内容开始--- Java对数据的操作都是通过流的方式,数据的输入和输出是相对内存来说的,将外设的数据读到内存:输入流:将内存的数据写到外设:输出流. 流按操作数据分为两种:字节流,字符流. ...
- JPEG文件结构
JPEG文件由八个部分组成,每个部分的标记字节为两个,首字节固定为:0xFF,当然,准许在其前面再填充多个0xFF,以最后一个为准.下面为各部分的名称和第二个标记字节的数值,用ultraedit的16 ...