1. 程式人生 > >Python實現屬於自己的公交地鐵線路圖

Python實現屬於自己的公交地鐵線路圖

本文主要講解的就是用Python計算公交線路圖的功能,即輸入起始點和結束點,即能夠得出公交的線路。
先說下資料的來源,直接網上爬取,也可以直接略過此點,直接下載我的原始碼獲取。
# coding=utf-8

import requests
from bs4 import BeautifulSoup
import time


def download_line(url, line_number):
    headers = {
        'Host': 'ditie.114huoche.com',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0'
, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'If-None-Match': 'W/"aa248d9ab9daa155024a37bbfb5ce775"', 'Cache-Control'
: 'max-age=0' } s = requests.session() resp = s.get(url, headers=headers) resp.encoding = 'gb2312' if resp.status_code == 200: print 'ok' text = resp.text soup = BeautifulSoup(text, 'html.parser') with open('./line/' + str(line_number) + '.txt', 'w+') as
f: for row in soup.find_all('span', attrs={'class': 'yx_span'}): if row.a is not None and len(row.a) > 0: f.write(row.a.string.encode('utf-8') + '\n') else: print 'connect fail' all_address = [ # ['http://ditie.114huoche.com/ShangHai/x_24/', '2'], # ['http://ditie.114huoche.com/ShangHai/x_25/', '3'], ['http://ditie.114huoche.com/ShangHai/x_26/', '4w'], ['http://ditie.114huoche.com/ShangHai/x_27/', '4n'], ['http://ditie.114huoche.com/ShangHai/x_28/', '5'], ['http://ditie.114huoche.com/ShangHai/x_29/', '6'], ['http://ditie.114huoche.com/ShangHai/x_30/', '7'], ['http://ditie.114huoche.com/ShangHai/x_31/', '8'], ['http://ditie.114huoche.com/ShangHai/x_32/', '9'], ['http://ditie.114huoche.com/ShangHai/x_33/', '10zhu'], ['http://ditie.114huoche.com/ShangHai/x_34/', '10zhi'], ['http://ditie.114huoche.com/ShangHai/x_35/', '11zhu'], ['http://ditie.114huoche.com/ShangHai/x_36/', '11zhi'], ['http://ditie.114huoche.com/ShangHai/x_37/', 'cixuanfu'], ['http://ditie.114huoche.com/ShangHai/x_38/', '1'], ['http://ditie.114huoche.com/ShangHai/x_39/', '12'], ['http://ditie.114huoche.com/ShangHai/x_40/', '13'], ['http://ditie.114huoche.com/ShangHai/x_43/', '16'] ] for row in all_address: print row download_line(row[0], row[1]) time.sleep(10)
下面開始進行地鐵線路的計算

先讀取資料

def read_line(filename):
    with open('d:/pythondemo/subline/download/line/' + filename) as f:
        value = [[row.replace('\n', ''), []] for row in f.readlines()]
    return value

計算一個站點在一條線路中的個數,以及計算位置position

def __count(data, site):
    number = 0
    for row in data:
        if row[0] == site:
            number += 1
    return number
def __index(data, site):
    for index, row in enumerate(data):
        if row[0] == site:
            return index
    else:
        return -1

下面就是具體的實現模組,有待改善

def statistics_transfer(data):
    '''
    統計可以在哪一站換乘
    那兩條路線之間可以換乘
    :param data:總的資料集合
    :return:
    '''
    line_data = []
    keys = data.keys()
    for row in keys:
        for r in keys:
            if row != r:
                for index, site in enumerate(data[row]):
                    if __count(data[r], site[0]) > 0:
                        data[row][index][1].append(r)
                        if line_data.count([row, r]) == 0:
                            line_data.append([row, r])

    return data, line_data


def sub_line(data, line, start, end):
    '''
    擷取對應的站點
    :param data: 總的資料集合
    :param line: 路線
    :param start: 開始點
    :param end: 結束點
    :return:
    '''
    value = []
    start_index = __index(data[line], start)
    end_index = __index(data[line], end)
    if start_index < end_index:
        for i in range(start_index, end_index + 1):
            value.append(data[line][i][0])
        return value
    else:
        for i in range(end_index, start_index + 1):
            value.append(data[line][i][0])
        return [row for row in value[::-1]]


def find_line(data, site, line=[]):
    '''
    查詢站點位於哪一條線路
    :param data: 總的線路資料
    :param site: 站點
    :return:
    '''
    for row in data.keys():
        if line.count(row) == 0:
            if __count(data[row], site) > 0:
                return row
    return -1


def find_transfer(data, start, direction, line=[]):
    '''
    查詢在開始點後哪一站可以換乘
    :param data:線路上的站點
    :param start:開始點
    :param direction:換乘的方向
    :param line:線路
    :return:
    '''
    start_index = __index(data, start)
    if direction == 1:
        if start >= len(data):
            return None
        else:
            for i in range(start_index + 1, len(data)):
                if len(data[i][1]) > 0:  # and len([row for row in data[i][1] if row in line]) == 0:
                    for row in data[i][1]:
                        if row not in line:
                            return data[i][0], row
            return None
    else:
        return None


def find_all_start_line(line_data, start_line):
    return [row[1] for row in line_data if row[0] == start_line]


def find_transfer_data(line_data, start_line, end_line):
    '''
    查詢重開始線路到終點線路的所有的可能的情況
    :param line_data:
    :param start_line:
    :param end_line:
    :return:
    '''
    t = find_all_start_line(line_data, start_line)
    all_date = [[start_line, r] for r in t]
    length = len([r for r in all_date if r[-1] == end_line])
    while length != len(all_date):
        for i in range(len(all_date)):
            if all_date[i][-1] != end_line:
                t = find_all_start_line(line_data, all_date[i][-1])
                i_v = all_date[i]
                del all_date[i]
                for r in t:
                    if i_v.count(r) == 0:
                        c = [r1 for r1 in i_v]
                        c.append(r)
                        all_date.append(c)
                break
        length = len([r for r in all_date if r[-1] == end_line])
    return all_date


def sub_all_site_start_end(data, start, start_line, end_line):
    '''
    擷取兩條線路之間的站點
    :param data:
    :param start:
    :param start_line:
    :param end_line:
    :return:
    '''
    value = []
    index = __index(data[start_line], start)
    for i in range(0, index)[::-1]:
        if data[start_line][i][1].count(end_line) > 0:
            value.append(sub_line(data, start_line, data[start_line][i][0], start)[::-1])
    for i in range(index, len(data[start_line])):
        if data[start_line][i][1].count(end_line) > 0:
            value.append(sub_line(data, start_line, start, data[start_line][i][0]))
    return value


def get_lines(data, lines, start, end):
    '''
    根據['1','2']獲取到整條路線站點資訊
    :param lines:
    :param start:
    :param end:
    :return:
    '''
    t = []
    for index, r in enumerate(lines):
        if index == 0:
            start_tmp = start
            continue
        if len(t) == 0:
            index_value = sub_all_site_start_end(data, start_tmp, lines[index - 1], lines[index])
            for r1 in index_value:
                lineData = LineData(lines[index - 1], r1, r1[0], r1[-1], None)
                t.append(lineData)
        else:
            row_list = []
            find_all_linedata(t, end, row_list)
            for row in row_list:
                index_value = sub_all_site_start_end(data, row.end, lines[index - 1], lines[index])
                rowt = []
                for rowr in index_value:
                    lineData = LineData(lines[index - 1], rowr, row.end, rowr[-1], None)
                    rowt.append(lineData)
                row.next_d = rowt
        if index == len(lines) - 1:
            row_list = []
            find_all_linedata(t, end, row_list)
            for row in row_list:
                rowt = sub_line(data, r, row.end, end)
                lineData = LineData(lines[index], rowt, row.end, rowt[-1], None)
                row.next_d = lineData
    return t


def find_all_linedata(linedata, end, list):
    for row in linedata:
        if row.next_d is None:
            if row.end != end:
                list.append(row)
        else:
            find_all_linedata(row.next_d, end, list)


def calculator_line(data, line_data, start, end, line=[]):
    start_line = find_line(data, start, line)
    end_line = find_line(data, end, line)
    if start_line == end_line:
        return [sub_line(data, start_line, start, end)]
    else:
        # 查詢start後面的哪一個站點可以換乘
        # site, line = find_transfer(data[start_line], start, 1, line=[])
        all_line = find_transfer_data(line_data, start_line, end_line)
        value = []
        for row in all_line:
            value.append(get_lines(data, row, start, end))
        lines = []
        get_all_line(value, end, lines, [])
        return lines


def get_end_number(lineData, end, nuber):
    '''
    取得能夠到達終點的個數
    :param lineData:
    :param end:
    :return:
    '''
    if lineData.next_d is None:
        if lineData.end == end:
            nuber += 1
            return nuber
        else:
            return nuber
    else:
        if type(lineData.next_d) == LineData:
            nuber += get_end_number(lineData.next_d, end, nuber)
        else:
            for row in lineData.next_d:
                nuber += get_end_number(row, end, nuber)
    return nuber


def get_all_line(lineData, end, list, data):
    '''
    根據資料取得所有的路線圖
    :param lineData:
    :return:
    '''
    if type(lineData) == LineData:
        if lineData.next_d is None:
            if lineData.end == end:
                data.append({'start': lineData.start, 'end': lineData.end, \
                             'line': lineData.line, 'data': lineData.ld})
                list.append(data)
        else:
            if type(lineData.next_d) == LineData:
                d = data[:]
                d.append({'start': lineData.start, 'end': lineData.end, \
                          'line': lineData.line, 'data': lineData.ld})
                get_all_line(lineData.next_d, end, list, d)
            else:
                for row in lineData.next_d:
                    d = data[:]
                    d.append({'start': lineData.start, 'end': lineData.end, \
                              'line': lineData.line, 'data': lineData.ld})
                    get_all_line(row, end, list, d)
    else:
        for row in lineData:
            get_all_line(row, end, list, data[:])


def print_lineData(lineData):
    '''
    打印出路線圖
    :param lineData:
    :return:
    '''


class LineData(object):
    def __init__(self, line, ld, start, end, next):
        self.line = line
        self.ld = ld
        self.start = start
        self.end = end
        self.next_d = next
        self.number = len(self.ld) - 1


data = {'1': read_line('1.txt'),
        '2': read_line('2.txt'),
        '3': read_line('3.txt'),
        '4n': read_line('4n.txt'),
        '6': read_line('6.txt')
        }
data, line_data = statistics_transfer(data)
# end = u'富錦路'.encode('utf-8')
end = u'曹楊路'.encode('utf-8')
# end = u'莘莊'.encode('utf-8')
start = u'徐涇東'.encode('utf-8')
# end = u'港城路'.encode('utf-8')
value = calculator_line(data, line_data, start, end)
for row in value:
    print '---------------------'
    for r in row:
        if type(r) == dict:
            vl = 'start:' + r['start'] + ';end:' + r['end'] + ';line:' + r['line'] + ';data:'
            for d in r['data']:
                vl += '-->' + d
            print vl
        else:
            print r

希望大神多多指導,共同改進改進,其中沒有公交路線,需要的話可以自己新增進去就行了,按照txt的格式進行,不需要修改其他的。

原始碼下載