【爬虫】实时公交查询

2018-11-04  本文已影响193人  Himryang

背景

北京的公交基本都能查询实时公交,这个功能对于公交出行能带来很大的便利,能节省很多等车的时间。最直接的方法就是去公交官网查询,但是每次操作起来都有些吃力,统计了一下公交列表发现有1300+趟车,在其中找特定的一辆得老半天,让人脑阔疼。再一个就是在北京公交集团的微信公众号上面查询,这个还算可以,但是有一次遇到了不准的问题,就不愿意用了。正好最近也比较迷,不知道该干什么好(怀疑人生ing)。趁着周末有空就自己写个Python脚本来实现一下。

环境

python 3.0+
依赖外部库:requests
相关文件参见:https://github.com/Himryang/bjbus-query

效果展示

效果展示

思路

用到Chrome的开发者工具(Fiddler也差不多),在官网上单步正常操作,在时间轴上能看到自己每次操作对应的内容。主要就是捕捉ajax的请求,分析一下每步request的header,看cookies内容,发现从主页的get请求开始,到最后一步都是一样的(除了SESSIONID里面的时间戳不一样),所以就比较简单了,直接用requests库创建一个session来自动处理过程中的cookies就行了,时间戳相关的部分会自动同步的,不用担心。
这里提取内容都直接写正则实现的,也可以用美味汤(BeautifulSoup)来解析html(为了让最后打包的exe文件,re和bs4我想最好就只用一个库咯)。

代码和注释

import requests
import re
from os import system
from time import sleep
from ast import literal_eval

def getLineDir(bus,s):
    # 构建请求链接
    selbline = requests.utils.quote(bus)
    url = 'http://www.bjbus.com/home/ajax_rtbus_data.php?act=getLineDir&selBLine={}'.format(selbline)
    r = s.get(url,verify=False)
    r.encoding = r.apparent_encoding #处理编码
    rt = r.text
    LineDir_info = dict()
    # 匹配信息
    uuid_list = re.findall('(?<=data-uuid=")\d+',rt)
    busdir_list = re.findall('\((.*?)\)',rt)
    for i in [1,2]:
        flist = []
        flist.append(uuid_list[i-1])
        flist.append(busdir_list[i-1])
        LineDir_info["{}".format(i)]=flist
    return LineDir_info

def getbus(uuid,s):
    url = 'http://www.bjbus.com/home/ajax_rtbus_data.php?act=busTime&selBLine=1&selBDir={}&selBStop=1'.format(uuid)
    r = s.get(url,verify=False)
    html = literal_eval(r.text)['html']
    busc = re.findall('(?<=id=")\d+(?=m"><i  class="busc")',html)
    busc = [int(i)-1 for i in busc]
    buss = re.findall('(?<=id=")\d+(?="><i class="buss")',html)
    buss = [int(i)-1 for i in buss]
    bus_list = re.findall('(?<=title=").+?(?=")',html)
    return busc,buss,bus_list

def print_res(busc,buss,bus_list):
    for i in range(0,len(bus_list)):
        if i in busc:
            print("====================\t<======途中车辆")
        elif i in buss:
            print("{0:{1}<10}\t<------到站".format(bus_list[i],chr(12288)))
            continue
        print("{0:{1}<10}".format(bus_list[i],chr(12288)))

def rec_str(mystr):
    # 输入't19'或者'T19'都能自动转换为'特19'
    return re.sub('t','特',mystr,flags=re.I)

def main():
    # 创建session,自动处理cookies,添加头信息
    s = requests.Session()
    UA='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
    header = {'User-Agent':UA}
    s.headers.update(header)

    # 输入车次信息,获取班车的特征码uuid以及始末站
    bus = rec_str(input("输入查询车次:"))
    line_info = getLineDir(bus,s)

    fint = input("选择方向:\n[1]{}\n[2]{}\n选择(Enter确认):".format(line_info["1"][1],line_info["2"][1]))
    res = system('cls')
    uuid = line_info[fint][0]

    # 循环查询,间隔10s
    while 1:
        busc,buss,bus_list = getbus(uuid,s)
        print_res(busc,buss,bus_list)
        sleep(15)#为了避免给服务器造成影响,时间间隔设置为跟网页查询的一样比较合适
        res = system("cls")

main()

后记

最后上一张在Android手机上使用的截图效果:


demo for Android

Android平台的Termux真的很强大,而且还挺稳定,就类似于一个linux bash。在上面安装python3和requests库之后就能正常跑这个脚本了。为了避免每次都繁琐的输入,可以写个简单的shell脚本,内容python ./bus_query.py,取个简单的名字,比如a,然后chmod 777 ./a给权限,就能直接用./a来运行了。一般更多的只会查询固定班次的车,可以改源码来定制。
差不多就这些东西了,又要继续去搬砖了Orz...

注:仅供学习交流,请合理参考使用

上一篇下一篇

猜你喜欢

热点阅读