【Python爬虫】爬取公共交通路网数据

news2025/3/9 11:44:30

程序来自于Github,以下这篇博客作为完整的学习记录,也callback上一篇爬取公共交通站点的博文。

Bardbo/get_bus_lines_and_stations_data_from_gaode: 这个项目是基于高德开放平台和公交网获取公交线路及站点数据,并生成shp文件,代码相对粗糙,但简单可用https://github.com/Bardbo/get_bus_lines_and_stations_data_from_gaode

1. 导入库

首先是程序需要的python库。

import requests
import json
import pandas as pd
from lxml import etree
import time
from tqdm import tqdm
  • requests:用于发送HTTP请求,获取网页内容或API数据。

  • json:用于处理JSON格式的数据。

  • pandas:用于数据处理和保存为CSV文件。

  • lxml:用于解析HTML内容。

  • time:用于控制爬取速度,避免过快请求导致被封禁。

  • tqdm:用于显示进度条,方便查看爬取进度。

2. 获取城市公交线路名称(get_bus_line_name 函数)

def get_bus_line_name(city_phonetic):
    url = 'http://{}.gongjiao.com/lines_all.html'.format(city_phonetic)
    r = requests.get(url).text
    et = etree.HTML(r)
    line_name = et.xpath('//div[@class="list"]//a/text()')
    return line_name

这里同样也需要先爬取公交线路的名称,函数需要传入city_phonetic也就是城市的拼音(如 changsha、wuhan),函数会返回该城市所有公交线路的名称列表(line_name)。

3. 爬取公交路线(get_line_station_data 函数)

获取公交线路的url是高德的https://restapi.amap.com/v3/bus/linename?extensions=all&key={ak}&output=json&city={city}&offset=1&keywords={line_name}

那么先来看一下官方的介绍,这里使用的是公交路线关键字查询,也就是说我们要输入公交路线的关键字,例如15路等等。

返回的内容如下,status记录了查询成功与否,buslines中记录了查询成功的公交路线。

接下来来看代码:

def get_line_station_data(city, line_name, ak, city_phonetic):
    print(f'正在获取-->{line_name}')
    time.sleep(1)
    url = f'https://restapi.amap.com/v3/bus/linename?extensions=all&key={ak}&output=json&city={city}&offset=1&keywords={line_name}'
    r = requests.get(url).text
    rt = json.loads(r)
    try:
        if rt['buslines']:
            if len(rt['buslines']) == 0:
                print('no data in list..')
            else:
                dt = {}
                dt['line_name'] = rt['buslines'][0]['name']
                dt['polyline'] = rt['buslines'][0]['polyline']
                dt['total_price'] = rt['buslines'][0]['total_price']

                station_name = []
                station_coords = []
                for st in rt['buslines'][0]['busstops']:
                    station_name.append(st['name'])
                    station_coords.append(st['location'])

                dt['station_name'] = station_name
                dt['station_corrds'] = station_coords

                dm = pd.DataFrame([dt])
                dm.to_csv(f'{city_phonetic}_lines.csv',
                          mode='a',
                          header=False,
                          index=False,
                          encoding='utf_8_sig')
        else:
            print('data not avaliable..')
            with open('data not avaliable.log', 'a') as f:
                f.write(line_name + '\n')
    except:
        print('error.. try it again..')
        time.sleep(2)
        get_line_station_data(city, line_name, ak, city_phonetic)

函数通过高德地图API获取某条公交线路的详细信息,并保存到CSV文件中。通过构造API请求URL获取公交线路数据,解析响应并提取线路名称、路径、票价、站点名称和坐标,将数据保存到CSV文件,若数据不可用则记录日志,失败时等待2秒后重试。

如果爬取失败的话,检查一下key是否达到了限额,一天只能爬取5000次,爬取公交线路比较耗费次数。

爬取完成后来看一下保存的csv,总共5列。

  • A列:公交线路关键字(名称)
  • B列:公交线路polyline,也就是线路途径的每个点(注意不是站点,公交线路的每个拐点都会被记录)
  • C列:总价
  • D列:途径站点关键字(名称)
  • E列:途径站点经纬度坐标

4. 主程序调用

if __name__ == '__main__':
    city = '益阳'
    city_phonetic = 'yiyang'
    ak = '###'  # 这里建议更改为自己的key

    start_time = time.time()
    print(f'==========正在获取 {city} 线路名称==========')
    line_names = get_bus_line_name(city_phonetic)
    print(f'{city}在公交网上显示共有{len(line_names)}条线路')
    for line_name in tqdm(line_names):
        get_line_station_data(city, line_name, ak, city_phonetic)
    end_time = time.time()
    print(f'我爬完啦, 耗时{end_time - start_time}秒')

自己需要设置的是最开始的city、city_phonetic、ak.

程序调用了上面的函数并记录了爬取的时间。

5. 线路、站点转成shp

那么有了这个csv怎么可视化呢?

那么作者就写了DataToShp这个 类用于将公交线路和站点数据从CSV文件转换为Shapefile格式,主要功能包括:

get_station_data:将站点坐标和名称从字符串格式转换为列表格式,将数据从横向展开为纵向,并去除重复项;

get_line_data:将线路的折线数据从字符串格式转换为列表格式;

create_station_shp:创建站点Shapefile,包含站点名称和坐标;

create_lines_shp:创建线路Shapefile,包含线路名称和折线坐标;

其实作者其实在这里还引入了从高德的火星坐标系转换为WGS_84的函数,但是大部分时候这种转换并不可靠,所以建议高德爬取的数据就搭配高德地图进行可视化使用。

# -*- coding: utf-8 -*-
# @Author: Bardbo
# @Date:   2020-11-09 21:09:12
# @Last Modified by:   Bardbo
# @Last Modified time: 2020-11-09 21:59:35
import pandas as pd
import numpy as np
import shapefile
# import converter

class DataToShp:
    def __init__(self, filename):
        self.data = pd.read_csv(filename,
                                names=[
                                    'line_name', 'polyline', 'price',
                                    'station_names', 'station_coords'
                                ])

    def get_station_data(self):
        df_stations = self.data[['station_coords', 'station_names']]
        # 将原本的一行字符串变为列表
        df_stations['station_coords'] = df_stations['station_coords'].apply(
            lambda x: x.replace('[', '').replace(']', '').replace(
                '\'', '').split(', '))
        df_stations['station_names'] = df_stations['station_names'].apply(
            lambda x: x.replace('[', '').replace(']', '').replace(
                '\'', '').split(', '))
        # 横置的数据变为纵向的数据
        station_all = pd.DataFrame(\
                      np.column_stack((\
                                       np.hstack(df_stations['station_coords'].repeat(list(map(len, df_stations['station_coords'])))),
                                       np.hstack(df_stations['station_names'].repeat(list(map(len, df_stations['station_names']))))
                                     )),
                      columns=['station_coords','station_names'])
        # 去除重复
        station_all = station_all.drop_duplicates()
        # # 坐标转换
        # station_all['st_coords_wgs84'] = station_all['station_coords'].apply(
        #     self.stations_to_wgs84)
        station_all.reset_index(inplace=True)
        self.stations = station_all

    def get_line_data(self):
        df_lines = self.data[['line_name', 'polyline']]
        df_lines['polyline'] = df_lines['polyline'].apply(
            lambda x: x.split(';'))
        # # 坐标转换
        # df_lines['lines_wgs84'] = df_lines['polyline'].apply(
        #     self.lines_to_wgs84)
        df_lines.reset_index(inplace=True)
        self.lines = df_lines

    # def stations_to_wgs84(self, coor):
    #     xy = coor.split(',')
    #     lng, lat = float(xy[0]), float(xy[1])
    #     return converter.gcj02_to_wgs84(lng, lat)
    #
    # def lines_to_wgs84(self, coor):
    #     ls = []
    #     for c in coor:
    #         xy = c.split(',')
    #         lng, lat = float(xy[0]), float(xy[1])
    #         ls.append(converter.gcj02_to_wgs84(lng, lat))
    #     return ls

    def create_station_shp(self, city_phonetic):
        w = shapefile.Writer(f'./data/{city_phonetic}_stations.shp')
        w.field('name', 'C')
        # 确保所有坐标都是浮动类型
        for i in range(len(self.stations)):
            coords = self.stations.loc[i, 'station_coords'].split(',')  # 获取坐标
            lat = float(coords[0])  # 强制转换为浮动类型
            lon = float(coords[1])  # 强制转换为浮动类型
            # 确保坐标是浮动类型
            w.point(lat, lon)  # 写入点
            w.record(self.stations.loc[i, 'station_names'])  # 写入记录
        w.close()

    def create_lines_shp(self, city_phonetic):
        w = shapefile.Writer(f'./data/{city_phonetic}_lines.shp')
        w.field('name', 'C')
        for i in range(len(self.lines)):
            polyline = self.lines['polyline'][i]
            # 如果 polyline 是字符串,则使用 split();如果是列表,则直接使用
            if isinstance(polyline, list):
                polyline = [list(map(float, point.split(','))) for point in polyline]
            # 确保 polyline 是列表类型,进行写入
            w.line([polyline])
            w.record(self.lines['line_name'][i])
        w.close()


if __name__ == '__main__':
    dts = DataToShp('yiyang_lines.csv')
    dts.get_station_data()
    dts.get_line_data()
    dts.create_station_shp()
    dts.create_lines_shp()
    print('shp文件创建完成')

如下就是可视化后的效果【爬的时候无意中发现学校这多了两条公交,深入鄂州,win!】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2312120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

009---基于Verilog HDL的单比特信号边沿检测

文章目录 摘要一、边沿检测二、时序逻辑实现2.1 rtl2.2 tb 三、组合逻辑实现3.1 rtl3.2 tb 摘要 文章为学习记录。采用时序逻辑和组合逻辑实现边沿检测的核心逻辑。组合逻辑实现的上升沿和下降沿的脉冲比时序逻辑实现的上升沿和下降沿的脉冲提前一拍。 一、边沿检测 边沿检测…

Trae IDE新建C#工程

目录 1 结论 2 项目结构 3 项目代码 1 结论 新建C#工程来说,Trae的Chat比DeepSeek的Coder好用。 2 项目结构 MyWinFormsApp/ │ ├── Program.cs ├── Form1.cs ├── Form1.Designer.cs ├── MyResources/ │ └── MyResources.resx └── MyWin…

三、0-1搭建springboot+vue3前后端分离-idea新建springboot项目

一、ideal新建项目1 ideal新建项目2 至此父项目就创建好了,下面创建多模块: 填好之后点击create 不删了,直接改包名,看自己喜欢 修改包名和启动类名: 打开ServiceApplication启动类,修改如下: …

Unity光照之Halo组件

简介 Halo 组件 是一种用于在游戏中创建光晕效果的工具,主要用于模拟光源周围的发光区域(如太阳、灯泡等)或物体表面的光线反射扩散效果。 核心功能 1.光晕生成 Halo 组件会在光源或物体的周围生成一个圆形光晕,模拟光线在空气…

递归专题刷题

文章目录 递归合并两个有序链表题解代码 反转链表题解代码 两两交换链表中的节点题解代码 Pow(x, n)(快速幂)题解代码汉诺塔题解代码 总结 递归 1. 重复的子问题宏观看待递归问题 合并两个有序链表 题目链接 题解 1. 重复的子问题 -> 函数头的设…

Android Studio 一直 Loading devices

https://stackoverflow.com/questions/71013971/android-studio-stuck-on-loading-devices

摄相机标定的基本原理

【相机标定的基本原理与经验分享】https://www.bilibili.com/video/BV1eE411c7kr?vd_source7c2b5de7032bf3907543a7675013ce3a 相机模型: 定义: 内参:就像相机的“眼睛”。它描述了相机内部的特性,比如焦距(镜头的放…

CentOS 7 安装 Redis6.2.6

获取资源、下载安装 Redis6.2.6 安装Redis6.2.6 上传到服务器或直接下载(wget http://download.redis.io/releases/redis-6.2.6.tar.gz)、再解压安装 tar -zxvf redis-6.2.6.tar.gz 进入redis解压目录 cd redis-6.2.6先编译 make再执行安装 make PREFI…

3D数字化:家居行业转型升级的关键驱动力

在科技日新月异的今天,家居行业正经历着一场前所未有的变革。从传统的线下实体店铺到线上电商平台的兴起,再到如今3D数字化营销的广泛应用,消费者的购物体验正在发生翻天覆地的变化。3D数字化营销不仅让购物变得更加智能和便捷,还…

无人机推流/RTMP视频推拉流:EasyDSS无法卸载软件的原因及解决方法

视频推拉流/直播点播EasyDSS平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务,在应用场景中可实现视频直播、点播、转码、管理、录像、检索、时移回看等。此外,平台还支持用户自行上传视频文件,也可将上传的点播…

Logisim实验--计组

每个实验会先讲一下原理再给出答案。 实验一:7段数码管驱动电路设计 实验目的 (1)帮助学生理解真值表方式设计电路的原理; (2)能利用Logisim的真值表生成电路功能自动生成所需电路。 这里我们要看清每个引脚控制的是哪个灯亮,注意看它的线…

【Linux】软硬链接 | 动静态链接(三)

目录 前言: 一、软硬链接 1.软链接 2.硬链接 3.硬链接数 4.软硬链接的区别 5.使用unlink删除链接的文件 6.目录文件链接数( . 和 .. ) 二、静态库的制作和使用 1.制作静态库 2.使用静态库 2.1方法一 2.2方法二 2.3方法三 三、动态库的制作和使用 1.…

数据结构(回顾)

数据结构(回顾) 回顾 不同点顺序表链表存储空间上物理上一定连续逻辑上连续,物理上不一定连续随机访问支持,时间复杂度O(1)不支持,时间复杂度O(N)任意位置插入或者删除元素可能需要挪动元素,效率低&#…

达梦数据库在Linux,信创云 安装,备份,还原

(一)系统环境检查 1操作系统:确认使用的是国产麒麟操作系统,检查系统版本是否兼容达梦数据库 V8。可以通过以下命令查看系统版本: cat /etc/os-release 2硬件资源:确保服务器具备足够的硬件资源&#xff0…

从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动

目录 所以,键盘是如何工作的 说一说我们的8042 输出缓冲区寄存器 状态寄存器 控制寄存器 动手! 注册中断 简单整个键盘驱动 Reference ScanCode Table 我们下一步就是准备进一步完善我们系统的交互性。基于这个,我们想到的第一个可以…

01-简单几步!在Windows上用llama.cpp运行DeepSeek-R1模型

1.llama.cpp介绍 Llama.cpp 是一个开源的、轻量级的项目,旨在实现 Meta 推出的开源大语言模型 Llama 的推理(inference)。Llama 是 Meta 在 2023 年开源的一个 70B 参数的高质量大语言模型,而 llama.cpp 是一个用 C 实现的轻量化…

HarmonyOS Next 属性动画和转场动画

HarmonyOS Next 属性动画和转场动画 在鸿蒙应用开发中,动画是提升用户体验的关键要素。通过巧妙运用动画,我们能让应用界面更加生动、交互更加流畅,从而吸引用户的注意力并增强其使用粘性。鸿蒙系统为开发者提供了丰富且强大的动画开发能力&…

JavaWeb-mysql8版本安装

下载方式 地址:https://www.mysql.com/cn/downloads/ 选择:MySQL Community (GPL) downloads 选择:MySQL Community Server 选择: 选择: 安装mysql (8.0.30) 1、以管理员身份 打开 命令行…

【实战ES】实战 Elasticsearch:快速上手与深度实践-3.2.3 案例:新闻搜索引擎的相关性优化

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 Elasticsearch新闻搜索引擎相关性优化实战3.2.3 案例:新闻搜索引擎的相关性优化项目背景1. 相关性问题诊断与分析1.1 初始查询DSL示例1.2 问题诊断矩阵1.3 性能基…

HCIA复习拓扑实验

一.拓扑图 二.需求 1.学校内部的HTTP客户端可以正常通过域名www.baidu.com访问到百度网络中HTTP服务器 2.学校网络内部网段基于192.168.1.0/24划分,PC1可以正常访问3.3.3.0/24网段,但是PC2不允许 3.学校内部路由使用静态路由,R1和R2之间两…