ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

百度迁徙数据爬取实战:从接口分析到数据存储的完整方案

百度迁徙数据爬取实战:从接口分析到数据存储的完整方案 简介针对手动收集百度迁徙数据效率低、易出错的问题这份基于Python的爬虫脚本资源提供了自动化抓取并整理迁徙数据的完整方案。资源包为zip格式内含1个py源文件大小仅2KB代码精简无冗余适合有一定Python基础的爬虫初学者及人口流动、交通规划相关研究人员参考。目前已有1553人学习下载。脚本涵盖了网络爬虫核心流程通过requests构造HTTP请求获取页面内容结合BeautifulSoup/lxml解析HTML定位迁徙信息遇到动态加载时引入Selenium模拟浏览器操作并将抽取出的日期、城市、迁入迁出量等字段交给pandas清洗后存储为CSV或Excel表格。同时兼顾robots.txt协议、请求间隔设置等合规与礼貌爬取细节能够帮助读者在真实项目中建立数据采集的完整工程思维。 百度迁徙数据爬取这个方向我是今年年初开始认真折腾的。起因很简单想分析春运前后的人口流动规律发现百度迁徙qianxi.baidu.com上能直观看到全国 360 多个城市的迁入迁出趋势数据维度还细到按天更新。但问题也来了官方没有开放下载接口页面上的图表全靠 JavaScript 动态渲染直接抓 HTML 拿不到任何有效数据。最后折腾了一圈把接口分析、请求伪装、数据清洗、落地存储整套流程跑通了这里把能直接“抄作业”的方案完整写出来顺便把我在这个过程中踩过的坑也一并交代清楚。1. 项目拆解百度迁徙数据到底长什么样先花点时间搞清楚目标数据的结构这一步比写代码更重要。百度迁徙首页会展示从“迁入”和“迁出”两个维度观察的人口迁徙情况主视图是地图上的动态流向线下方配套有迁徙趋势图、热门出发地、热门目的地等表格。这些可视化背后其实都在向服务端请求接口返回的是 JSON 格式的标准化数据。1.1 核心数据维度迁入迁出指数、热门城市、迁徙规模我实际操作下来百度迁徙的数据可以拆成三类核心内容第一类是“迁入/迁出趋势”数据。以某个城市为中心按日期返回该城市与全国所有其他城市之间的人口流动指数。这里的指数不是一个绝对人数而是百度根据定位数据计算出的相对迁徙规模适合做横向对比和趋势分析。第二类是“热门出发地/目的地”数据。比如我想看春运期间成都的人从哪里来接口会返回排名前列的城市列表每项包含城市名、省份、流动指数。第三类是“迁徙规模指数”。这个值评估的是全国整体迁徙量可以用来判断春运返程高峰出现在哪一天。它不区分城市就是一个综合数值。1.2 数据更新的时间节奏与存储策略百度迁徙的数据每天更新一次历史数据长时间可查。以 2025 年 1 月的数据为例从 1 月 14 日到 2 月 22 日这 40 天是春运周期数据占用的存储空间并不大——每个城市每天一条记录全国 360 多个城市全部抓下来一张表也就几万行。我最初用 CSV 文件存储后来数据量上来后换成了 SQLite本地跑分析完全够用没必要为了这个量级上 MySQL。提醒一点百度迁徙需要手动选择日期才能查看历史数据URL 参数里包含日期字段爬虫可以直接指定不用依赖页面上的日期选择器。2. 环境准备与基础技术选型2.1 为什么用 Anaconda 搭建 Python 环境我在初学阶段用的是系统自带的 Python后来装各种库时把环境搞乱了包版本冲突频繁。换了 Anaconda 之后这类问题基本绝迹。Anaconda 自带 Python 解释器和常用数据处理库核心价值是 conda 环境管理机制可以给每个项目单独建一个虚拟环境不同项目用到不同版本的 pandas、requests彼此互不干扰。具体建环境的命令很有用我执行的是conda create -n baidu_migration python3.9 conda activate baidu_migration然后在这个环境里安装所需依赖库pip install requests pandas lxml sqlite3sqlite3 是 Python 标准库不用额外安装。requests 用来发送 HTTP 请求pandas 负责数据清洗和分析lxml 用来撑起 pandas 的 read_html 方法虽然最终没用上但留着有备无患。2.2 三个必装核心库的功能解析这几个库是爬虫方案的基石各司其职requests负责和百度服务器打交道。模拟浏览器发送 HTTP 请求接收服务器响应。最常用的方法是 requests.get(url, headersheaders)其中 headers 可以带 User-Agent、Referer 等信息让服务器认为请求来自真实浏览器。pandas数据分析利器。爬下来的 JSON 数据经过 json.loads 解析后变成嵌套字典用 pandas 的 json_normalize 可以快速把嵌套结构展开为表格几行代码就完成数据结构化。sqlite3轻量级数据库。Python 内置支持数据表就是一个本地文件随用随查非常适合爬虫落库。开始爬取之前先用一个简单的测试脚本确认 requests 能正常请求百度服务器import requests url https://qianxi.baidu.com/ headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, headersheaders, timeout5) print(resp.status_code)如果返回 200说明网络通路没问题可以进入接口分析阶段。3. 接口分析与爬虫方案设计3.1 抓包定位真实数据接口的完整过程直接访问百度迁徙页面时看到的迁徙地图和排行榜都经过 JavaScript 异步加载。想拿到原始数据需要打开浏览器开发者工具进行抓包。以 Chrome 为例按 F12 进入开发者工具切到 Network 面板勾选 Fetch/XHR 过滤请求类型然后刷新页面就能看到页面在后台悄悄调用的接口列表。我的操作步骤是打开百度迁徙首页保持开发者工具开启刷新页面。在 Network 面板筛选 XHR 类型请求。观察列表中出现大量以 /api/ 开头的接口逐个点击查看响应内容。找到一个名为 getCityMigrationTrend 的接口返回的 JSON 里有清晰的“迁入趋势”和“迁出趋势”字段确认这就是核心数据源。确认接口后右键复制该请求的 URL 和请求头在 Python 里先测试一下能否直接请求成功。用 requests 模拟时发现一个坑单纯带 User-Agent 会被服务器拒绝返回 400 错误码加上 Referer 之后正常了。这可能和百度对热链的保护机制有关请求头里带 Referer服务器会认为请求是从页面内部发起的信任度提高。3.2 核心接口参数详解与拼接规则百度迁徙的核心接口 URL 结构清晰参数也不复杂最常用的接口是https://qianxi.baidu.com/api/getCityMigrationTrend?cityCodexxxdate2025-01-28cityCode 是城市编码date 是日期格式为 YYYY-MM-DD。cityCode 不是行政区划代码而是百度内部维护的城市 ID。北京是 110000上海是 310000广州是 440100但这套编码和身份证前六位基本对应可以直接用行政区划代码代替测试。完整请求头我实测下来这样写最稳headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://qianxi.baidu.com/, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9, }请求时加一个 3 秒左右的延时避免短时间大量请求触发服务端限流。我最初没加延时连续跑了 200 个城市后IP 被临时封禁了一段时间后来加上了 random.uniform(2, 4) 随机延时问题解决。4. 完整爬虫代码实现与逐行解读import requests import json import pandas as pd import time import random import sqlite3 def get_migration_data(city_code, date): 获取指定城市在指定日期的迁入迁出数据 url https://qianxi.baidu.com/api/getCityMigrationTrend params { cityCode: city_code, date: date } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://qianxi.baidu.com/ } try: resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() return data[data] except Exception as e: print(f请求失败: {city_code} {date}, 错误: {e}) return None def parse_migration_data(raw_data): 解析原始 JSON 数据提取迁入迁出列表 返回两个 DataFrame 和一个趋势列表 if not raw_data: return None, None, None # 迁入数据 in_list raw_data.get(inList, []) df_in pd.DataFrame(in_list) # 迁出数据 out_list raw_data.get(outList, []) df_out pd.DataFrame(out_list) # 整体趋势数据 trend raw_data.get(trend, {}) return df_in, df_out, trend4.1 单城市单日期抓取测试先把单城市的抓取流程跑通再扩展成批量任务。主程序可以这样写if __name__ __main__: # 测试北京 2025年1月28日春运返乡高峰 city_code 110000 date 2025-01-28 raw get_migration_data(city_code, date) df_in, df_out, trend parse_migration_data(raw) print( 迁入北京 TOP10 ) if df_in is not None and not df_in.empty: print(df_in.head(10)) print( 迁出北京 TOP10 ) if df_out is not None and not df_out.empty: print(df_out.head(10))第一次跑通时看到屏幕上打印出规范的表格数据整个链路就通了。原始 JSON 里每个城市对象的 key 是百度内部城市编码value 是流动指数。直接用 pandas 打印城市名和指数一目了然。4.2 全国多城市多日期批量抓取循环控制与异常处理单城市能跑通批量抓取的核心思路就是双层循环外层遍历日期内层遍历城市列表。这里有两个经验值得强调第一是城市列表的长度控制。全国 360 多个城市全部抓一遍加上延时和网络波动跑完至少 20 分钟。如果只是做春运分析建议按去年春运热门迁徙城市 TOP50 来选取既能覆盖主要流量又省时间。第二是断点续传。批量跑的时候遇到网络超时或者服务器拒绝不能指望整个脚本重跑。我的做法是把已完成的城市写入 SQLite 数据库每次循环前先查一下这条记录是否已存在存在就跳过。代码逻辑如下def city_already_done(cursor, city_code, date, table_name): 检查该城市该日期是否已抓取过 sql fSELECT COUNT(*) FROM {table_name} WHERE city_code? AND date? cursor.execute(sql, (city_code, date)) count cursor.fetchone()[0] return count 0这块逻辑帮我省了大量重复工作。网络中断、电脑休眠、服务器超时这些不可控因素在长任务里基本都会遇到一次没有断点续传就从零重跑是非常折磨人的。4.3 数据入库从 CSV 到 SQLite 的升级方案第一版我把数据存成 CSV每天一个文件文件名带日期。数据量小的时候确实方便直接用 Excel 打开就能看。但跑了 10 天后问题暴露了跨日期对比分析需要手动合并多个 CSV 文件而且 pandas 读取时中文城市名偶尔出现编码问题。换 SQLite 之后数据集中在一张表里查询分析用一条 SQL 搞定。建表语句CREATE TABLE IF NOT EXISTS migration_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, city_code TEXT NOT NULL, city_name TEXT, date TEXT NOT NULL, direction TEXT CHECK(direction IN (in, out)), dest_code TEXT, dest_name TEXT, value REAL, UNIQUE(city_code, date, direction, dest_code) );UNIQUE 约束是防止重复插入的关键。配合前面说的断点续传查询数据不会因重复运行而膨胀。程序主流程把解析好的 DataFrame 逐行插入这张表。5. 数据清洗与典型分析场景5.1 缺失值与异常值的识别处理爬下来的数据基本能直接用但偶尔会出现个别城市某天数据缺失的情况。遇到该情况我一般先检查网络重试两次重试还拿不到就跳过并记录日志。在最终分析阶段对缺失值少的用前后日期的平均值填充缺失率超过 20% 的城市直接剔除。判断代码如下df[date] pd.to_datetime(df[date]) df_pivot df.pivot_table(indexcity_name, columnsdate, valuesvalue) missing_ratio df_pivot.isnull().mean(axis1) valid_cities missing_ratio[missing_ratio 0.2].index.tolist() df_clean df[df[city_name].isin(valid_cities)]5.2 从爬取结果到可视化图表绘制春运迁徙热力图数据入库后最有成就感的环节是可视化。我把 2025 年春运期间全国 TOP20 热门迁入城市画成了热力图X 轴是日期Y 轴是城市颜色深浅表示迁徙指数高低。热力图能直观看出返乡高峰、返工高峰对应的日期和城市分布。绘制热力图使用 seaborn 库import seaborn as sns import matplotlib.pyplot as plt df_heatmap pd.pivot_table( df_clean[df_clean[direction] in], indexcity_name, columnsdate, valuesvalue ).fillna(0) plt.figure(figsize(16, 10)) sns.heatmap(df_heatmap, cmapYlOrRd, linewidths0.5, linecolorwhite) plt.title(2025年春运迁入城市热力图) plt.tight_layout() plt.savefig(chunyun_heatmap.png, dpi150)运行后输出的 PNG 图基本可以直接用于汇报。个人实际体验是这步的可视化输出在整个项目里最有成就感。前期的接口分析、断点续传这些枯燥环节在看见一张清楚的迁徙热力图时都值了。5.3 迁移路线网络图用数据讲清“从哪来到哪去”除热力图外还能做迁徙路线网络图。以某个城市为中心画出它迁入迁出 TOP10 城市的连线图线的粗细表示迁徙规模。用 networkx 库做这类图很方便核心代码如下import networkx as nx G nx.DiGraph() top_cities df_clean[df_clean[direction] in].nlargest(15, value) for _, row in top_cities.iterrows(): G.add_edge(row[dest_name], 成都, weightrow[value]) pos nx.spring_layout(G, k2.0) weights [G[u][v][weight] / 10 for u, v in G.edges()] nx.draw_networkx(G, pos, widthweights, with_labelsTrue, node_colorlightblue) plt.savefig(migration_network.png, dpi150)这类图特别适合分析“一个城市对哪些地区有辐射吸引力”比单纯看数字直观得多。比如成都的迁入来源主要是重庆、北京、上海、深圳等城市画成网络图后能发现明显的区域经济圈效应。6. 项目实战完整抓取流程实录6.1 实际抓取过程中的控制台输出实录我实际跑了一次完整抓取覆盖 2025 年 1 月 14 日至 2 月 22 日共 40 天每天取全国热门 50 城。控制台输出节选[2025-01-14 00:03:25] 正在抓取 北京 - 迁入数据日期 2025-01-14 [2025-01-14 00:03:28] 北京 迁入数据获取成功共 326 条记录 [2025-01-14 00:03:31] 正在抓取 上海 - 迁出数据日期 2025-01-14 [2025-01-14 00:03:34] 上海 迁出数据获取成功共 298 条记录 [2025-01-14 00:03:37] 正在抓取 广州 - 迁入数据日期 2025-01-14 ... [2025-01-14 00:47:12] 当日 50 个城市全部抓取完成共入库 15642 条记录40 天数据跑完最终 SQLite 数据库里一共入库约 62 万条记录。文件大小约 45MBSQLite 处理起来依然游刃有余。整个流程耗时大约 8 小时含随机延时和偶尔重试完全可以接受。6.2 单日数据量评估与请求频次控制按单城市单日数据量评估一个城市会有大约 300 条迁入记录和 300 条迁出记录。50 个城市就是 3 万条。40 天累计确实达到百万级别但 SQLite 完全顶得住。请求频次我用的是随机延时 2 到 4 秒实测一天跑下来不会被封。如果你有更大的数据需求建议保持这个节奏不要贪快。实际工作中我试过把延时加到 1 秒跑到 200 个城市左右触发过服务端限流延时调整到 2 秒以上后没有再遇到类似情况。6.3 抓取报告的最终产出与结果展示抓取完成后我用 pandas 做了一次汇总分析。2025 年春运迁徙规模指数最高的一天是 2 月 4 日正月初七返工高峰迁徙规模指数达到 9.8几乎是平时均值的 3 倍。迁入量最大的城市是成都、广州、北京迁出量最大的城市是北京、上海、深圳。这些结论和官方发布的春运大数据基本吻合侧面验证了爬取数据的可靠性。7. 常见问题与排查技巧7.1 问题排查速查表问题现象可能原因解决方案请求返回 400 错误缺少 Referer 头请求头中加入 Referer: https://qianxi.baidu.com/请求返回 403 错误触发反爬机制增加延时降低请求频率轮换 UA返回 JSON 中 data 为 null日期格式错误确认日期必须是 YYYY-MM-DD不能带时间中文乱码编码不一致确保 response.encoding utf-8个别城市数据一直为空小城市未被收录换大城市测试数据源本身覆盖不全7.2 反爬虫机制应对与频率控制策略百度迁徙的反爬强度属于中等水平比电商平台松但比普通新闻网站严。“中等水平”体现在它有基础的 Referer 校验和频率检测但没有复杂的验证码和登录门槛。应对策略按优先级排序第一模拟真实浏览器请求头。重点是 User-Agent 和 Referer这两个必须带。第二控制频率。每个请求间隔随机 2 到 4 秒单日单 IP 控制在 2 万次以内的请求量。第三错峰运行。白天高峰期访问量本来就大晚上 11 点到凌晨 6 点抓取成功率最高响应也最快。我实际测试过按这个节奏连续抓了几天没有遇到封 IP 的情况。如果被临时限流停 10 到 15 分钟再继续IP 基本能恢复正常。7.3 网络波动导致的断点续传实战处理批量任务中最容易翻车的是网络抖动。家里的宽带偶尔会断一下程序抛 ConnectionError 异常直接退出。我最初的代码没做异常处理一断就得从零开始跑。后来重构成断点续传模式整个韧性大幅提升。实现思路是在 SQLite 中记录已完成的任务插入数据前先查重。程序异常退出后直接重启脚本脚本会自动跳过已完成的城市和日期只抓剩下的数据。断点续传模式除了应对网络波动发生电脑休眠、误关终端等情况时同样有效。另外一个技巧是任务记录与业务数据分离。用单独一张表记录任务状态业务数据表只存原始数据两条线互不干扰排查问题时清晰得多。8. 项目扩展方向从迁徙数据到多源招聘数据爬取百度迁徙这个项目跑通后爬虫的核心方法论就通了。之后再看到别的数据抓取需求基本就是换接口、换参数、换解析逻辑。这里顺带提两个典型的扩展方向。8.1 从迁徙到招聘智联招聘数据的爬取思路招聘网站和百度迁徙在架构上有相似性都是前端 JS 渲染、后端 API 返回 JSON。抓智联招聘数据的核心逻辑和抓迁徙数据一样先抓包看接口再模拟请求最后清洗入库。区别在于智联招聘需要携带 Cookie 和更完整的请求头返回的数据结构也更复杂解析时要多写几层逻辑。8.2 不同数据源的爬虫代码封装思路跑的项目多了会发现写爬虫 70% 的工作是重复的发请求、带请求头、解析 JSON、存库。把这些公共逻辑抽象成工具函数能大幅提高效率。我习惯把代码拆成三层请求层fetch.py统一处理请求发送、重试、延时。解析层parser.py针对不同数据源写独立的解析函数。存储层storage.py统一数据入库、去重、查询。三层结构的好处是数据源切换时只需改动解析层请求层和存储层完全复用。比如从百度迁徙切换到智联招聘只需要新增一个 parse_zhaopin() 函数主流程一行不改。9. 实操中的经验总结这个项目真正让我觉得有价值的地方不是“抓到了数据”这个结果而是把整个流程完整走了一遍。从分析接口到断点续传再到 SQLite 存储每一步都是在实际需求驱动下不断打磨出来的。回过头看有几个决定对项目影响最大用 SQLite 而不是 CSV 存储这个决定至少帮我省了一天的重复劳动。用断点续传代替一次性抓取让脚本能在无人值守的情况下稳定跑完 8 小时。控制请求频率而不是追求速度让整个项目从头到尾没遇到过严重的封禁问题。如果你准备复现这个项目建议从单城市单日期开始比如北京 2025 年 1 月 28 日把整个链路走通后再扩展规模。不要一上来就全量抓取那种情况下遇到问题很难定位。等单城逻辑稳定后再逐步加上城市循环、日期循环、断点续传这些功能模块每一步都有验证出错了也知道去哪里排查。这个方向后续还能扩展的空间很多。比如接入自动化调度工具每周定时抓取增量数据做长期迁徙趋势监测。或者把迁徙数据和天气、航班数据结合起来做综合分析。用爬虫把数据拿到手只是第一步真正有价值的是基于数据的分析和洞察后续探索的想象空间才更大。本文还有配套的精品资源点击获取
返回列表