兩個在不同公司上班的人如何快速找到通勤最方便的租房位置~①


RT,要畢業工作了,面臨租房子的問題,如果是自己一個人的話,那么很簡單,只需要采取就近原則去找房子就可以了,但如果要兩個人呢?如果想和自己的npy或者其他好朋友一起租房子住,但是雙方的工作地點又不在一起怎么辦呢?目前主流的租房APP,如自如、鏈家、貝殼等還不提供這種功能,因此自己寫了一個小工具來實現一下。

代碼運行環境:Python3,可自行下載運行,指路鏈接,目前還需要自己搭建相應的環境嗷。

下面會從以下5個方面展開:

  • 介紹該方法的思路
  • 介紹第一版本工具的功能
  • 詳細介紹實現過程
  • 該方法存在的不足
  • 之后想要進一步實現的一些想法

當前思路

可以將當前的城市想象成一個由無數點構成的圖,其中兩點為兩個人的工作地點,標記為A和B,那么我們就是要從這個圖中找到比較合適的幾個點,使其到達A和B的通勤時間和交通方案比較容易讓人接受(如通勤時間盡可能短,換乘盡可能少,步行盡可能少之類的)。剛剛畢業的大家一般沒有car等大型的交通工具,絕大部分人還是以公共交通為主,博主本人更偏向於乘坐地鐵,因此將所在城市(北京)的所有地鐵站點作為備選點,分別計算每個地鐵站點到達A和B的時間t1和t2,並使用時間之和t1+t2對地鐵站進行排序,從而縮小合適地點的范圍,減小人工查詢的大量工作量。

工具功能

  1. 獲取北京市內所有的地鐵站點
  2. 計算每個站點到達A和B所需的時間和費用
  3. 篩選出時間和費用滿足一定條件的交通方案,並按照時間之和進行排序輸出

實現過程

首先第一步就是要獲取北京市內所有的地鐵站點,這就是路徑規划中的起始點,A和B作為終點,然后調用高德的API進行路徑規划,其中需要的是origindestination的經緯度,所以還需要將A、B以及地鐵站點都轉換為經緯度。因此將整個過程划分為以下三個步驟:獲取站點、獲取經緯度、路徑規划。

1. 爬蟲獲取所有地鐵站點信息

北京本地寶網站上保存有最新的地鐵線路和地鐵站點信息,首先請求首頁的源代碼,並利用正則表達式匹配其中每條線路的鏈接,如下圖1為首頁,下圖2為源代碼,其中可獲取每個線路的鏈接。

遍歷每個線路的鏈接,並再次獲取源代碼,使用正則表達式匹配獲取全部的地鐵站點名,如下圖1展示了1號線上所有的站點,下圖2為源代碼,從中可獲取全部的站點名稱。

以下為爬蟲獲取站點的代碼
link為每條地鐵線路的鏈接,遍歷每個鏈接,獲取地鐵站名稱name,代碼中的for ele in name:部分是對正則匹配出的內容進行處理,刪除多余字符,並使name以‘站’結尾,最后將獲得的站名保存在subway變量中。

# 爬蟲爬取當前的所有地鐵站名
def get_subway():
    subway = set()
    prefix = 'http://bj.bendibao.com'
    home_url = 'http://bj.bendibao.com/ditie/time.shtml'
    strhtml = requests.get(home_url)
    res = r'(?<=<strong><a href=\").+?(?=\")|(?<=<strong><a href=\').+?(?=\')'
    link = re.findall(res, strhtml.text, re.S|re.M)
    for value in link:
        url = prefix + value
        content = requests.get(url)
        res1 = r'(?:shtml" target="_blank">).+?(?:</a></td>)'
        name = re.findall(res1, content.text, re.S|re.M)
        for ele in name:
            ele = ele.lstrip('shtml" target="_blank">')
            ele = ele.rstrip('</a></td>')
            ele = re.sub(u"\\(.*?)", "", ele)
            ele = ele.replace(' ', '')
            if ele[-1] != '站':
                ele = ele + '站'
            subway.add(ele)
    return subway

2. 獲取A、B以及所有地鐵站點的經緯度

高德API-地理/逆地理編碼提供了經緯度獲取的功能,根據其中的參數說明、返回結果說明以及服務實例可以完成以下代碼,注意需要申請自己的key,coords變量為查詢得到的經緯度。

# 獲取參數|地鐵站的經緯度
def get_location(addr):
    api_url = f'https://restapi.amap.com/v3/geocode/geo?city=北京市&address={addr}&output=json&key=**your_key**'
    res = requests.get(api_url)
    json_res = json.loads(res.text)
    if json_res['status'] == '1':
        coords = json_res['geocodes'][0]['location']
    else:
        coords = None
    return coords

運行示例如下圖:西直門

3. 調用高德路徑規划API,查詢並篩選符合要求的交通方案

遍歷所有地鐵站點的經緯度,分別計算到達A和B的交通方案所需的時間和費用,需要用到高德API-路徑規划2.0-公交路線規划,參數包括show_fields=costcity1=010city2=010max_trans=2origin={origin}destination={destination}strategy={strategy}output=jsonkey=**your_key**等,其中origin為路徑起點的經緯度,即所有地鐵站點的經緯度,destination為終點的經緯度,即A和B的經緯度,strategy為換乘策略,包括用時最短、步行最短等等。
以下代碼中limit_time表示單程通勤限制時長80分鍾,limit_fee表示單程通勤限制費用20元。
當查詢西土城地鐵站到騰訊北京總部大樓的交通方案時,origin為西土城地鐵站的經緯度,destination為騰訊的經緯度。查詢后count表示返回的方案數量,默認最大為5,遍歷每種方案的時長duration和費用fee,當滿足給定限制條件時,將該方案保存在tmp_list中,最后將所有方案保存在res_list中返回。

limit_time = 80
limit_fee = 20

# 返回查詢到的方案中用時小於給定限制的時間和費用
def path_plan(origin, destination, strategy):
    res_list = []
    api_url = f'https://restapi.amap.com/v5/direction/transit/integrated?show_fields=cost&city1=010&city2=010&max_trans=2&origin={origin}&destination={destination}&strategy={strategy}&output=json&key=**your_key**'
    res = requests.get(api_url)
    json_res = json.loads(res.text)
    if json_res['status'] == '1':
        count = json_res['count']
        for i in range(int(count)):
            tmp_list = []
            duration = int(int(json_res['route']['transits'][i]['cost']['duration']) / 60)
            fee = (json_res['route']['transits'][i]['cost']['transit_fee']).rstrip('.0')
            if fee == '':
                fee = 0
            else:
                fee = int(fee)
            if duration <= limit_time and fee <= limit_fee:
                tmp_list.append(duration)
                tmp_list.append(fee)
                res_list.append(tmp_list)

    if len(res_list) == 0:
        return None
    else:
        return res_list

為了減少爬蟲以及地理編碼API的調用,將地鐵站名以及相應的經緯度信息在查詢后保存到文件中,后續需要使用經緯度信息時,直接讀取文件即可。如以下代碼中使用的變量coor_dict即為讀取經緯度文件獲得的信息。
在主函數main中遍歷coor_dict中的地鐵站名key和地鐵站點的經緯度value,然后依次調用path_plan函數,獲取每個地鐵站到達A和B的所有可行方案的時間和費用列表,即list1list2,分別對list1list2中每個方案所需的時間進行排序,找到用時最少的方案作為以當前地鐵站為起點的最優方案,最后按照time1+time2時間之和對地鐵站點進行排序,得到sort_ress

ress = dict()
for key, value in coor_dict.items(): # coor_dict字典,保存(站點名稱:經緯度)信息
    # 獲取每個地鐵站到達目標地點的方案列表
    list1 = path_plan(value, target1, 0)
    list2 = path_plan(value, target2, 0)
    if list1 is not None and list2 is not None:
        # 以下處理每種方案的時間
        time1 = limit_time
        time2 = limit_time
        for i in range(len(list1)):
            if list1[i][0] <= time1:
                time1 = list1[i][0]
                fee1 = list1[i][1]
        for i in range(len(list2)):
            if list2[i][0] <= time2:
                time2 = list2[i][0]
                fee2 = list2[i][1]
        ress[key] = [time1 + time2, time1, time2, abs(time1 - time2), fee1, fee2]
# 按照時間之和進行排序
sort_ress = sorted(ress.items(), key=lambda d: d[1], reverse=False)

sort_ress中的結果寫到文件中進行記錄,得到如下結果

存在不足

  • 調用高德路徑規划API的結果與使用高德APP查詢的方案有時有較大出入,不過僅僅有少數結果錯誤,大部分的結果是正確的,只是需要人為去甄別一下。
  • 最后得出的sort_ress中的排序結果也只能當做一個大致的參考,因為地圖只有步行+公共交通的方案,如果有輛自行車可以用的話,就需要適度調整查詢得到的通勤時間,然后就會有其他的一些更方便的方案。

未來展望

  • 一個方向是完善當前的這種做法,搞成一個可執行文件,提供設置界面,方便更多人使用;
  • 再一個的話,想的是構造有向權重圖,權重表示兩個相鄰地鐵站點之間需要的時間,然后基於圖遍歷的方法去查找最優路徑。但是這種方法的難點就是如果獲取站點之間的時間,此外還需要考慮站內換乘的時間。

參考資料

批量獲取經緯度-高德地圖API
批量獲得路徑規划—高德地圖API


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM