尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python爬虫实战:从API解析到数据存储的完整流程

Python爬虫实战:从API解析到数据存储的完整流程 1. 从零到一为什么选择运满满作为爬虫入门项目很多朋友在学Python爬虫时总感觉无从下手。网上的教程要么是抓取静态网页的“Hello World”要么是直接上难度搞反爬复杂的电商网站中间缺少一个既能练手、又有实际数据价值的过渡项目。今天我就拿“运满满”这个国内知名的货运信息平台来当例子带你走一遍完整的爬虫实战流程。选择运满满有几个很实在的理由。首先它的数据是动态更新的货运信息本身就具备很强的时效性和商业参考价值学会抓取这类数据对你理解物流、供应链甚至区域经济都有帮助。其次它的页面结构相对清晰既有一定的JavaScript动态加载内容适合练习处理Ajax请求又不是那种防护到牙齿的网站对新手比较友好。最后通过这个项目你能一次性串联起爬虫的核心环节请求发送、数据解析、反爬应对、数据存储甚至还能初步接触数据清洗。这比单纯抓取一篇新闻文章要实用得多。我接下来要分享的不仅仅是一段能跑的代码更会拆解每一步背后的逻辑为什么用requests而不用urllib遇到动态加载的数据该怎么找接口拿到数据后如何高效地存下来过程中有哪些坑是我踩过的这些经验才是从“照着抄”到“自己写”的关键。2. 环境搭建与核心工具选型构建你的爬虫工作台工欲善其事必先利其器。爬虫项目的环境搭建核心是选择一个趁手的“武器库”。这里没有唯一答案但我会给出一个经过大量实战验证、稳定且高效的组合。2.1 Python版本与包管理强烈建议使用Python 3.8 或更高版本。3.8之后的版本在异步编程、数据类型提示等方面更加成熟社区支持也最好。避免使用Python 2.x其已停止维护且很多新库不再支持。包管理工具pip是标准答案。为了环境隔离务必使用虚拟环境。我个人的习惯是使用venv它内置于Python 3.3无需额外安装。# 创建虚拟环境 python -m venv venv_ymm # 激活虚拟环境 (Windows) venv_ymm\Scripts\activate # 激活虚拟环境 (MacOS/Linux) source venv_ymm/bin/activate # 激活后命令行提示符前会出现 (venv_ymm)表示已进入该环境2.2 核心库安装与作用解析在激活的虚拟环境中安装我们所需的库。不要一次性安装所有“可能用到”的库按需引入保持环境干净。pip install requests lxml pandas我们来拆解一下这三个库的角色requests: HTTP库的“事实标准”。它的API设计极其人性化发起GET/POST请求就像调用函数一样简单。相比于Python自带的urllibrequests自动处理了连接池、会话保持、SSL验证等底层细节让我们能更专注于业务逻辑。在爬虫中它负责“伸手去要”数据。lxml: 一个高性能的HTML/XML解析库。为什么选它而不是同样流行的BeautifulSoup核心在于速度和灵活性。lxml底层是C语言实现解析大型或复杂的HTML文档时速度优势明显。它支持XPath和CSS选择器两种定位方式XPath尤其强大可以编写非常精确和复杂的路径表达式来定位元素。BeautifulSoup通常搭配lxml作为解析器后端使用但对于我们这种以提取结构化数据为目标的项目直接使用lxml的XPath更直接高效。pandas: 数据分析的瑞士军刀。爬虫的终点不是把数据抓下来而是能用起来。pandas的DataFrame数据结构是处理表格型数据的利器。我们可以轻松地将爬取的列表数据转换为DataFrame进行清洗、去重、筛选然后一键导出为CSV或Excel文件无缝对接后续的数据分析或可视化步骤。2.3 辅助工具浏览器开发者工具这是爬虫工程师最重要的“眼睛”。以Chrome浏览器为例按F12打开开发者工具我们最常用的是两个面板Network (网络): 用于监听浏览器发出的所有请求。当你在运满满网站点击搜索、翻页时这里会记录下所有的XHRAjax请求、图片、脚本等。我们寻找数据接口主要就靠它。Elements (元素): 用于查看网页的DOM结构。我们可以在这里查看某个数据比如货主电话在HTML中的具体位置和标签属性从而编写对应的XPath或CSS选择器。一个关键技巧在Network面板中勾选“Preserve log”保留日志这样在页面跳转或刷新时之前的请求记录不会被清空方便我们追踪翻页等操作触发的请求。3. 目标分析与请求构造找到数据的大门直接打开运满满的官网搜索一批货运信息。你会发现列表数据并非一次性全部加载在初始HTML里而是随着你滚动或点击翻页动态加载出来的。这是现代Web应用的典型做法意味着数据通常通过额外的API接口Ajax请求获取。3.1 定位数据接口打开浏览器开发者工具的Network面板并勾选“Preserve log”。在运满满网站进行一次货运信息搜索例如从上海到北京。观察Network面板在请求类型中筛选“XHR”或“Fetch”。你会看到一系列请求其中很可能包含名字类似list.json、search、或者带有明显参数如startCityendCity的请求。逐个点击这些请求在“Preview”或“Response”标签页查看其返回内容。如果你看到了结构化的JSON数据里面包含了货运列表、货物信息、价格等那么恭喜你找到了真正的数据接口。注意网站接口可能随时变更。你需要根据实际情况寻找最新的接口地址和参数格式。本文以常见的查找思路为例具体接口URL和参数名需你现场分析。假设我们找到了一个接口其返回的JSON结构大致如下{ “code”: 200, “data”: { “list”: [ { “id”: “123456”, “startCity”: “上海市” “endCity”: “北京市” “cargoName”: “电子产品” “weight”: “10” “truckType”: “厢式货车” “publishTime”: “2023-10-27 14:30:00” “contact”: “张先生” // ... 可能还有其他字段 }, // ... 更多数据 ], “total”: 150 } }3.2 模拟浏览器请求找到接口后我们需要用Python的requests库来模拟这个请求。关键点在于请求头Headers。网站服务器会通过Headers中的一些字段来判断请求是否来自真实的浏览器从而决定是否返回数据。从开发者工具中找到你刚才定位到的那个数据接口请求点击它查看“Headers”面板。我们需要复制其中关键的几项到我们的代码中最常见且必需的有User-Agent: 标识客户端浏览器和操作系统。没有它服务器可能直接拒绝请求或返回错误页面。Referer: 表示请求是从哪个页面发起的。对于有页面来源校验的接口这个字段必须正确设置。Cookie: 维持会话状态。如果网站需要登录才能查看数据那么Cookie就是你的“通行证”。首次获取可能需要模拟登录但像运满满的部分列表数据可能不需要。我们可以这样构造请求import requests # 从浏览器开发者工具中复制过来的Headers headers { ‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36’, ‘Referer’: ‘https://www.ymm56.com/’ # 替换成实际的来源页 ‘Accept’: ‘application/json, text/plain, */*’, ‘Accept-Language’: ‘zh-CN,zh;q0.9,en;q0.8’, # 如果接口需要可能还需要添加 Authorization、X-Requested-With 等 } # 接口URL和参数需要根据实际分析填写 url ‘https://api.ymm56.com/xxx/list’ # 示例URL非真实地址 params { ‘startCity’: ‘上海’ ‘endCity’: ‘北京’ ‘pageNum’: 1 ‘pageSize’: 20 # 可能还有时间戳、签名等参数 } # 发送GET请求 response requests.get(url, headersheaders, paramsparams) # 检查请求是否成功 if response.status_code 200: # 通常接口返回JSON格式数据 data_json response.json() print(“请求成功获取到数据。”) # 打印一下数据结构看看 print(data_json) else: print(f“请求失败状态码{response.status_code}”) print(response.text)运行这段代码如果你能成功打印出包含货运列表的JSON数据那么最艰难的一步——找到并叩开数据之门——就已经完成了。4. 数据解析与提取从JSON到结构化信息成功拿到JSON响应后接下来的任务就是从这堆嵌套的字典和列表中把我们关心的字段如起始地、目的地、货物、重量、发布时间等精准地提取出来并整理成规整的表格形式。4.1 解析JSON结构response.json()方法已经帮我们把返回的文本转换成了Python的字典dict或列表list对象。现在需要像剥洋葱一样一层层找到目标数据。根据之前假设的JSON结构数据列表位于data_json[‘data’][‘list’]路径下。我们需要遍历这个列表对每一条货运信息进行提取。# 接续上面的代码假设 data_json 已成功获取 if data_json.get(‘code’) 200: # 判断接口业务状态码 cargo_list data_json[‘data’][‘list’] extracted_data [] # 准备一个空列表用来存放每条提取后的数据 for cargo in cargo_list: # 提取每个字段使用 .get() 方法可以避免 KeyError item { ‘运单ID’: cargo.get(‘id’), ‘起始城市’: cargo.get(‘startCity’), ‘目的城市’: cargo.get(‘endCity’), ‘货物名称’: cargo.get(‘cargoName’), ‘货物重量(吨)’: cargo.get(‘weight’), ‘所需车型’: cargo.get(‘truckType’), ‘发布时间’: cargo.get(‘publishTime’), ‘联系人’: cargo.get(‘contact’), # 可以继续添加其他你感兴趣的字段 } extracted_data.append(item) print(f“共提取了 {len(extracted_data)} 条货运信息。”) # 打印前两条看看效果 for i, data in enumerate(extracted_data[:2]): print(f“第{i1}条: {data}”) else: print(f“接口返回异常: {data_json.get(‘message’, ‘未知错误’)}”)4.2 使用Pandas进行数据整理将提取出来的字典列表直接转换成Pandas DataFrame后续的处理和导出会变得异常简单。import pandas as pd # 假设 extracted_data 是上一步得到的列表 df pd.DataFrame(extracted_data) # 查看数据前5行和基本信息 print(“数据预览”) print(df.head()) print(“\n数据信息”) print(df.info()) print(“\n数据形状行数 列数”, df.shape)现在df就是一个标准的DataFrame对象。你可以用它做很多事情数据清洗处理缺失值df.fillna()、去重df.drop_duplicates()、转换数据类型例如将重量字符串转为数值df[‘货物重量(吨)’].astype(float)。数据分析快速进行分组统计df.groupby(‘起始城市’).size()、筛选df[df[‘货物重量(吨)’] 5]。数据导出一键保存为文件。4.3 应对可能的HTML解析场景虽然我们通过API拿到了干净的JSON数据但作为爬虫入门练习有必要了解另一种更常见的情况数据直接嵌在HTML中。这时就需要用到lxml和XPath。假设某个字段比如一个联系电话被加密或隐藏在HTML的某个span标签里而API返回的JSON中没有我们就需要去解析初始的HTML页面。from lxml import etree # 假设我们通过另一个请求获取了包含部分详细信息的HTML页面 detail_html requests.get(‘某个详情页URL’ headersheaders).text # 使用 lxml 解析 HTML tree etree.HTML(detail_html) # 使用XPath定位元素。XPath可以通过浏览器开发者工具直接复制。 # 在Elements面板右键点击目标元素 - Copy - Copy XPath phone_xpath ‘//*[id“detail”]/div[2]/div[1]/span[2]/text()’ phone_element tree.xpath(phone_xpath) if phone_element: phone_number phone_element[0].strip() print(f“提取到的电话{phone_number}”) else: print(“未找到电话信息。”)XPath使用心得浏览器复制的XPath往往很长且脆弱依赖于固定的层级位置。更好的做法是观察目标元素的特征如独特的id、class、>total_items data_json[‘data’][‘total’] # 总数据条数 page_size 20 # 每页条数从参数或返回数据中获取 total_pages (total_items page_size - 1) // page_size # 向上取整计算总页数 print(f“总数据量{total_items} 每页{page_size}条 共{total_pages}页。”)5.2 实现翻页循环将之前的单次请求代码封装成一个函数然后在一个循环中调用它。import time def fetch_one_page(page_num): “”“抓取指定页码的数据”“” params[‘pageNum’] page_num # 修改页码参数 try: response requests.get(url, headersheaders, paramsparams, timeout10) response.raise_for_status() # 如果状态码不是200抛出HTTPError异常 data_json response.json() if data_json.get(‘code’) 200: return data_json[‘data’][‘list’] else: print(f“第{page_num}页接口业务错误{data_json.get(‘message’)}”) return [] except requests.exceptions.RequestException as e: print(f“第{page_num}页请求失败{e}”) return [] except ValueError as e: print(f“第{page_num}页响应解析JSON失败{e}”) return [] # 初始化一个列表用于存放所有页的数据 all_cargo_list [] # 假设我们只抓取前5页作为演示 for page in range(1, 6): print(f“正在抓取第 {page} 页...”) page_data fetch_one_page(page) if page_data: all_cargo_list.extend(page_data) # 将本页数据添加到总列表 print(f“第 {page} 页抓取成功获得 {len(page_data)} 条数据。”) else: print(f“第 {page} 页抓取失败或无数据。”) # 非常重要的礼貌性延迟避免请求过快给服务器造成压力也是规避反爬的基础措施 time.sleep(2) # 休眠2秒 print(f“所有页面抓取完成总共获得 {len(all_cargo_list)} 条数据。”)关键点解释函数封装将抓取单页的逻辑写成函数使代码更清晰、可复用。异常处理网络请求充满不确定性超时、连接错误、服务器返回非JSON格式等。使用try...except进行包裹确保即使某一页失败程序也不会崩溃并能继续尝试下一页。延迟time.sleep这是网络爬虫的“基本礼仪”。不加节制的高频请求会占用服务器资源容易被识别为恶意攻击并封禁IP。在页间加入随机延时例如time.sleep(random.uniform(1, 3))是更友好的做法。6. 数据存储与持久化让数据落地生根数据抓取到内存Python列表中只是第一步必须将其持久化保存到文件或数据库才算完成任务。这里介绍两种最常用的方式CSV文件和SQLite数据库。6.1 保存为CSV文件CSV逗号分隔值是一种通用、简单的表格数据存储格式可以用Excel、Numbers或文本编辑器直接打开。Pandas使其保存变得极其简单。# 假设我们已经将所有数据解析并放入了 extracted_data 列表字典格式 # 然后转换为DataFrame df_all pd.DataFrame(extracted_data) # 这里的extracted_data应包含所有页的数据 # 保存为CSV文件 csv_filename ‘运满满货运数据_上海到北京.csv’ df_all.to_csv(csv_filename, indexFalse, encoding‘utf-8-sig’) # indexFalse不保存行索引 print(f“数据已保存至文件{csv_filename}”)参数说明indexFalse: 不将DataFrame的索引0,1,2...写入文件。encoding‘utf-8-sig’: 使用带BOM的UTF-8编码可以确保在Windows系统的Excel中打开时中文字符正常显示不会乱码。6.2 保存到SQLite数据库如果需要更复杂的查询、或数据量很大SQLite是一个轻量级、无需单独安装服务器的数据库选择。Python内置了sqlite3模块。import sqlite3 # 连接到数据库如果不存在则创建 conn sqlite3.connect(‘cargo_data.db’) cursor conn.cursor() # 创建数据表 create_table_sql “”“ CREATE TABLE IF NOT EXISTS cargo_info ( id TEXT PRIMARY KEY, start_city TEXT, end_city TEXT, cargo_name TEXT, weight REAL, truck_type TEXT, publish_time TEXT, contact TEXT ) “”“ cursor.execute(create_table_sql) # 准备插入数据的SQL语句 insert_sql “”“ INSERT OR REPLACE INTO cargo_info (id, start_city, end_city, cargo_name, weight, truck_type, publish_time, contact) VALUES (?, ?, ?, ?, ?, ?, ?, ?) “”“ # 遍历数据并插入 for item in extracted_data: # 确保数据顺序和字段类型与SQL语句中的占位符 ? 一一对应 data_tuple ( item.get(‘运单ID’), item.get(‘起始城市’), item.get(‘目的城市’), item.get(‘货物名称’), float(item.get(‘货物重量(吨)’, 0)) if item.get(‘货物重量(吨)’) else None, # 转换为浮点数 item.get(‘所需车型’), item.get(‘发布时间’), item.get(‘联系人’) ) cursor.execute(insert_sql, data_tuple) # 提交事务并关闭连接 conn.commit() conn.close() print(“数据已成功保存至SQLite数据库 cargo_data.db”)使用SQLite的好处是你可以随时用SQL语句进行复杂的查询例如“查找从上海出发重量大于5吨的所有电子产品运输需求”这比在CSV文件里用Pandas筛选更直观也便于后续与其他系统集成。7. 反爬虫策略应对与伦理边界任何向公众提供数据的网站都会有一定程度的反爬虫措施。作为学习者我们在练习时务必遵守规则把握尺度。7.1 常见反爬手段及应对思路请求头校验我们已经通过添加User-Agent、Referer等进行了模拟。IP限制与封禁这是最常用的手段。如果短时间内从一个IP发出大量请求服务器会将其封禁。应对使用time.sleep()在请求间加入随机延时如1-5秒降低请求频率。这是最基础、最必要的礼貌。进阶对于大规模采集可能需要使用代理IP池来轮换IP。但作为入门练习强烈不建议初学者立即尝试这涉及成本、稳定性和法律风险。请求参数签名/加密有些接口的params或data里会有一些看似随机的字符串如sign、token、timestamp这些是客户端用特定算法生成的用于验证请求的合法性。应对需要分析网站前端JavaScript代码找到生成这些参数的算法并用Python复现。这是爬虫中技术难度较高的部分需要一定的JS逆向能力。验证码当行为被判定为异常时可能会弹出验证码。应对对于简单图形验证码可以使用OCR库如ddddocr、tesseract尝试识别对于复杂验证码如滑块、点选通常需要借助第三方打码平台或考虑停止采集。7.2 爬虫伦理与法律风险这是比技术更重要的一课。遵守robots.txt访问网站域名/robots.txt查看该网站是否允许爬虫抓取相关路径。这是互联网的“君子协定”。尊重版权与数据所有权抓取的数据仅用于个人学习、研究或公益目的。切勿用于商业用途、公开传播或对原网站造成竞争性损害。控制访问频率像之前强调的使用延时避免对目标网站服务器造成显著负担DDoS攻击效果。你的爬虫不应该影响网站的正常用户访问。查看网站服务条款很多网站在用户协议中明确禁止自动化抓取数据。敏感信息规避对于明显属于个人隐私的信息如详细的联系方式、身份证号等即使能抓到也应主动规避不予收集。对于“运满满”这类商业平台其数据具有商业价值。本教程仅以技术学习为目的演示爬虫的基本原理和流程。在实际操作中务必保持极低的请求频率并且一旦达到学习目的就应立即停止。大规模、高频次的采集不仅不道德还可能面临法律风险。8. 项目总结与扩展思考走完这一整套流程你已经完成了一个完整的、贴近真实场景的爬虫小项目。我们来回顾一下核心技能点并看看还能如何扩展。8.1 核心流程复盘环境准备建立虚拟环境安装requests、lxml、pandas这“三剑客”。目标分析使用浏览器开发者工具Network面板找到真实的数据API接口分析其请求URL、参数Params和请求头Headers。请求模拟用requests库携带正确的Headers和Params模拟浏览器发送HTTP请求获取JSON格式的响应数据。数据解析解析JSON使用Python字典操作或Pandas提取所需字段转换为结构化的数据列表。翻页处理分析翻页参数变化规律通过循环构建翻页请求并礼貌性地加入延时time.sleep。数据存储将最终的数据列表使用Pandas保存为CSV文件或使用sqlite3存入SQLite数据库。反爬应对主要通过模拟浏览器Headers和请求间延时来应对基础反爬始终牢记爬虫伦理。8.2 项目扩展方向掌握了基础之后你可以尝试挑战更复杂的功能让这个项目更像一个“产品”增加数据字段尝试去解析货运信息的详情页补充更多字段如货物体积、运费、货主公司等。多路线抓取将起始城市和目的城市参数化写一个循环抓取多个不同城市对的货运信息。定时抓取与更新使用schedule或APScheduler库让脚本每天定时运行将新数据追加到数据库或文件中形成历史数据积累。简单数据分析用Pandas或matplotlib对抓取的数据做初步分析。例如哪个线路的货运需求最旺盛哪种车型的需求量最大货物重量的分布情况如何构建简单Web展示使用Flask或Streamlit快速搭建一个本地Web应用将数据库里的数据以表格或图表的形式展示出来。最后也是最重要的体会爬虫技术是一把双刃剑。它强大的数据获取能力背后是相应的技术责任和法律责任。在学习和练习的过程中请始终将尊重数据来源、控制访问影响、遵守法律法规放在首位。把这个项目当作理解Web通信、数据流和Python自动化处理的敲门砖你的收获会远比单纯拿到一堆数据要多得多。
返回列表