12306旅游产品数据抓取:Python+API逆向分析
1. 引言
12306作为中国铁路官方售票平台,不仅提供火车票预订服务,还涵盖了丰富的旅游产品,如跟团游、自由行、酒店套餐等。这些数据对旅游行业分析、价格监控、竞品研究等具有重要价值。然而,12306的数据接口通常有严格的访问限制和反爬机制,直接爬取网页可能效率低下且容易被封禁。
本文将通过API逆向分析的方式,使用Python模拟合法请求,高效抓取12306旅游产品数据,并提供完整的代码实现。
2. 技术选型
2.1 Python爬虫工具
- Requests:发送HTTP请求,获取API数据。
- Selenium(可选):用于动态渲染页面的情况。
- BeautifulSoup / PyQuery:解析HTML(如果涉及网页抓取)。
- JSON / Pandas:处理和存储数据。
2.2 反爬应对策略
- User-Agent轮换:模拟浏览器访问。
- IP代理池:防止IP被封。
- 请求频率控制:避免触发反爬机制。
- Cookie/Session管理:维持登录状态(如需要)。
3. 12306旅游产品API逆向分析
3.1 分析目标
我们需要获取12306旅游产品数据,包括:
- 旅游线路名称
- 出发地/目的地
- 价格
- 行程天数
- 产品详情页URL
3.2 寻找API接口
- 浏览器开发者工具(F12):
- 打开12306旅游频道(如:https://kyfw.12306.cn/otn/)。
- 进入Network(网络)选项卡,筛选
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">XHR</font>**
或**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Fetch</font>**
请求。 - 搜索关键词
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">product</font>**
、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">travel</font>**
、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">list</font>**
等,找到返回JSON数据的API。
- 示例API(需动态分析):
- 假设找到一个类似
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">https://kyfw.12306.cn/otn/travel/product/list?page=1</font>**
的接口。 - 观察请求头(Headers),特别是
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">User-Agent</font>**
、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Referer</font>**
、**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">Cookie</font>**
等。
- 假设找到一个类似
3.3 模拟合法请求
12306的API通常需要:
- Referer(来源页面)
- Cookie(可能涉及登录态)
- 加密参数(如
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">_json_att</font>**
,需动态获取)
我们可以用Python的**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">requests</font>**
库构造合法请求:
import requestsheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36","Referer": "https://kyfw.12306.cn/otn/leftTicket/init","Cookie": "JSESSIONID=...; RAIL_EXPIRATION=...; RAIL_DEVICEID=...", # 需替换为有效Cookie
}url = "https://kyfw.12306.cn/otn/travel/product/list?page=1"
response = requests.get(url, headers=headers)if response.status_code == 200:data = response.json()print(data)
else:print("请求失败:", response.status_code)
4. 完整爬取流程与代码实现
4.1 获取Cookie(模拟登录)
12306可能需要登录才能访问API,我们可以用Selenium模拟登录并提取Cookie:
from selenium import webdriver
import timedef get_12306_cookie():driver = webdriver.Chrome()driver.get("https://kyfw.12306.cn/otn/resources/login.html")# 手动扫码登录(或自动填充账号密码)time.sleep(20) # 留时间扫码# 获取Cookiecookies = driver.get_cookies()cookie_str = "; ".join([f"{c['name']}={c['value']}" for c in cookies])driver.quit()return cookie_strcookie = get_12306_cookie()
print("获取的Cookie:", cookie)
4.2 爬取旅游产品数据
结合API分析和Cookie管理,完整爬取代码:
import requests
import pandas as pd
from fake_useragent import UserAgent
import time
import random# 代理配置
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"# 构造代理字典
proxies = {"http": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}","https": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}",
}def get_random_headers():"""生成随机请求头"""ua = UserAgent()return {"User-Agent": ua.random,"Referer": "https://kyfw.12306.cn/otn/leftTicket/init","Accept": "application/json, text/javascript, */*; q=0.01","Accept-Language": "zh-CN,zh;q=0.9","Connection": "keep-alive","X-Requested-With": "XMLHttpRequest",}def fetch_12306_travel_products(page=1, max_pages=5):"""爬取12306旅游产品数据(带代理IP)"""all_products = []base_url = "https://kyfw.12306.cn/otn/travel/product/list"for page in range(1, max_pages + 1):try:# 每次请求使用随机请求头headers = get_random_headers()headers["Cookie"] = get_12306_cookie() # 替换为你的Cookieparams = {"page": page}# 添加随机延迟(1-3秒)time.sleep(random.uniform(1, 3))# 发送带代理的请求response = requests.get(base_url,headers=headers,params=params,proxies=proxies,timeout=10)if response.status_code == 200:data = response.json()products = data.get("data", {}).get("list", [])all_products.extend(products)print(f"✅ 成功爬取第 {page} 页,共 {len(products)} 条数据")else:print(f"❌ 第 {page} 页请求失败,状态码: {response.status_code}")except Exception as e:print(f"⚠️ 第 {page} 页请求异常: {str(e)}")continuereturn all_products# 示例:获取Cookie(需替换为实际实现)
def get_12306_cookie():"""获取12306登录Cookie(示例函数,需替换为实际实现)"""return "JSESSIONID=...; RAIL_EXPIRATION=...; RAIL_DEVICEID=..."# 主程序
if __name__ == "__main__":print("🚀 开始爬取12306旅游产品数据...")# 爬取数据(3页示例)travel_data = fetch_12306_travel_products(max_pages=3)# 数据处理if travel_data:df = pd.DataFrame(travel_data)# 提取关键字段df = df[["productName", # 产品名称"fromStation", # 出发地"toStation", # 目的地"price", # 价格"days", # 行程天数"productUrl" # 详情链接]]# 保存数据df.to_csv("12306_travel_products.csv", index=False, encoding='utf_8_sig')print(f"🎉 数据已保存至 12306_travel_products.csv,共 {len(df)} 条记录")else:print("❌ 未获取到有效数据")
4.3 数据解析与存储
爬取的数据通常是JSON格式,可以用**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pandas</font>**
整理成结构化数据:
import pandas as pd# 示例数据整理
df = pd.DataFrame(travel_data)
df = df[["productName", # 产品名称"fromStation", # 出发地"toStation", # 目的地"price", # 价格"days", # 行程天数"productUrl" # 详情链接
]]
print(df.head())
5. 结论
本文通过API逆向分析,使用Python高效抓取12306旅游产品数据,并提供了完整的代码实现。关键点包括:
- API分析:通过浏览器开发者工具找到数据接口。
- 模拟请求:构造合法Headers和Cookie。
- 反爬策略:使用代理IP、随机UA、请求延迟等。
- 数据存储:用
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pandas</font>**
整理并导出CSV。