悠悠楠杉
实战:用Python爬取动态加载的TfL自行车数据(附完整代码)
一、动态数据爬取的难点突破
当我们在浏览器中打开TfL自行车使用数据页面时,看似简单的"Download"按钮背后藏着技术玄机。传统爬虫直接获取HTML源码的方法在这里完全失效——因为数据是通过JavaScript动态加载的。
通过Chrome开发者工具分析网络请求(快捷键F12),我们会发现点击下载时实际触发了对https://data.london.gov.uk/download/number-bicycle-hires/...
的POST请求。这种设计是现代Web应用常用的反爬手段,但Python的requests
库配合正确参数就能完美破解。
二、逆向工程分析API
关键步骤解析:
1. 请求头伪装:服务器会校验User-Agent
等头部信息
python
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
'Referer': 'https://data.london.gov.uk/',
'X-Requested-With': 'XMLHttpRequest'
}
表单参数提取:通过浏览器监测捕获必要参数
python payload = { 'resource_id': 'bicycle-hires', 'format': 'csv', 'q': '2023', '_token': 'abc123' # 动态变化的CSRF令牌 }
会话保持:使用
Session
对象维持cookies
python with requests.Session() as s: s.get('https://data.london.gov.uk/') # 首次获取cookies response = s.post(api_url, headers=headers, data=payload)
三、完整爬虫实现代码
python
import requests
from bs4 import BeautifulSoup
import pandas as pd
def fetchtflbikedata(year):
baseurl = "https://data.london.gov.uk"
session = requests.Session()
# 第一步:获取初始页面提取CSRF令牌
index_page = session.get(f"{base_url}/dataset/number-bicycle-hires")
soup = BeautifulSoup(index_page.text, 'html.parser')
token = soup.select_one('meta[name="csrf-token"]')['content']
# 第二步:构造API请求
api_endpoint = f"{base_url}/download/number-bicycle-hires"
headers = {
'User-Agent': 'Mozilla/5.0',
'X-CSRF-TOKEN': token
}
payload = {
'year': year,
'format': 'csv'
}
# 第三步:流式下载大容量CSV
response = session.post(api_endpoint, headers=headers, data=payload, stream=True)
with open(f'tfl_bikes_{year}.csv', 'wb') as f:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
# 数据预览
df = pd.read_csv(f'tfl_bikes_{year}.csv')
print(f"成功获取{year}年数据,共{len(df)}条记录")
return df
示例:获取2023年数据
data2023 = fetchtflbikedata(2023)
四、数据处理与可视化进阶
获取数据只是第一步,真正的价值在于数据分析:python
import matplotlib.pyplot as plt
数据清洗
data2023['Day'] = pd.todatetime(data2023['Day']) data2023.set_index('Day', inplace=True)
月度聚合分析
monthlydata = data2023.resample('M').sum()
可视化
plt.figure(figsize=(12,6))
monthlydata['Number of bicycle hires'].plot(kind='bar')
plt.title('2023年伦敦自行车月租用量趋势')
plt.ylabel('租用次数')
plt.grid(axis='y')
plt.tightlayout()
plt.savefig('tflbiketrend.png')
五、反爬策略应对方案
- IP限制:使用
time.sleep(random.uniform(1,3))
模拟人类操作间隔 - 验证码:集成第三方服务如2Captcha
- 请求频率:通过代理池轮换IP(推荐使用
python-requests-ip-rotator
) - 数据校验:添加异常重试机制python
from tenacity import retry, stopafterattempt
@retry(stop=stopafterattempt(3))
def safe_fetch(url):
try:
return session.get(url)
except RequestException as e:
print(f"请求失败: {e}")
raise
通过这个真实案例,我们不仅掌握了动态CSV数据的抓取技术,更学会了如何像数据分析师一样思考。建议读者尝试修改参数获取不同年份数据,或者结合天气数据做更深度的关联分析。完整代码已测试通过,可直接应用于类似政府开放数据平台的数据采集场景。