共享经济的浪潮正在席卷各行各业,出行行业是这股浪潮的主要分支。如今,共享单车在城市随处可见,为人们的生活出行带来了便利。相信大家总会遇到这样的困境。APP里可以看到很多自行车,但是到了那里却发现自行车没有了。有些车藏在某处;有些汽车可能在高楼后面,但由于GPS错误而无法找到;那么有没有办法通过获取这些单车的数据来分析这些车是否已经变成了僵尸车呢?是不是有人故意把它放在社区里让人无法获取?带着这些问题,笔者开始研究如何获取这些数据。01从哪里获取数据如果你能看到数据,那么我们总有办法自动获取数据。只不过获取数据的方式决定了获取数据的效率。对于摩拜单车的数据分析任务,爬虫必须能够在短时间内(通常10分钟左右)获取到更多的数据,这对数据分析很有用。那么数据从何而来?最直接的来源是摩拜单车应用程序。现代软件设计讲究前后端分离,服务器会同时为APP、网页等服务。在这种趋势下,我们只需要搞清楚软件的HTTP请求就可以了。一般来说,有一些工具可以提供帮助:直接捕获数据包:Wireshark(在路由器或计算机上)SharkforRoot(Android)使用代理捕获和调试HTTP请求:Fiddler4CharlesPacketCapture(Android)自从我的手机没有Root,在路由器上抓包干扰太大,不好搞https。所以只能先尝试使用Fiddler或者Charles。挂掉Fiddler的代理,然后在手机上不停的移动位置,看看有没有新的请求。但遗憾的是,好像请求的都是高德地图,并没有摩拜单车相关的数据。怎么了?在手机上试试。换成抓包后,确实有流量。在request中找到了我最关心的:这个APIrequest一看就明白了。在postman中试了一下,可以正确返回信息。看来是你了!现在高兴还为时过早。连续爬了几天数据,分析数据,发现摩拜的GPS好像一直在跳动,有时跳动可以超过几公里的距离,这显然不是正常值。难不成是他们的接口操作了,返回了假数据?我观察过,即使在APP里,单车返回的数据也是跳动的。一天一大早到第二天早上,我时不时刷新一下我家附近的车,看看是不是真的这样。我找不到图片,但经过观察,我断定APP中返回的位置确实有问题。一辆车放在很远的地方,消失了一会儿,一会儿又回来了,跟我抓到的数据吻合。而且,这个抖动跟手机、手机号、甚至是移动运营商都没有关系。这说明抖动是摩拜单车界面的问题,也可以解释为什么有时候没有车却有车。这是之前发在朋友圈的视频截图。可以看到在营地入口附近有一个尖刺,这里其实是停着车的,但是GPS轨迹显示它在短时间内向附近移动,甚至向远方移动,然后返回到车。到那个位置。这样的数据对于数据分析根本没用,差点放弃。随着微信小程序的火爆,摩拜单车也在第一时间推出了小程序。看到就笑了,不错,又一个数据源给我来了,试试看。使用PacketCapture捕获一次数据后,很容易确定API。爬完之后,我爬了两三天数据,发现有个拐点,数据符合自行车的正常轨迹。剩下的就是提高爬虫的效率了。02其他尝试有时候直接分析APP源码找到API入口,反编译摩拜的安卓APP,发现除了一些有用的资源文件外,其他文件都被奇虎用了360的混淆器打包。网上有分析如何解包的文章,不过我没有太多时间深究,算了。摩拜单车的API之所以容易抓包分析,很大程度是因为API设计简单:只使用HTTP请求,抓包分析简单。在这些API中,没有对请求进行加密,使得他们的服务很容易被其他人使用。此外,微信小程序也是API泄露的重要来源。毕竟APP中的请求可以通过nativecode加密后再发送出去,但是小程序中好像没有这个功能。有兴趣的可以尝试看看小蓝单车APP的请求。他们使用https来请求和加密数据请求。捕获他们的数据将非常困难。当然,如果摩拜官方不关心数据,这样的API设计也是可以的。免责声明:本爬虫仅供学习研究之用,请勿用于非法用途。由此产生的任何法律纠纷由其自行承担。03目录结构\analysis-用于数据分析的jupyter\influx-importer-导入到influxdb,之前没做好\modules-代理模块\web-实时图形显示模块,刚学习react,看效果这里crawler.py-爬虫核心代码importToDb.py-导入postgres数据库分析sql.sql-建表sqlstart.sh- 连续运行脚本04思路核心代码放在crawler.py中,数据先存入sqlite3数据库,去重后导出为csv文件,节省空间。摩拜的API返回的是一个正方形区域的单车,我只要把这个区域一块块移动就可以抓到整个大区域的数据。左、上、右、下定义了抓拍范围,目前为成都绕城高速以南至南湖的方形区域。offset定义了爬行的间隔。现在以0.002为基准,在DigitalOcean5$服务器上15分钟可以爬取一次。defstart(self):left=30.7828453209top=103.9213455517right=30.4781772402bottom=104.2178123382offset=0.002ifos.path.isfile(self.db_name):os.remove(self.db_name)try:withsqlite3.dbconnect_name(asc.:c.execute('''CREATETABLEMobike(TimeDATETIME,bikeIdsVARCHAR(12),bikeTypeTINYINT,distIdINTEGER,distNumTINYINT,typeTINYINT,xDOUBLE,yDOUBLE)''')exceptExceptionasex:pass然后启动250个线程,因为你要问我为什么不使用协程,哼哼~~当时没学过~~~其实是可以的,可能效率更高吧。由于爬取后需要对数据进行去重,为了剔除小方块区域之间重复的部分,最好的group_data正是这样做的。executor=ThreadPoolExecutor(max_workers=250)print("Start")self.total=0lat_range=np.arange(左,右,-offset)forlatinlat_range:lon_range=np.arange(top,bottom,offset)forloninlon_range:self.total+=1executor.submit(self.get_nearby_bikes,(lat,lon))executor.shutdown()self.group_data()核心API代码在这里。小程序的API接口,几个变量就够了,非常简单。defget_nearby_bikes(self,args):try:url="https://mwx.mobike.com/mobike-api/rent/nearbyBikesInfo.do"payload="latitude=%s&longitude=%s&errMsg=getMapCenterLocation"%(args[0],args[1])headers={'charset':"utf-8",'platform':"4","referer":"https://servicewechat.com/wx40f112341ae33edb/1/",'content-type':"application/x-www-form-urlencoded",'user-agent':"MicroMessenger/6.5.4.1000NetType/WIFILanguage/zh_CN",'host':"mwx.mobike.com",'connection':"Keep-Alive",'accept-encoding':"gzip",'cache-control':"no-cache"}self.request(headers,payload,args,url)exceptExceptionasex:print(ex)***你你可能想问频繁抢IP不封了吗?其实摩拜单车是有IP访问限速的,但是破解的方法很简单,就是使用大量的代理。我有一个代理池,基本上每天有8000多个代理。直接在ProxyProvider中获取代理池,并提供pick功能,随机选择得分前50的代理。请注意,我的代理池每小时更新一次,但代码中提供的jsonblob代理列表只是一个示例,大部分应该会在一段时间后失效。这里使用代理评分机制。我没有直接随机选择agent,而是根据agent的分数对agent进行排序。每一次成功的请求都会加分,而错误的请求会扣分。这样可以在短时间内选出速度和质量最好的代理。如有必要,您可以保存它以备将来使用。classProxyProvider:def__init__(self,min_proxies=200):self._bad_proxies={}self._minProxies=min_proxiesself.lock=threading.RLock()self.get_list()defget_list(self):logger.debug("Gettingproxylist")r=requests.get("https://jsonblob.com/31bf2dc8-00e6-11e7-a0ba-e39b7fdbe78b",timeout=10)proxies=ujson.decode(r.text)logger.debug("Got%sproxies",len(proxies))self._proxies=list(map(lambdap:Proxy(p),proxies))defpick(self):withself.lock:self._proxies.sort(key=lambdap:p.score,reverse=True)proxy_len=len(self._proxies)max_range=50ifproxy_len>50elseproxy_lenproxy=self._proxies[random.randrange(1,max_range)]proxy.used()returnproxy实际使用时,通过proxyProvider.pick()选择代理,然后使用。如果代理有问题,直接使用proxy.fatal_error()来降低分数,这样以后就不会再选择代理了。defrequest(self,headers,payload,args,url):whileTrue:proxy=self.proxyProvider.pick()try:response=requests.request("POST",url,data=payload,headers=headers,proxies={"https":proxy.url},timeout=5,verify=False)withself.lock:withsqlite3.connect(self.db_name)asc:try:print(response.text)decoded=ujson.decode(response.text)['object']self.done+=1forxindecoded:c.execute("INSERTINTMObikeVALUES(%d,'%s',%d,%d,%s,%s,%f,%f)"%(int(time.time())*1000,x['bikeIds'],int(x['biketype']),int(x['distId']),x['distNum'],x['type'],x['distX'],x['distY']))timespend=datetime.datetime.now()-self.start_timepercent=self.done/self.totaltotal=timespend/percentprint(args,self.done,percent*100,self.done/timespend.total_seconds()*60,total,total-timespend)exceptExceptionasex:print(ex)breakexceptExceptionasex:proxy.fatal_error()抓取摩拜单车的数据,进行大数据分析。以下数据分析来自1月19日全天数据,涵盖成都周边及华阳(天府新区)周边地区。成都摩拜单车总体情况如下:05标准版和精简版数量基本持平,精简版单车份额在增加。(1是标准自行车,2是精简版)06大约30%的自行车还没有搬走。数据分析显示,有30%的单车根本没有动过,说明这些单车可能被放置在人迹罕至或偏远的地方。公民素质仍有待提高。07主要行驶距离小于3公里。数据分析显示,3公里以内的出行距离占比达87.2%,这也与共享单车的定位非常吻合。100米以下的距离也占用了大量的数据,但是100米以下的数据被认为是GPS的波动,所以排除。出行距离分布08骑行次数多在5次以下,自行车使用频率越高,共享效果越好。摩拜数据显示,在移动自行车中,5次以下的约占60%。不过一次性和二次单车也占据了30%左右的份额,说明摩拜单车的使用率并不是很高。单车骑行次数骑行次数09从单车看城市发展从摩拜单车热图分布来看,成都逐渐呈现“双核”发展态势,城市新中心天府新区正在聚集??更多人和机会。双核开发的原老城区拥有大量自行车。在老城区,热图显示东城区自行车较多,这可能与这里的商业(春熙路、太古里、万达)和人口密集的社区有直接关系。连接。成都南部的老城区和天府新区开发越来越有活力,商业区和居住区界限分明。夜间,大量单车聚集在华阳、世纪城、中和,而上班时间,大量单车聚集在软件园附近。晚上的软件园白天的软件园
