1. 程式人生 > >爬蟲獲取mobike共享單車信息

爬蟲獲取mobike共享單車信息

gzip 找不到 mac os x msg clu strftime number rms break

背景:端午節假期的時候參加了學校的數學建模比賽,題目是關於共享單車的供需匹配問題,需要獲得共享單車的數量和時空分布情況。

在苦苦找尋數據無果的情況下決定自己用爬蟲對天津地區的mobike進行統計。

在網上找到了這篇爬蟲的代碼,本著少造輪子的基本原則,我選擇了這個代碼進行統計,這裏記錄一下歷程,方便日後查閱。

先上原作者github地址:git clone https://github.com/derekhe/mobike-crawler。python3環境,爬取的是微信小程序,之前是可以爬手機客戶端的,不過隨著客戶端完善變得難爬,小程序的爬取

能用到什麽時候要也不確定。

我會把我的一些代碼和數據放到百度雲裏,鏈接在文末,有需要的小夥伴可以自取。

一、數據之初探

首先確定了要爬取微信小程序的時候,先需要確定一個大概的方向,下載fiddler,對微信小程序進行抓包處理,這一步進展比較順利。不太了解這一步的可以搜索手機APP抓包相關的知識,這裏不再贅述。

比較容易的發現在請求頁面需要post你的經緯度坐標給https://mwx.mobike.com/mobike-api/rent/nearbyBikesInfo.do這個網頁,然後返回周圍的車的信息,包括

bikeID,distX,distY,distance等信息,比較方便的是這些信息都放在了json文件中。而我們關系的只是車的位置和距離即distX,distY,distance。

註意點是要把瀏覽器頭部偽裝成手機瀏覽器。

z.json()[‘object‘]即可。

import requests
headers = {
    User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E304 MicroMessenger/6.5.7 NetType/WIFI Language/zh_CN,
    Content-Type: application/x-www-form-urlencoded,
    Referer: https://servicewechat.com/wx80f809371ae33eda/23/page-frame.html
, } url = https://mwx.mobike.com/mobike-api/rent/nearbyBikesInfo.do data = { longitude:121.1883,# 經度 latitude:31.05147, # 緯度 citycode:021, errMsg:getMapCenterLocation:ok } z = requests.post(url,data=data,headers=headers,verify=False)

二、數據之再思考

有了第一步的初探,下一步需要做的東西也十分清楚。要獲得天津地區的Mobike數量可以近似畫一個長方形,用爬蟲爬取這個長方形內的自行車數目即可。

直接貼原作者的代碼,我的代碼在其中做了一些修改,不過我是放在了jupyter notebook中運行的,頁面中有將近十萬的運行結果數據的原因,每次打開代碼都會卡死。。。

需要註意的點是,在我電腦上運行時找不到retrying庫,直接去掉即可運行。運行結果放在db文件夾下。

offset設置為0.002左右即可,太大容易導致漏數,太小會有重復誤差。這也是網上有其他代碼用數據庫的原因,不過由於數學建模的時間緊張,再加我對數據庫不太熟悉,直接暴力設定爬取步長也沒出現問題。

import datetime
import os
import os.path
import random
import sqlite3
import threading
import time
import ujson
from concurrent.futures import ThreadPoolExecutor

import numpy as np
import requests
from retrying import retry

from modules.ProxyProvider import ProxyProvider


class Crawler:
    def __init__(self):
        self.start_time = datetime.datetime.now()
        self.csv_path = "./db/" + datetime.datetime.now().strftime("%Y%m%d")
        os.makedirs(self.csv_path, exist_ok=True)
        self.csv_name = self.csv_path + "/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + .csv
        self.db_name = "./temp.db"
        self.lock = threading.Lock()
        self.proxyProvider = ProxyProvider()
        self.total = 0
        self.done = 0

    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)

    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()

    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

        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()

    def group_data(self):
        print("Creating group data")
        conn = sqlite3.connect(self.db_name)
        cursor = conn.cursor()

        f = open(self.csv_name, "w")
        for row in cursor.execute(‘‘‘SELECT * FROM mobike‘‘‘):
            timestamp, bikeIds, bikeType, distId, distNumber, type, lon, lat = row
            f.write("%s,%s,%s,%s,%s,%s,%s,%s\n" % (
                datetime.datetime.fromtimestamp(int(timestamp) / 1000).isoformat(), bikeIds, bikeType, distId, distNumber, type, lon, lat))
        f.flush()
        f.close()

        os.system("gzip -9 " + self.csv_name)


Crawler().start()

三、數據簡單處理和分析

爬取天津主城區(紅橋區,河西區,河東區,和平區,南開區,河北區)和周邊範圍的Mobike,

爬取結果大概為98398輛,利用Kmeans聚類進行聚類分析。

分別取聚類點50,75,100,150,200來通過matplotlib繪圖觀察聚類結果。選擇n_clusters=50。

得到聚類中心點坐標,然後再利用反解析軟件解析坐標對應的地點信息,即可獲得共享單車的主要分布區域。

最後,一些數據和代碼放在百度雲上,用則自取。

百度雲地址:http://pan.baidu.com/s/1eRHjmVK密碼:d5dw

以上。

:)

爬蟲獲取mobike共享單車信息