1. 项目概述:地铁线路数据采集实战
地铁作为城市交通的动脉,其线路信息对交通规划、出行服务等领域具有重要价值。今天我将分享一个实用的Python爬虫项目,教大家如何从百科类静态页面(如维基百科)采集地铁线路的核心数据,包括线路名称、起止站点、车站数量、里程以及首末班车时间等关键信息,并最终导出为结构化的CSV文件。
这个项目特别适合刚接触爬虫开发的朋友练手,因为:
- 目标页面结构清晰,适合初学者理解HTML解析
- 不需要处理复杂的动态加载内容
- 数据字段明确,便于验证采集结果
- 最终产出是可直接使用的结构化数据
提示:在实际操作前,请务必确认目标网站的robots.txt文件和使用条款,遵守爬虫道德规范。本教程仅用于技术学习交流。
2. 技术选型与工具准备
2.1 为什么选择这些工具?
对于静态页面爬取,我们采用经典的"请求-解析-存储"三件套:
- Requests库:轻量级HTTP客户端,适合处理静态页面
- BeautifulSoup4:HTML解析神器,语法简单直观
- Pandas:数据清洗和导出工具,轻松生成CSV
python复制# 基础环境配置(Python 3.6+)
pip install requests beautifulsoup4 pandas
2.2 目标页面分析
以维基百科的北京地铁页面为例,我们需要:
- 定位包含线路信息的表格或列表区域
- 分析表格结构(表头、数据行的对应关系)
- 识别关键数据的CSS选择器路径
打开浏览器开发者工具(F12),使用元素检查功能查看页面结构。通常地铁线路信息会以<table>标签组织,类名可能包含"wikitable"等特征。
3. 核心实现步骤详解
3.1 请求层实现
python复制import requests
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...'
}
def fetch_page(url):
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # 检查HTTP错误
return response.text
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None
注意:合理的请求间隔(如time.sleep(2))和User-Agent设置是友好爬虫的基本素养
3.2 解析层实现
假设目标表格结构如下:
html复制<table class="wikitable">
<tr>
<th>线路名称</th>
<th>起止站</th>
<th>车站数量</th>
<!-- 其他表头 -->
</tr>
<tr>
<td>1号线</td>
<td>苹果园 - 四惠东</td>
<td>23</td>
<!-- 其他数据 -->
</tr>
</table>
对应的解析代码:
python复制def parse_subway_lines(html):
soup = BeautifulSoup(html, 'html.parser')
table = soup.find('table', {'class': 'wikitable'})
lines = []
for row in table.find_all('tr')[1:]: # 跳过表头
cols = row.find_all('td')
line_data = {
'name': cols[0].get_text(strip=True),
'stations': cols[1].get_text(strip=True),
'distance': cols[2].get_text(strip=True),
# 其他字段...
}
lines.append(line_data)
return lines
3.3 数据存储实现
使用Pandas进行数据清洗和导出:
python复制import pandas as pd
def save_to_csv(data, filename):
df = pd.DataFrame(data)
# 处理数据格式(如拆分起止站)
df[['start_station', 'end_station']] = df['stations'].str.split(' - ', expand=True)
df.to_csv(filename, index=False, encoding='utf-8-sig')
4. 完整代码整合
python复制import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
def main():
url = "https://zh.wikipedia.org/wiki/北京地铁"
html = fetch_page(url)
if html:
lines = parse_subway_lines(html)
save_to_csv(lines, "subway_lines.csv")
print(f"成功采集{len(lines)}条线路数据")
if __name__ == "__main__":
main()
time.sleep(2) # 礼貌性延迟
5. 常见问题与解决方案
5.1 表格结构不一致怎么办?
不同城市的地铁页面表格结构可能有差异,解决方案:
- 先打印出完整的表格HTML结构
- 动态调整选择器逻辑
- 使用更通用的选择策略,如
nth-of-type
python复制# 更健壮的解析方式
name = row.select_one('td:nth-of-type(1)').get_text(strip=True)
5.2 数据清洗难题
常见数据问题及处理方法:
- 多余字符:使用
str.replace()或正则表达式python复制df['distance'] = df['distance'].str.replace('km', '').astype(float) - 时间格式:统一转换为标准时间格式
python复制df['first_train'] = pd.to_datetime(df['first_train'], format='%H:%M')
5.3 反爬机制应对
如果遇到访问限制,可以:
- 增加随机延迟
python复制import random time.sleep(random.uniform(1, 3)) - 轮换User-Agent
- 使用代理IP(需谨慎评估合法性)
6. 进阶优化方向
- 多城市支持:创建城市URL映射字典,批量采集
python复制city_urls = { '北京': 'https://...', '上海': 'https://...' } - 历史版本对比:定期运行脚本,建立数据版本库
- 可视化分析:用Matplotlib/Pyecharts生成线路长度分布图等
7. 项目总结与心得
在实际开发中,我发现几个值得注意的点:
- 维基百科的表格结构可能随编辑而变化,代码需要一定容错性
- 不同语言版本的页面结构可能有差异,建议固定使用中文版
- 首末班车时间可能包含特殊说明(如节假日),需要特别处理
这个项目的完整代码已上传到GitHub,包含更完善的错误处理和日志记录功能。通过这个案例,我们掌握了静态页面爬取的标准流程,这套方法同样适用于其他百科类数据的采集。