1. 从"退出码0"说起:那次爬虫程序什么都没爬到的排查经历
先讲个真实的场景。你在PyCharm里写了一个爬虫,辛辛苦苦把requests库的代码敲完,点下运行,控制台只显示一行:
code复制Process finished with exit code 0
没了。什么输出都没有,没有报错,没有异常,程序"成功地"什么都没干。这个情况在热搜词里反复出现,说明踩中的人不在少数。我当时爬机票价格的时候也遇到过,而且是在连续爬了十几个航班数据之后突然发生的——程序没有报错,但抓回来的数据是空的。
那次的排查过程给我留下了很深的印象,因为它暴露出来的问题不止一个,而是层层叠加的:第一个是目标网站改了接口参数,第二个是我的爬虫还在用旧的UA伪装,第三个是我连最基本的响应码检查都没做。 三层问题叠在一起,程序按旧逻辑正常跑完,但拿到的页面里已经没有机票价格了。
所以这篇文章不打算从一个"从零开始写爬虫"的角度来讲。你需要的不是"如何安装requests"这种基础内容,而是真正在爬机票价格走势这个具体场景下,怎么把数据稳定地抓下来、解析出来、存起来、最后画成趋势图。我会把整个过程拆开,每一步把为什么这么做讲透,包括那些文档里不会写的坑。
另外说一句,文中的所有代码基于Python 3.9+和requests库,目标站点我会用模拟的API地址代替。具体是哪家航司或者哪个聚合平台不重要,重要的是这套思路换到任何机票类站点上都能用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 为什么爬机票价格比爬普通网页麻烦:先搞清数据藏在哪
机票价格数据和普通文章页面的最大区别在于:它几乎全部是动态加载的。 你打开一个机票查询页面,看到的价格列表、航班号、起降时间,这些信息不是在HTML源码里写死的,而是页面加载完成后通过JavaScript发请求,从后端API拿到JSON数据,再渲染出来的。
这意味着什么?意味着你用requests直接get那个查询页面,得到的HTML源码里根本没有价格数据,只有一堆JavaScript代码和页面骨架。很多爬虫新手在这里卡住:明明浏览器里能看到价格,requests却什么都拿不到。
2.1 先通过开发者工具确认真实数据接口
这个步骤不能跳。打开目标机票网站的查询页面,按F12进入开发者工具,切到Network(网络)面板,勾选Fetch/XHR过滤条件,然后手动在页面上发起一次机票搜索。观察Network面板里出现的请求,找到返回内容里包含价格数据的那个请求。
判断标准很简单:点中某个请求,看右侧的Response(响应)标签页,如果里面出现了你刚才在页面上看到的航班号和价格,那就说明这就是数据的真实来源。绝大多数情况下,这个请求的URL会带有一长串参数,包含出发地、目的地、日期这些查询条件。
一个常见的反直觉情况是:你可能会看到十几个XHR请求,不要把时间浪费在逐个试上。按时间顺序找最早发起的那个请求,它往往是查询入口,后面的请求多半是二次补充数据或者埋点上报。我实战中遇到的多数情况是第一个请求就直接返回了价格JSON。
2.2 分析请求参数和响应结构
拿到真实的API地址之后,接下来要做的是分析它的参数结构和返回数据的JSON层次。这个工作没有捷径,就是逐个字段看。
以我当时的做法为例。假设真实接口是:
python复制https://api.example-air.com/flight/search
它的GET参数大概是这样的:
python复制params = {
"departureCity": "BJS", # 出发城市代码
"arrivalCity": "SHA", # 到达城市代码
"date": "2025-06-15", # 出发日期
"adultNum": "1", # 成人数量
"childNum": "0", # 儿童数量
"infantNum": "0", # 婴儿数量
"cabinType": "Y", # 舱位类型 Y=经济舱
"queryType": "普通查询"
}
而我最初踩坑的地方就在最后这个queryType参数上。一开始我以为是固定值,结果某天这个值变了,从"普通查询"变成了"normal",导致接口返回空数据。
这就是为什么我一再强调:即使拿到了真实接口,也要给请求响应加校验,不要假设参数永远不变。
响应JSON的结构通常是嵌套的,需要耐心地一层层剥开。典型结构:
json复制{
"status": 0,
"data": {
"flightList": [
{
"flightNo": "CA1234",
"departureTime": "08:00",
"arrivalTime": "10:30",
"price": 1250,
"tax": 50,
"cabin": "经济舱"
}
]
}
}
拿price这个字段举例,第一层是status和data,第二层是flightList数组,第三层才是每条航班信息里的价格。用Python解析的时候,就是一层一层地索引进去:resp_json["data"]["flightList"][0]["price"]。
3. 明确爬取目标:不是爬一次,而是持续监测价格
很多人的思路是:写个脚本,跑一次,拿到今天的价格,结束了。但"价格走势"这个词决定了你要做的不是一次性抓取,而是**在一个时间周期内,对同样的航线、同样的日期,反复抓取并记录价格变化
