1. 项目背景与需求分析
地铁线路数据作为城市交通基础设施的核心信息,在城市规划、出行导航、商业选址等领域具有重要价值。传统人工收集方式效率低下且难以保证数据一致性,而百科类网站(如维基百科)往往以结构化表格形式呈现这些信息,这为自动化采集提供了天然条件。
本项目需要实现的核心功能包括:
- 精准抓取百科页面的地铁线路基础信息(线路名称、起止站点、车站数量、运营里程)
- 提取首末班车时间等动态运营数据
- 将清洗后的数据导出为标准化CSV格式
- 处理百科类页面常见的多语言、多版本等特殊情况
提示:虽然维基百科数据相对规范,但不同城市/地区的页面结构存在差异,需设计兼容性强的解析方案
2. 技术方案设计
2.1 工具选型与依赖配置
# 核心依赖库 import requests # 网络请求 from bs4 import BeautifulSoup # HTML解析 import pandas as pd # 数据结构处理 import re # 正则表达式 from datetime import datetime # 时间处理选择理由:
requests相比urllib3具有更简洁的API和更好的连接池管理- BeautifulSoup支持多种解析器(本项目选用lxml解析器,需额外安装)
- pandas提供完善的表格数据处理和CSV导出功能
- 正则表达式用于处理非结构化的时间格式文本
安装命令:
pip install requests beautifulsoup4 pandas lxml2.2 页面解析策略设计
百科类页面的典型数据特征:
- 数据通常存在于
<table class="wikitable">标签中 - 表头包含"线路名称"、"车站数量"等英文或本地化字段名
- 时间数据可能采用"05:30-23:00"或"5:30 AM - 11 PM"等格式
解析流程图:
- 定位目标表格 → 2. 提取表头映射关系 → 3. 逐行解析数据 → 4. 特殊字段格式化
3. 核心代码实现
3.1 网页请求与防封禁处理
HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'Accept-Language': 'en-US,en;q=0.9' } def get_html(url): try: resp = requests.get(url, headers=HEADERS, timeout=10) resp.raise_for_status() return resp.text except Exception as e: print(f"请求失败: {str(e)}") return None关键参数说明:
- User-Agent模拟浏览器访问
- Accept-Language控制返回的语言版本
- timeout防止长时间无响应
- 异常处理应对网络波动和反爬机制
3.2 表格数据提取器
def parse_table(html): soup = BeautifulSoup(html, 'lxml') tables = soup.find_all('table', {'class': 'wikitable'}) results = [] for table in tables: headers = [th.get_text().strip() for th in table.find_all('th')] if not validate_headers(headers): continue for row in table.find_all('tr')[1:]: cells = row.find_all('td') if len(cells) != len(headers): continue data = { headers[i]: clean_text(cell.get_text()) for i, cell in enumerate(cells) } results.append(normalize_data(data)) return results3.3 特殊字段处理函数
def parse_time(text): # 处理"05:30-23:00"格式 if re.match(r"\d{1,2}:\d{2}-\d{1,2}:\d{2}", text): start, end = text.split('-') return { 'first_train': datetime.strptime(start.strip(), "%H:%M").time(), 'last_train': datetime.strptime(end.strip(), "%H:%M").time() } # 处理AM/PM格式(英文页面) # 其他格式处理...4. 完整实现案例
以伦敦地铁页面为例的完整流程:
def scrape_london_metro(): url = "https://en.wikipedia.org/wiki/London_Underground" html = get_html(url) if not html: return None data = parse_table(html) df = pd.DataFrame(data) # 数据后处理 df['station_count'] = df['Stations'].astype(int) df['length_km'] = df['Length'].str.extract(r'(\d+\.?\d*)')[0].astype(float) # 导出CSV df.to_csv('london_underground.csv', index=False) return df5. 异常处理与优化建议
5.1 常见问题排查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 返回403错误 | IP被封禁 | 1. 增加请求间隔 2. 更换User-Agent |
| 表格定位失败 | 类名变更 | 1. 打印soup检查实际结构 2. 改用其他特征定位 |
| 时间解析异常 | 格式不符 | 1. 添加新正则模式 2. 人工校验样本 |
5.2 性能优化技巧
- 缓存机制:对已抓取页面进行本地存储(sqlite或文件缓存)
- 并行处理:使用concurrent.futures实现多页面并发抓取
- 增量更新:通过最后修改时间判断数据是否需要刷新
6. 数据导出规范
标准CSV输出字段示例:
line_name,start_station,end_station,station_count,length_km,first_train,last_train "Bakerloo","Harrow & Wealdstone","Elephant & Castle",25,23.2,05:00,01:00 "Central","Epping","Ealing Broadway",49,74.0,05:30,00:30字段处理要求:
- 线路名称保留原始语言
- 时间统一转为24小时制
- 里程单位统一转换为公里
- 空值标记为NULL
7. 项目扩展方向
- 多语言支持:通过URL参数获取不同语言版本数据
- 历史版本对比:利用维基百科的页面历史功能分析线路变更
- 地理信息补充:结合车站坐标数据生成GIS可视化
- 实时数据集成:对接官方API获取实时运营状态
注意事项:大规模抓取前请务必检查目标网站的robots.txt文件,遵守爬虫道德规范。建议设置至少2秒的请求间隔,避免对服务器造成负担。