欧美中文字幕第一页-欧美中文字幕一区-欧美中文字幕一区二区三区-欧美中文字幕在线-欧美中文字幕在线播放-欧美中文字幕在线视频

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

我是創始人李巖:很抱歉!給自己產品做個廣告,點擊進來看看。  

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

APP中能看到很多單車,但走到那里的時候,才發現車并不在那里。有些車不知道藏到了哪里;有些車或許是在高樓的后面,由于有GPS的誤差而找不到了;有些車被放到了小區里面,一墻之隔讓騎車人無法獲得到車。

那么有沒有一個辦法通過獲得這些單車的數據,來分析這些車是否變成了僵尸車?是否有人故意放到小區里面讓人無法獲取呢?

帶著這些問題,我開始了研究如何獲取這些數據。

從哪里獲得數據

如果你能夠看到數據,那么我們總有辦法自動化的獲取到這些數據。只不過獲取數據的方式方法決定了獲取數據的效率。

對于摩拜單車的數據分析這個任務而言,這個爬蟲要能夠在短時間內(通常是10分鐘左右)獲取到更多的數據,對于數據分析才有用處。那么數據來源于哪里?

最直接的來源是摩拜單車的APP。現代的軟件設計都講究前后端分離,而且服務端會同時服務于APP、網頁等。在這種趨勢下我們只需要搞清楚軟件的HTTP請求就好了。一般而言有以下一些工具可以幫忙:

直接抓包:

  • Wireshark (在路由器或者電腦)
  • Shark for Root (Android)

用代理進行HTTP請求抓包及調試:

  • Fiddler 4
  • Charles
  • Packet Capture (Android)

由于我的手機沒有root,在路由器上抓包又太多的干擾,對于https也不好弄。所以只能首先采用Fiddler或者Charles的方式試試。

掛上Fiddler的代理,然后在手機端不停的移動位置,看有沒有新的請求。但遺憾的是似乎請求都是去拿高德地圖的,并沒有和摩拜車相關的數據。

那怎么一回事?試試手機端的。換成Packet Capture后果然就有流量了,在請求中找到了我最關心的那個:

這個API請求一看就很顯然了,在postman中試了一下能夠正確的返回信息,看來就是你了!

高興得太早。

連續爬了幾天的數據,將數據進行一分析,發現摩拜單車的GPS似乎一直在跳動,有時候跳動會超過幾公里的距離,顯然不是一個正常的值。

難道是他們的接口做了手腳返回的是假數據?我觀察到即便在APP中,單車返回的數據也有跳動。有某一天凌晨到第二天早上,我隔段時間刷新一下我家附近的車,看看是否真的如此。

圖片我找不到了,但是觀察后得出的結論是,APP中返回的位置確實有問題。有一臺車放在一個很偏僻的位置,一會兒就不見了,待會兒又回來了,和我抓下來的數據吻合。

而且這個跳動和手機、手機號、甚至移動運營商沒有關系,說明這個跳動是摩拜接口的問題,也可以從另一方面解釋為什么有時候看到車但其實那里沒有車。

這是之前發的一個朋友圈的視頻截圖,可以看到在營門口附近有一個尖,在那里其實車是停住的,但是GPS軌跡顯示短時間內在附近攢動,甚至攢動到很遠,又回到那個位置。

這樣的數據對于數據分析來講根本沒法用,我差點就放棄了。

隨著微信小程序的火爆,摩拜單車也在第一時間出了小程序。我一看就笑了,不錯,又給我來了一個數據源,試試。

用Packet Capture抓了一次數據后很容易確定API。抓取后爬取了兩三天的數據,發現出現了轉機,數據符合正常的單車的軌跡。

剩下事情,就是提高爬蟲的效率了。

其他嘗試

有時候直接分析APP的源代碼會很方便的找到API入口,將摩拜的Android端的APP進行反編譯,但發現里面除了一些資源文件有用外,其他的文件都是用奇虎360的混淆器加殼的。網上有文章分析如何進行脫殼,但我沒有太多時間去鉆研,也就算了。

摩拜單車的API之所以很容易抓取和分析,很大程度上來講是由于API設計的太簡陋:

僅使用http請求,使得很容易進行抓包分析
在這些API中都沒有對request進行一些加密,使得自己的服務很容易被人利用。
另外微信小程序也是泄露API的一個重要來源,畢竟在APP中request請求可以通過native代碼進行加密然后在發出,但在小程序中似乎還沒有這樣的功能。

如果大家有興趣,可以試著看一下小藍單車APP的request,他們使用https請求,對數據的request進行了加密,要抓取到他們的數據難度會增加非常多。

當然了,如果摩拜單車官方并不care數據的事情的話,這樣的API設計也是ok的。

聲明:

此爬蟲僅用于學習、研究用途,請不要用于非法用途。任何由此引發的法律糾紛自行負責。

沒耐心看文章的請直接:公眾號菜單欄回復“摩拜”獲取完整源碼

目錄結構

				\analysis - jupyter做數據分析
				\influx-importer - 導入到influxdb,但之前沒怎么弄好
				\modules - 代理模塊
				\web - 實時圖形化顯示模塊,當時只是為了學一下react而已,效果請見這里
				crawler.py - 爬蟲核心代碼
				importToDb.py - 導入到postgres數據庫中進行分析
				sql.sql - 創建表的sql
				start.sh - 持續運行的腳本
			

思路

核心代碼放在crawler.py中,數據首先存儲在sqlite3數據庫中,然后去重復后導出到csv文件中以節約空間。

摩拜單車的API返回的是一個正方形區域中的單車,我只要按照一塊一塊的區域移動就能抓取到整個大區域的數據。

left,top,right,bottom定義了抓取的范圍,目前是成都市繞城高速之內以及南至南湖的正方形區域。offset定義了抓取的間隔,現在以0.002為基準,在DigitalOcean 5$的服務器上能夠15分鐘內抓取一次。

				def start(self):
				left = 30.7828453209
				top = 103.9213455517
				right = 30.4781772402
				bottom = 104.2178123382
				offset = 0.002
				if os.path.isfile(self.db_name):
				os.remove(self.db_name)
				try:
				with sqlite3.connect(self.db_name) as c:
				c.execute('''CREATE TABLE mobike
				(Time DATETIME, bikeIds VARCHAR(12), bikeType TINYINT,distId INTEGER,distNum TINYINT, type TINYINT, x DOUBLE, y DOUBLE)''')
				except Exception as ex:
				pass
			

然后就啟動了250個線程,至于你要問我為什么沒有用協程,哼哼~~我當時沒學~~~其實是可以的,說不定效率更高。

由于抓取后需要對數據進行去重,以便消除小正方形區域之間重復的部分,最后的group_data正是做這個事情。

				executor = ThreadPoolExecutor(max_workers=250)
				print("Start")
				self.total = 0
				lat_range = np.arange(left, right, -offset)
				for lat in lat_range:
				lon_range = np.arange(top, bottom, offset)
				for lon in lon_range:
				self.total += 1
				executor.submit(self.get_nearby_bikes, (lat, lon))
				executor.shutdown()
				self.group_data()
			

最核心的API代碼在這里。小程序的API接口,搞幾個變量就可以了,十分簡單。

				def get_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.1000 NetType/WIFI Language/zh_CN",
				'host': "mwx.mobike.com",
				'connection': "Keep-Alive",
				'accept-encoding': "gzip",
				'cache-control': "no-cache"
				}
				self.request(headers, payload, args, url)
				except Exception as ex:
				print(ex)
			

最后你可能要問頻繁的抓取IP沒有被封么?其實摩拜單車是有IP的訪問速度限制的,只不過破解之道非常簡單,就是用大量的代理。

我是有一個代理池,每天基本上有8000以上的代理。在ProxyProvider中直接獲取到這個代理池然后提供一個pick函數用于隨機選取得分前50的代理。

請注意,我的代理池是每小時更新的,但是代碼中提供的jsonblob的代理列表僅僅是一個樣例,過段時間后應該大部分都作廢了。

在這里用到一個代理得分的機制。我并不是直接隨機選擇代理,而是將代理按照得分高低進行排序。每一次成功的請求將加分,而出錯的請求將減分。

這樣一會兒就能選出速度、質量最佳的代理。如果有需要還可以存下來下次繼續用。

				class ProxyProvider:
				def __init__(self, min_proxies=200):
				self._bad_proxies = {}
				self._minProxies = min_proxies
				self.lock = threading.RLock()
				self.get_list()
				def get_list(self):
				logger.debug("Getting proxy list")
				r = requests.get("https://jsonblob.com/31bf2dc8-00e6-11e7-a0ba-e39b7fdbe78b", timeout=10)
				proxies = ujson.decode(r.text)
				logger.debug("Got %s proxies", len(proxies))
				self._proxies = list(map(lambda p: Proxy(p), proxies))
				def pick(self):
				with self.lock:
				self._proxies.sort(key = lambda p: p.score, reverse=True)
				proxy_len = len(self._proxies)
				max_range = 50 if proxy_len > 50 else proxy_len
				proxy = self._proxies[random.randrange(1, max_range)]
				proxy.used()
				return proxy
			

在實際使用中,通過proxyProvider.pick()選擇代理,然后使用。如果代理出現任何問題,則直接用proxy.fatal_error()降低評分,這樣后續就不會選擇到這個代理了。

				def request(self, headers, payload, args, url):
				while True:
				proxy = self.proxyProvider.pick()
				try:
				response = requests.request(
				"POST", url, data=payload, headers=headers,
				proxies={"https": proxy.url},
				timeout=5,verify=False
				)
				with self.lock:
				with sqlite3.connect(self.db_name) as c:
				try:
				print(response.text)
				decoded = ujson.decode(response.text)['object']
				self.done += 1
				for x in decoded:
				c.execute("INSERT INTO mobike VALUES (%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_time
				percent = self.done / self.total
				total = timespend / percent
				print(args, self.done, percent * 100, self.done / timespend.total_seconds() * 60, total,
				total - timespend)
				except Exception as ex:
				print(ex)
				break
				except Exception as ex:
				proxy.fatal_error()
			

抓取了摩拜單車的數據并進行了大數據分析。以下數據分析自1月19日整日的數據,范圍成都繞城區域以及至華陽附近(天府新區)內。成都的摩拜單車的整體情況如下:

標準、Lite車型數量相當

摩拜單車在成都大約已經有6萬多輛車,兩種類型的車分別占有率為55%和44%,可見更為好騎的Lite版本的占有率在提高。(1為標準車,2為Lite車型)

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

三成左右的車沒有移動過

數據分析顯示,有三成的單車并沒有任何移動,這說明這些單車有可能被放在不可獲取或者偏僻地方。市民的素質還有待提高啊。

出行距離以3公里以下為主

數據分析顯示3公里以下的出行距離占據了87.2%,這也十分符合共享單車的定位。100米以下的距離也占據了大量的數據,但認為100米以下的數據為GPS的波動,所以予以排除。

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

出行距離分布

騎行次數以5次以下居多

單車的使用頻率越高共享的效果越好。從摩拜單車的數據看,在流動的單車中,5次以下占據了60%左右的出行。但1次、2次的也占據了30%左右的份額,說明摩拜單車的利用率也不是很高。

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

單車騎行次數

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

單車騎行次數

從單車看城市發展

從摩拜單車的熱圖分布來看,成都已經逐步呈現“雙核”發展的態勢,城市的新中心天府新區正在聚集更多的人和機會。

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

雙核發展

原來的老城區占有大量的單車,在老城區,熱圖顯示在東城區占有更多的單車,可能和這里的商業(春熙路、太古里、萬達)及人口密集的小區有直接的聯系。

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

老城區

而在成都的南部天府新區越來越多也茁壯的發展起來,商業區域和住宅區域區分明顯。在晚上,大量的單車聚集在華陽、世紀城、中和,而在上班時間,則大量聚集在軟件園附近。

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

軟件園夜間

【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

軟件園白天

via:數據挖掘與入門實踐

End.

轉載請注明來自36大數據(36dsj.com): 36大數據 ? 【干貨】用python抓取摩拜單車API數據并做可視化分析(源碼)

隨意打賞

mobike摩拜單車摩拜單車大數據摩拜單車創始人摩拜單車app摩拜單車成本摩拜單車分析摩拜單車押金摩拜單車數據干貨有哪些干貨市場
提交建議
微信掃一掃,分享給好友吧。
主站蜘蛛池模板: 奇米影音先锋 | 成熟的女性强烈交性视频 | 国产伦精品一区二区三区免费迷 | 色综合久久久久综合体桃花网 | 国产成人一区二区三中文 | 久久爱www. | 国产精品视频免费一区二区三区 | 在线 中文字幕 日韩 欧美 | 日韩精品中文字幕在线 | 欧美日韩色视频 | 久久天天躁夜夜躁狠狠85麻豆 | 久久99国产综合色 | 国产高清不卡一区二区三区 | 亚洲精品国自产拍影院 | 成人免费视频一区二区 | 欧洲做视频在线观看 | 伊人婷婷色香五月综合缴缴情 | sihu影院永久在线影院 | 9久热这里只有精品视频在线观看 | 破处一级片 | 最猛黑人xxxⅹ黑人猛交 | 日韩精品欧美一区二区三区 | 亚洲国产成人久久综合野外 | 伊人国产在线 | 在线色片 | 伊人久操 | 一级成人a毛片免费播放 | 一区二区国产一区二区a4yy | 亚欧免费视频 | 亚洲欧美日韩一区二区在线观看 | 亚洲一区二区三区日本久久九 | 黄色片免费在线观看视频 | 欧洲美女a视频一级毛片 | 亚洲视频在线观看 | 国产亚洲精品第一区在线观看 | 91精品久久久久久久久久小网站 | 日韩 欧美 亚洲 国产 | 精品国免费一区二区三区 | www久久只有这里有精品 | 九九久久精品 | 夜色综合|