从零开始学Python爬虫与数据分析:一条高效实战路线
最近总看到有人问:“想学 Python 搞爬虫和数据分析,是不是得先把语法书从第一章啃到最后一章?”我的建议是:不用。爬虫和数据分析是 Python 两个最典型的“用着学”场景,你把一个请求发出去、拿回数据、用 pandas 清洗、再画一张图,这一圈跑通,比背十遍“列表和元组的区别”都管用。
这条学习路线适合两类人。第一类是真零基础,之前没写过 Python,想直奔爬虫和数据分析,不想在无关知识点上耗时间;第二类是写过一点脚本,但遇到“请求超时、网页结构变了、中文乱码、Excel 输出打不开”就卡住的人。下面我按一条可执行的路径走:先准备环境,再速通 Python 最常用的语法,然后写一个能跑通的爬虫脚本,把抓到的数据交给 pandas 清洗、统计、可视化,最后把脚本升级成带重试、限速和日志的批量任务。整个过程不需要高配硬件,普通办公电脑就够。
先给结论:Python 做爬虫和数据分析,核心门槛不在电脑配置,而在三件事——环境能不能一次装好、请求能不能稳定发出去、数据清洗逻辑能不能经得住真实脏数据。这篇文章会把这三件事逐个拆开,并且每个环节都会给验证方法。遇到问题,直接看后面的排查清单。
1. Python 核心能力速览(爬虫 + 数据分析)
| 能力项 | 说明 |
|---|---|
| 技术方向 | 爬虫(requests + BeautifulSoup)、数据分析(pandas + matplotlib) |
| 硬件门槛 | 普通 CPU 电脑即可,GPU 不是必需 |
| 支持系统 | Windows / macOS / Linux 均可 |
| 运行环境 | Python 3.9 及以上,建议使用 venv 虚拟环境 |
| 核心依赖 | requests、beautifulsoup4、lxml、pandas、matplotlib、openpyxl |
| 启动方式 | 命令行运行脚本,配合 VS Code 或 PyCharm 开发 |
| 接口能力 | 可调用目标站点 API;也可用 FastAPI/Flask 把分析结果包装成 HTTP 服务 |
| 批量任务 | 支持,通过循环、线程池或队列实现,需加限速和重试 |
| 输出形式 | CSV、Excel、JSON、数据库、图片图表 |
| 适合场景 | 数据采集、数据清洗、报表统计、自动化数据处理 |
从这张表能看出来,它并不是一个需要“服务器级配置”的深度学习项目,而是一套低成本、强实用的技能组合。下面从环境开始,逐步搭起来。
2. 适用场景与使用边界
先说适合干什么。
爬虫部分的典型场景是:自动采集公开的商品信息、行业报告、新闻列表、公开数据集,或者把多个页面的数据汇总成一张表。数据分析部分的典型场景是:对采集到的数据做清洗、去重、缺失值处理、分组统计,最后输出图表或 Excel 报表。两者连起来,就是一个“采数据 - 洗数据 - 出结果”的完整小项目,也是很多数据相关岗位的日常缩影。
不适合什么场景也说得直白一点:如果你做的是海量级商业采集、需要分布式调度、需要登录后抓取大量个人数据,那这套入门组合只能当起点,你得考虑更严谨的采集架构、代理管理、验证码处理和存储方案。如果只是想要“回车就出分析报告”的零代码工具,不想写任何代码,那这条 Python 路线的学习成本对你来说可能偏高。
使用边界必须重点说。爬虫不是“能访问就能抓”。无论教程里给什么示例,你实际使用时都要注意:
- 只采集目标网站明确允许访问的内容,先看
robots.txt和网站服务条款。 - 不采集个人敏感信息,不抓取需要登录才能查看的非公开数据。
- 控制请求频率,不要对目标站点造成压力。
- 有官方 API 时优先走 API,而不是硬爬网页。
- 采集数据不能直接用于商业发布或二次分发,尤其涉及版权素材时要确认授权。
这不是套话。数据采集领域的法律纠纷越来越多,做技术的人更要清楚边界。文章里的示例都基于公开测试接口或本地测试页面,实际项目落地前一定要先做合规评估。
3. 环境准备与前置条件
3.1 操作系统与 Python 版本
Windows、macOS、Linux 都可以。建议安装 Python 3.9 及以上版本,安装时勾选“Add Python to PATH”,避免后面命令行找不到python命令。
先确认安装结果,打开终端或命令提示符执行:
python --version pip --version如果python不生效但python3生效,说明系统里存在多个 Python 版本,后续命令把python换成python3即可。
3.2 创建虚拟环境并安装依赖
我一直建议用虚拟环境,因为爬虫和数据分析的第三方库依赖比较集中,用 venv 隔离后,不会污染系统 Python,也不会和别的项目打架。创建和激活方式如下:
python -m venv pyenv # Windows pyenv\Scripts\activate # macOS / Linux source pyenv/bin/activate激活后命令行前面会出现(pyenv)字样,说明已经在虚拟环境里。接下来安装本文会用到的核心依赖:
pip install requests beautifulsoup4 lxml pandas matplotlib openpyxl安装过程如果超时,先确认网络情况,也可以临时切换国内镜像源。推荐把镜像源写入全局配置,省得每次手动加参数。
3.3 IDE 推荐
新手建议用 VS Code,插件装Python和Jupyter,写脚本和调试都比较方便。如果更习惯专业数据分析场景,可以装 Anaconda,里面自带了 pandas、matplotlib 和 Jupyter Notebook。用 Anaconda 的话,上面的依赖基本不用重复装。
3.4 磁盘空间与端口占用
这类学习项目依赖包体积不大,安装完成后的虚拟环境一般在几百 MB 以内,不需要担心磁盘。如果你后面要跑 FastAPI 接口服务,注意默认端口8000可能被其他服务占用,启动前先检查端口。后面排错章节会专门讲。
4. Python 基础语法速通:一周学习路径
“零基础”不等于“零语法”。但要学的不是整本语法书,而是爬虫和数据分析立刻用得上的子集。下面这套一周规划,每个阶段都配一个可验证的小任务。
| 时间 | 学习内容 | 练习目标 |
|---|---|---|
| Day 1 | 变量、数字、字符串、列表、字典 | 能写出一个保存商品信息的字典列表 |
| Day 2 | 条件判断、for 循环、while 循环 | 能遍历列表并按要求筛选数据 |
| Day 3 | 函数、异常处理、模块导入 | 能把请求逻辑封装成函数,并捕获异常 |
| Day 4 | 文件读写、CSV 处理、pip 安装 | 能把结果写入 CSV 文件 |
| Day 5 | requests 请求、响应解析 | 能请求公开接口并保存 JSON |
| Day 6 | BeautifulSoup 解析、翻页、去重 | 能跑通一个列表页抓取脚本 |
| Day 7 | pandas 清洗、统计、matplotlib 出图 | 能输出统计表和柱状图 |
4.1 变量与四种常用数据结构
爬虫返回的数据几乎都是列表和字典的组合。你不需要把 Python 所有容器类型都精通,但列表、字典、元组、集合必须会。尤其是列表推导式,写数据清洗时能省一大半代码:
nums = [1, 2, 3, 4, 5, 6] # 只需要奇数,并且计算平方 squares = [n * n for n in nums if n % 2 == 1] print(squares) # [1, 9, 25] # 字典:保存一条商品记录 product = { "title": "Python 爬虫实战", "price": 59.9, "stock": 20, } # 元组:适合保存固定结构,例如坐标、日期 point = (10, 20) # 集合:适合去重 tags = {"爬虫", "数据分析", "爬虫", "报表"} print(tags) # {'数据分析', '报表', '爬虫'}这一小段覆盖了后续脚本里 80% 的容器操作。学习时不要只看,直接在 IDE 里逐行运行,观察输出。
4.2 流程控制与函数封装
爬虫脚本里的逻辑基本是“循环遍历页面 -> 判断状态码 -> 解析字段 -> 保存结果”。把这些逻辑封装成函数,脚本会清晰很多:
def get_page(url: str) -> dict: """发送 GET 请求并返回状态码和文本""" import requests resp = requests.get(url, timeout=10) return {"status": resp.status_code, "text": resp.text} def main(): page = get_page("https://httpbin.org/get") if page["status"] == 200: print("请求成功") else: print(f"请求失败,状态码: {page['status']}") if __name__ == "__main__": main()这里的if __name__ == "__main__"是 Python 脚本的常见写法,表示只有直接运行该文件时才执行main(),被导入时不执行。这个习惯从第一天就养成,后面写工程化代码会很舒服。
4.3 异常处理:爬虫的必修课
爬虫最怕的不是语法错误,而是运行到一半网络抖动导致脚本崩溃。所以请求逻辑必须包一层try/except:
import requests from requests.exceptions import RequestException try: resp = requests.get("https://httpbin.org/get", timeout=10) resp.raise_for_status() except RequestException as e: print(f"请求异常: {e}") else: print(resp.json())raise_for_status()会在状态码是 4xx/5xx 时抛出异常,配合except就能把“请求失败导致的崩溃”变成“打印日志后继续运行”。这是后面批量任务稳定性的基础。
5. 爬虫实战:从 requests 到数据落地
5.1 第一次请求:先看状态码
requests 是 Python 里最常用的 HTTP 请求库。第一个测试用公开测试接口https://httpbin.org/get,它能原样返回我们发出的请求参数,非常适合验证环境通不通。
import requests headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0 Safari/537.36" } resp = requests.get( "https://httpbin.org/get", headers=headers, timeout=10, ) print(resp.status_code) print(resp.json())运行后看到200和一段 JSON,说明 requests 已经能正常工作。headers里的User-Agent模拟浏览器标识,这是最基础的身份声明。很多站点会拒绝没有 User-Agent 的请求,所以这个参数通常都要带。
判断成功的标准很简单:状态码为 200,resp.json()能正常解析。常见的失败是超时或 SSL 错误,前者多半是网络问题,后者会在后文排查清单里给方案。
5.2 用 BeautifulSoup 解析页面
如果目标页面返回的是 HTML 而不是 JSON,就需要解析 HTML 并提取字段。BeautifulSoup 配合 lxml 解析器是最常见的组合。下面先用一段本地 HTML 测试解析逻辑,不依赖网络:
from bs4 import BeautifulSoup html = """ <div class="item"> <h2 class="title">Python 爬虫实战</h2> <span class="price">59.9</span> </div> <div class="item"> <h2 class="title">pandas 数据分析入门</h2> <span class="price">49.0</span> </div> """ soup = BeautifulSoup(html, "lxml") items = soup.select(".item") for item in items: title = item.select_one(".title").text.strip() price = item.select_one(".price").text.strip() print(title, price)select和select_one接收 CSS 选择器,.item表示 class 为item的元素。解析前先用浏览器开发者工具确认目标元素的 class 或 id,这一步是爬虫脚本能不能写对的关键。
5.3 列表页采集并保存 CSV
把请求、解析、保存串起来,就是一个最小可用的采集脚本。下面示例是伪数据页面结构,实际使用时把 URL 和选择器替换成你自己的目标即可:
import csv import requests from bs4 import BeautifulSoup headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" } def fetch_page(url: str) -> str: resp = requests.get(url, headers=headers, timeout=10) resp.encoding = resp.apparent_encoding return resp.text def parse_page(html: str) -> list[dict]: soup = BeautifulSoup(html, "lxml") rows = [] for item in soup.select(".item"): title = item.select_one(".title").text.strip() price = item.select_one(".price").text.strip() rows.append({"title": title, "price": price}) return rows def main(): html = fetch_page("https://example.com/list") rows = parse_page(html) with open("output.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=["title", "price"]) writer.writeheader() writer.writerows(rows) print(f"已保存 {len(rows)} 条数据") if __name__ == "__main__": main()这里有一个容易被忽略的细节:写入 CSV 时用了encoding="utf-8-sig"。如果直接用utf-8,生成的 CSV 用 Excel 打开时中文可能乱码。utf-8-sig会在文件开头写入 BOM,Excel 能正确识别。
5.4 分页采集与去重
真实站点通常有多页数据,翻页规律一般是 URL 里的页码参数,比如?page=1、?page=2。写循环时建议把每一页的解析结果先放在一个总列表里,最后再一次去重和保存:
all_rows = [] for page in range(1, 6): url = f"https://example.com/list?page={page}" html = fetch_page(url) rows = parse_page(html) all_rows.extend(rows) # 去重:按 title 去重,保留最后一次结果 unique_rows = {row["title"]: row for row in all_rows}.values()字典的 key 唯一性可以用来去重,{row["title"]: row}会保留同名商品的最后一次记录。如果想去重时保留第一条,可以用更完整的 set 判断逻辑。这个技巧在数据量不大时非常实用,不需要引入额外依赖。
6. 数据分析实战:pandas 清洗与可视化
6.1 读取数据并检查质量
采集完的数据往往带着空值、重复值、类型错乱等问题。pandas 的第一件事,不是马上算均值,而是先看数据长什么样:
import pandas as pd df = pd.read_csv("output.csv") print(df.info()) print(df.head()) print(df.describe())df.info()展示每列的非空数量和数据类型。看到price列是 object 而不是 float,说明价格字段里混入了非数字内容,比如“暂无报价”这类文本。这是真实数据最常见的问题。
6.2 数据清洗典型操作
# 删除标题为空的行 df = df.dropna(subset=["title"]) # 价格转为数值,无法转换的置为 NaN df["price"] = pd.to_numeric(df["price"], errors="coerce") # 处理缺失价格,用均值填充或直接置 0 df["price"] = df["price"].fillna(0) # 去掉完全重复的行 df = df.drop_duplicates() # 按分组统计 result = df.groupby("category")["price"].agg(["count", "mean", "sum"]) print(result)判断清洗是否成功的方法:df.info()里price列类型变成float64,非空数量等于总行数,drop_duplicates后行数明显减少。只有这步通过,后续统计才有意义。
6.3 输出 Excel 报表
把统计结果导出 Excel,是很多业务场景的需求。需要提前装openpyxl,导入时就引入 Excel 支持:
with pd.ExcelWriter("report.xlsx", engine="openpyxl") as writer: result.to_excel(writer, sheet_name="分类汇总") df.to_excel(writer, sheet_name="明细", index=False)一个 Excel 工作簿里放两个 sheet:明细和汇总。注意明细表导出时用index=False,不然会多出一列索引,业务方打开后会奇怪“第一列怎么是 0 1 2”。
6.4 matplotlib 可视化
图表能直接暴露数据异常。比如你想看每个分类的商品数量分布:
import matplotlib.pyplot as plt df["category"].value_counts().plot(kind="bar") plt.title("各类别商品数量分布") plt.xlabel("类别") plt.ylabel("数量") plt.tight_layout() plt.savefig("category.png", dpi=150) print("图表已保存")tight_layout()用来避免标签被截断,dpi=150保证导出图片清晰度。如果中文标签乱码,是因为 matplotlib 默认字体不含中文字符。解决方案是显式指定一个支持中文的字体,或者只在代码阶段用英文标签,分析报告时再替换为中文。
7. 批量任务、API 接口与工程化
7.1 从单页脚本升级为批量任务
批量任务要解决三个问题:重试、限速、失败记录。直接用for循环可以跑,但一旦目标站点限流,脚本很容易跑一半中断。下面是一个带重试的批量请求模板:
import time import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session = requests.Session() # 重试策略:最多重试 3 次,退避因子 1 秒,遇到 500/502/503 时重试 retry = Retry( total=3, backoff_factor=1, status_forcelist=[500, 502, 503], ) adapter = HTTPAdapter(max_retries=retry) session.mount("http://", adapter) session.mount("https://", adapter) urls = [ "https://httpbin.org/status/200", "https://httpbin.org/status/500", "https://httpbin.org/status/200", ] for url in urls: try: resp = session.get(url, timeout=10) print(url, resp.status_code) except requests.RequestException as e: print(f"失败: {url}, {e}") time.sleep(1) # 限速:每请求间隔 1 秒Session会复用底层连接,比每次新建连接更快。Retry会自动处理临时故障,backoff_factor=1表示重试前等待时间为 1 秒、2 秒、4 秒递增。time.sleep(1)是主动限速,给目标站点留出处理空间。这里用httpbin.org/status/500演示失败情况,注意实际运行时该 URL 会返回 500 状态并触发重试逻辑。
判断批量任务是否健壮的标准:遇到 500 后脚本不会崩,能自动重试并继续后续任务;如果最终仍然失败,会在日志里留下记录,方便补采。
7.2 用 API 代替硬爬
能调 API 就不要硬爬网页。API 返回的是结构化 JSON,请求稳定、解析简单、对服务器压力也小。下面是调用一个 JSON 接口并用 pandas 直接转成 DataFrame 的通用模板:
import requests import pandas as pd resp = requests.get( "https://httpbin.org/json", headers={"User-Agent": "Mozilla/5.0"}, timeout=10, ) resp.raise_for_status() data = resp.json() # 如果返回的是列表,直接转 DataFrame df = pd.DataFrame(data.get("slideshow", {}).get("slides", [])) print(df.head())httpbin.org/json是一个公开测试接口,返回固定的 JSON 结构。实际项目中把 URL 换成你的目标接口,再根据返回结构调整取值路径即可。判断接口调用是否成功的标准:resp.status_code为 200,data能正常解析,df不为空。
7.3 用 FastAPI 包装分析结果
如果希望把“爬取 + 分析”的结果提供给其他系统使用,可以用 FastAPI 包一层 HTTP 服务。这一步不是必须,但能让你理解“数据分析结果怎么变成接口服务”的完整链路。
pip install fastapi uvicorn创建一个app.py:
from fastapi import FastAPI import pandas as pd app = FastAPI() @app.get("/summary") def summary(): df = pd.read_csv("output.csv") result = df.groupby("category")["price"].agg(["count", "mean"]).to_dict() return {"total_rows": len(df), "by_category": result}启动服务:
uvicorn app:app --host 127.0.0.1 --port 8000启动后访问http://127.0.0.1:8000/summary就能看到 JSON 格式的统计数据。这个服务只监听本地地址,避免暴露到公网。如果要把服务部署到服务器,需要额外考虑访问控制,不要让任何人都能触发你的数据读取接口。
8. 资源占用与性能观察
8.1 观察 CPU 和内存
爬虫和分析脚本是 CPU 密集型任务,显存完全用不上。性能观察的重点是:
- 内存:如果一次把几百万行数据全部读入 pandas,内存会飙升。处理大文件时用
pd.read_csv(..., chunksize=10000)分批读取。 - CPU:解析大量 HTML 时,
lxml解析器速度明显快于标准库html.parser,这是我们在前面就指定"lxml"的原因。 - 网络:爬虫瓶颈通常在网络等待,而不是 CPU。这也是为什么批量任务里可以用
Session复用连接提升效率。
8.2 如何降低资源占用
数据量大时,优先做三件事:只保留需要的列、尽早过滤不需要的行、先采样验证逻辑再全量跑。比如读取时直接指定列:
df = pd.read_csv("output.csv", usecols=["title", "price", "category"])这样可以减少内存占用。如果分析对象是上 GB 的日志文件,建议学一下dtype参数和 chunk 分块处理,这已经属于进阶内容,但入门阶段先记住“能早过滤就早过滤”这个原则。
8.3 端口冲突与进程残留
用 FastAPI 启动服务时,如果端口被占用,启动会报Address already in use。排查方式很简单:
# Linux / macOS lsof -i :8000 # Windows netstat -ano | findstr 8000找到占用进程后,可以换一个端口启动uvicorn app:app --port 8001,或者结束占用进程。不建议直接kill -9一个不了解的进程,先确认进程归属再处理。
9. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
pip安装超时 | 默认源访问不稳定 | 查看报错 URL 与网络状态 | 切换国内镜像源并写入全局配置 |
requests请求超时 | 网络波动或目标站点限制 | 加 timeout 参数、打印状态码 | 重试、延长超时、降低请求频率 |
| 返回内容中文乱码 | 编码识别错误 | 打印resp.encoding | 手动指定resp.encoding = "utf-8"或按页面实际情况调整 |
pandas读 CSV 报错 | 分隔符或编码不一致 | 用文本编辑器查看文件头几行 | pd.read_csv(..., sep=",", encoding="utf-8-sig") |
| Excel 打开 CSV 乱码 | 写入时未用带 BOM 编码 | 用记事本看文件编码 | 写入时使用encoding="utf-8-sig" |
| 翻页时数据重复 | 目标页面存在重复推广位 | 检查每页结果数量 | 解析后统一按标题去重 |
| 批量任务跑到一半卡住 | 未设置重试和超时 | 查看日志和重试次数 | 使用 Session + Retry + 单请求超时 |
| 接口服务启动失败 | 端口被占用 | 检查端口占用情况 | 换端口或结束冲突进程 |
| 图表中文显示为方块 | matplotlib 默认字体不含中文 | 查看运行环境字体列表 | 手动设置支持中文的字体名称 |
遇到问题不要先想着“加反爬绕过”,优先检查自己的代码逻辑和请求合规性。很多“被限制”其实是请求频率太高或者缺少基本请求头导致的,先把限速和请求头补齐。
10. 最佳实践与合规建议
工程化能力不是第一天就有的,但可以从第一次写脚本时就培养。下面几条是我自己在项目里一直坚持的做法:
- 第一轮先小范围测试。不要直接跑一万个 URL,先跑 5 个,确认解析和保存逻辑没问题,再全量执行。
- 目录分清楚。建议建三个目录:
input/放 URL 清单,output/放原始结果,report/放分析和图表。后面找数据、重新生成报表会方便很多。 - 每个脚本都要写日志。用 Python 的
logging模块替代print,方便批量任务排错。 - 批量任务要支持断点续跑。能通过记录“已成功处理的 URL”来跳过已完成项,避免失败重跑时重复请求。
- 接口服务不要监听公网地址。没有认证的读取接口暴露到公网,轻则被扫描器刷流量,重则数据泄露。
- 涉及人脸、声音、版权素材、个人数据时,必须先确认授权。爬虫采集的内容不能想当然地认为可以商用。
- 发布分析结果前,人工复核一遍。图表数字可能因为清洗逻辑错误而失真,数据看板的“看起来对”不等于“真的对”。
这些建议在学习和个人项目阶段够用。等你要做正式的商业采集服务,还需要更完整的权限控制、数据脱敏和审计机制。
11. 总结与下一步
这条 Python 爬虫 + 数据分析路线的价值在于:它用最少的概念,把你从“完全不会写代码”带到“能跑通一个采集、清洗、出图、出报表的完整流程”。建议你先按顺序跑通前三件事:环境安装、requests 请求、pandas 读文件。这三个跑通,后面就是不断替换 URL、选择器和字段名的熟练工作。
最容易踩的坑有三个:一是环境没装好就开始写代码,最后乱在不熟悉的部分;二是爬虫请求不设超时和重试,网络一抖脚本就崩;三是清洗逻辑没验证就出报表,数字错了还往上交。这三个坑,这篇文章都给了对应的验证方法和排查方案。
下一步可以按自己的兴趣选方向:想深入采集,可以学 Scrapy 框架、分布式采集、数据去重和增量更新;想深入分析,可以学 SQL、数据可视化进阶、统计分析,甚至机器学习入门。把今天这套最小闭环跑熟,你后面学任何方向都会更快。
建议把这份学习路线收藏备用。遇到问题时,回来看第三章的环境检查、第五章的爬虫模板和第九章的排查表格,大部分入门问题都能直接定位。
