日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

Python实现命令行监控北京实时公交之一

發(fā)布時(shí)間:2025/3/8 python 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python实现命令行监控北京实时公交之一 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

開頭先放上效果展示

在命令行輸入 python bus.py -i,顯示app基本信息,如下:

在命令行輸入 python bus.py 438,顯示北京438路公交車的位置,如下:

紅色的B說明在梅園附近有一輛438公交車,并且會(huì)不斷刷新。

GitHub地址 - https://github.com/Harpsichor...


開發(fā)過程

作為一個(gè)在北京西二旗郊區(qū)上班的苦逼,不敢太早下班,怕領(lǐng)導(dǎo)心里不滿,又不敢走太晚,不然趕不上末班公交車了,雖然加班打車能報(bào)銷,但打不著車!因此實(shí)時(shí)公交成立必備神器。


目前用的主要兩個(gè)查公交的途徑是車來了微信小程序北京公交微信公眾號(hào),經(jīng)過用了一段時(shí)間發(fā)現(xiàn)北京公交的結(jié)果是更準(zhǔn)的,但是用起來不方便,需要點(diǎn)擊多次才能看到結(jié)果,如圖:

由于想寫一個(gè)監(jiān)控公交車的小程序,車快到了能自動(dòng)提醒。


經(jīng)過在北京公交官網(wǎng)的一番搜索、分析,發(fā)現(xiàn)下面兩個(gè)可以使用的URL:

  • http://www.bjbus.com/home/ind...
    北京公交官網(wǎng)首頁,從這里可以獲取所有的公交車編號(hào)
  • http://www.bjbus.com/home/aja...
    AJAX接口,獲取指定公交車的路線、站名、目前公交車的位置
  • 獲取所有公交車

    先看第一個(gè),是官網(wǎng)首頁,使用requests去獲取,返回的是整個(gè)頁面的html, 公交車的編號(hào)在圖中顯示的這個(gè)dd標(biāo)簽中:

    我們可以使用正則表達(dá)式結(jié)合一些字符串操作,將所有公交車編號(hào)獲取到一個(gè)list中,代碼如下:

    index_url = r'http://www.bjbus.com/home/index.php'def get_bus_code_list():with open('db/bus.txt', 'r', encoding='utf-8') as f:db_data = json.loads(f.read())if db_data['time'] >= time() - 12*3600:print('Getting bus code from db...')return db_data['data']resp = requests.get(index_url).content.decode('utf-8')print('Getting bus code from web...')bus_code_string = re.findall('<dd id="selBLine">([\s\S]*?)</dd>', resp)bus_code_string = bus_code_string[0].strip().replace('<a href="javascript:;">', '')bus_code_list = bus_code_string.split('</a>')[:-1]db_data = {'time': time(),'data': bus_code_list}with open('db/bus.txt', 'w', encoding='utf-8') as f:f.write(json.dumps(db_data, ensure_ascii=False, indent=2))return bus_code_list

    注意為了避免每次都要聯(lián)網(wǎng)獲取,我將數(shù)據(jù)用json.dumps保存到了bus.txt里,并設(shè)置了一個(gè)保存時(shí)間,每次請(qǐng)求這個(gè)數(shù)據(jù)的時(shí)候,會(huì)先從bus.txt里讀取,如果數(shù)據(jù)還在有效期內(nèi)(12*3600秒),則直接使用。


    獲取指定公交車的位置

    而如果獲取公交車的實(shí)時(shí)狀態(tài),那么需要去GET請(qǐng)求一個(gè)這樣格式的url:

    http://www.bjbus.com/home/ajax_rtbus_data.php?act=busTime&selBLine=17&selBDir=5223175012989176770&selBStop=9

    那么可以看到這個(gè)url有4個(gè)參數(shù),分別是act(固定為busTime),selBLine(表示公交車編號(hào)),selBDir(表示公交車線路的編號(hào)),selBStop(表示上車的車站),請(qǐng)求上面的這個(gè)url時(shí),返回的結(jié)果是json對(duì)象,并且有個(gè)key是'html',這個(gè)html的部分結(jié)構(gòu)如下圖:

    首先開頭是一段類似提示性的信息,然后一個(gè)ul無序列表,每一個(gè)li標(biāo)簽中都有一個(gè)div,其id是遞增的數(shù)字或者是數(shù)字加一個(gè)m,純數(shù)字的div還有對(duì)應(yīng)的車站名,帶m的則是空的,我理解帶m的表示車站之間的中間區(qū)域。注意div中的i標(biāo)簽,有的有class和clstag這兩個(gè)屬性,這代表的就是公交車了,并且clstag的值表示公交車距離我們選擇上車的車站selBStop=9還有多遠(yuǎn),如果是已經(jīng)過站的車,這個(gè)值則為空或-1。所以直接給出代碼如下,代碼解釋也寫在注釋里:

    main_url = r'http://www.bjbus.com/home/ajax_rtbus_data.php'# 獲取公交車的位置,參數(shù)為公交車編號(hào),線路編號(hào),上車站點(diǎn)的編號(hào) def get_bus_status(bus_code, direction, station_no):payload = {'act': 'busTime','selBLine': bus_code,'selBDir': direction,'selBStop': station_no}# 帶參數(shù)的Get方法,將返回對(duì)象json化,獲取key為'html'的內(nèi)容resp = requests.get(main_url, params=payload).json()['html']print('Getting bus status from web...')# 這部分使用正則太復(fù)雜,因此使用BeautifulSoup解析htmlsoup = BeautifulSoup(resp, 'html.parser')# html開頭的路線,并將bs的string類型轉(zhuǎn)換為普通stringpath = str(soup.find(id="lm").contents[0]) # html開頭的提示性信息,獲取上車車站的名稱,路線的運(yùn)營時(shí)間station_name, operation_time, *_ = soup.article.p.string.split('\xa0')# tip獲取html開頭的提示性信息的具體文本,例如最近一輛車還有多遠(yuǎn)tip = ''for content in soup.article.p.next_sibling.contents:if isinstance(content, str):tip += content.replace('\xa0', '')else:tip += content.stringbus_position = []# 獲取所有有公交車的標(biāo)簽(即有clstag這個(gè)屬性的)for tag in soup.find_all('i', attrs={'clstag': True}):temp_dic = dict()# 獲取車站的idstation_id = tag.parent['id']# 如果id不帶m,說明公交車離車站較近,near_station為Truetemp_dic['near_station'] = False if 'm' in station_id else Truestation_id = station_id.replace('m', '')temp_dic['station_id'] = station_id# 獲取公交車離上車車站的距離,如果已過站則為-1temp_dic['distance'] = int(tag['clstag']) if tag['clstag'].isdigit() else -1# 此時(shí)temp_dic有車站id,距離,及near_station三個(gè)屬性,將其append到listbus_position.append(temp_dic)result = {'path': path,'station_name': station_name,'operation_time': operation_time,'bus_position': bus_position, # A list of dict'tip': tip}# 返回的結(jié)果包含較多內(nèi)容,后續(xù)按需要選取部分?jǐn)?shù)據(jù)使用return result

    獲取公交車路線代碼和公交車所有車站

    剛剛我們的函數(shù)獲取公交車的位置,需要公交車編號(hào)、路線編號(hào)和車站編號(hào)三個(gè)參數(shù),在一開始我們獲取了所有北京公交車的編號(hào),并存儲(chǔ)在bus.txt中,那么怎么獲取路線的編號(hào)的呢?同樣用Chrome瀏覽器分析北京公交官網(wǎng)的訪問過程,可以找到這樣一個(gè)鏈接:

    http://www.bjbus.com/home/ajax_rtbus_data.php?act=getLineDirOption&selBLine=438

    其返回的結(jié)果是這樣的(可以試試直接用瀏覽器訪問):

    很明顯option中的value就是公交車路線的代碼,因此也很容易寫出一個(gè)獲取公交車路線代碼的函數(shù),如下:

    main_url = r'http://www.bjbus.com/home/ajax_rtbus_data.php'def get_bus_direction(bus_code):# 先從文本中讀取,避免重復(fù)聯(lián)網(wǎng)訪問with open('db/direction.txt', 'r', encoding='utf-8') as f:db_data = json.loads(f.read())bus_direction = db_data.get(str(bus_code))if bus_direction and bus_direction['time'] >= time() - 12*3600:print('Getting bus direction from db...')return bus_direction['data']payload = {'act': 'getLineDirOption','selBLine': bus_code}resp = requests.get(url=main_url, params=payload).content.decode('utf-8')print('Getting bus direction from web...')# 正則獲取編號(hào)direction_no = re.findall('value="(\d+)"', resp)if not direction_no:print('%s路公交車未找到' % str(bus_code))return []# 正則獲取路線direction_path = re.findall(str(bus_code) + '(.*?)<', resp)data = []for j in range(2):direction_path_str = direction_path[j][1:-1]data.append([direction_no[j], direction_path_str])# 最新數(shù)據(jù)寫入文本with open('db/direction.txt', 'w+', encoding='utf-8') as f:db_data[str(bus_code)] = {'time': time(),'data': data}f.write(json.dumps(db_data, ensure_ascii=False, indent=2))return data

    獲取公交車的車站也是類似的,其url是:

    http://www.bjbus.com/home/ajax_rtbus_data.php?act=getDirStationOption&selBLine=438&selBDir=5204817486192029180

    其返回的結(jié)果是:

    直接上代碼:

    def get_bus_stations(bus_code, direction):with open('db/station.txt', 'r', encoding='utf-8') as f:db_data = json.loads(f.read())bus_station = db_data.get(str(bus_code) + '#' + str(direction))if bus_station and bus_station['time'] >= time() - 12 * 3600:print('Getting bus station from db...')return bus_station['data']payload = {'act': 'getDirStationOption','selBLine': bus_code,'selBDir': direction}resp = requests.get(main_url, params=payload).content.decode('utf-8')print('Getting bus station from web...')stations = re.findall('<option value="\d*?">(.*?)</option>', resp)[1:]with open('db/station.txt', 'w+', encoding='utf-8') as f:db_data[str(bus_code) + '#' + str(direction)] = {'time': time(),'data': stations}f.write(json.dumps(db_data, ensure_ascii=False, indent=2))return stations

    至此,功能函數(shù)就都已經(jīng)寫好了,剩余的是實(shí)現(xiàn)命令行輸出結(jié)果的功能,在后續(xù)文章說明。

    總結(jié)

    以上是生活随笔為你收集整理的Python实现命令行监控北京实时公交之一的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。