爬虫零基础入门:Requests、BeautifulSoup与数据提取全流程

发布时间:2026/8/30 1:22:32
爬虫零基础入门:Requests、BeautifulSoup与数据提取全流程 动手写爬虫的第一天很多人会卡在同一个位置照着文档把requests.get写出来了网页内容也打印出来了然后呢看到一屏密密麻麻的 HTML不知道下一步该做什么。另一部分人更早放弃他们想抓的页面一访问就返回 403于是以为爬虫需要什么“高深技巧”转头去搜各种看起来很厉害的库结果越学越乱。我对爬虫的判断很简单它不是一个需要“通天神技”的黑客工具而是一项面向公开数据的自动化技能。你要掌握的无非是三个环节——发请求拿到响应、从响应里解析出目标信息、把结果存下来。把这三步跑通你已经超过了大多数“收藏过教程但没动手”的初学者。这篇文章就按这个最小闭环来写。全程不讨论绕过登录、突破频率限制等灰色操作只讲公开数据、公开测试接口和常规技术原理。看完之后你至少能写出第一个可以稳定运行的爬虫并且知道后面该往哪几个方向深入。建议收藏备用适合零基础、刚接触 Python、学过语法但没做过项目、想系统梳理爬虫流程的读者。1. 这篇文章真正要解决的问题很多人学爬虫不是因为缺教程而是因为教程太碎。有人在讲 Requests有人在讲 BeautifulSoup还有人在讲 Scrapy每个单独看都能看懂但合在一起就懵了我到底该先学哪个为什么我按某个教程写了跑出来的结果和它不一样爬虫真正的学习难点不在某一个库而在“串起来”的能力。你需要明白一次完整的爬取会发生什么再考虑用什么工具。这里的核心链路只有四步构造请求告诉目标服务器“我要访问哪个页面”。获取响应服务器把 HTML、JSON 等数据返回给你。解析数据从返回内容中提取你真正需要的字段。保存结果把数据写到 CSV、JSON、数据库或 Excel。只要按这个链路走一遍后面所有的高级话题——增量抓取、分布式、反爬、数据清洗——都能找到自己的位置。读这篇文章你会得到三条明确收益掌握requests、BeautifulSoup、lxml这几个最常用的爬虫库知道什么时候用谁。跑通一个“请求 → 解析 → 保存”的完整项目而不是只复制片段。具备排查问题的基本思路遇到 403、乱码、动态页面时知道先查什么。如果你已经有 Python 基础可以直接跳到最后几章看工程化和合规注意事项如果你是零基础建议按顺序读每段代码都动手敲一遍。2. 爬虫的核心概念与分类2.1 什么是网络爬虫网络爬虫的定义并不神秘一段按照预定规则自动请求网页、再从中提取有用数据的程序。你手动在浏览器里打开网页、复制标题、粘贴到 Excel这个动作重复一万次就是爬虫在做的事。可以把爬虫理解成一个“自动化的信息收集员”。它不会凭空创造数据只是将网页上已经公开的信息用更快的速度、更整齐的格式收集起来。这也是爬虫能合法存在的基本前提你抓取的目标对象最好是你有权限访问的公开数据而不是通过漏洞或绕过手段才能拿到的内容。2.2 静态页面与动态页面刚开始写爬虫最先遇到的区分是静态页面和动态页面。静态页面服务器返回的 HTML 里直接包含你想要的文字和图片。你查看网页源代码就能看到数据。动态页面服务器先返回一个空壳 HTML数据由 JavaScript 在浏览器里再请求接口填充。你直接请求这个页面拿到的是空结构。这个区分决定了你用requests直接抓还是需要借助浏览器自动化工具。很多入门者抓不到数据不是因为写错了代码而是连目标页面的类型都没分清。2.3 批量型、增量型、垂直型爬虫从工程角度看爬虫可以分成三类类型特点典型场景批量型爬虫一次性把目标数据抓完整迁移数据、导出历史资料增量型爬虫只抓新增或变化的部分新闻聚合、价格监控、行情同步垂直型爬虫只抓某个行业或领域的结构化数据商品信息、招聘信息、学术文献对新手来说不用一开始就把三类都学完。你只需要先实现一个能跑的批量抓取在此基础上再加入“上次抓到哪了”的记录就能逐步升级成增量型爬虫。垂直型爬虫更看重业务理解比如你知道电商页面里哪个字段是价格、哪个字段是库存爬虫才有意义。2.4 一次爬取的完整流程把刚才的流程再展开一点构造请求 → 服务器返回响应 → 状态码判断 → 内容解析 → 数据清洗 → 存储 → 巡检和异常处理。注意真实项目里“状态码判断”和“异常处理”很重要。你访问一个不存在的页面会得到 404访问频率太快会得到 403服务器出问题会得到 500。好的爬虫不是一访问到底而是会根据状态码决定是否继续、重试或放弃。3. 环境准备与开发工具3.1 安装 Python建议安装 Python 3.8 及以上版本具体版本号以官方发布为准。Windows 用户在安装时记得勾选“Add Python to PATH”否则后面在命令行里执行python会报“不是内部或外部命令”。安装完成后打开命令行执行python --version如果能输出版本号说明 Python 环境没有问题。3.2 选择 IDE新手可以选择 PyCharm Community 版或 VS Code二者都免费。PyCharm 对项目结构和调试更友好VS Code 更轻量。这里不强行推荐哪个关键是先用熟一个。如果只是做练习也可以用 Python 自带的 IDLE但我不太建议因为代码一多没有代码提示和调试器会很痛苦。3.3 安装爬虫依赖库本文后续会用到的库有requests、beautifulsoup4、lxml。先全部装上pip install requests beautifulsoup4 lxml国内网络环境下如果 pip 下载慢可以临时换成国内镜像源例如pip install requests beautifulsoup4 lxml -i https://pypi.tuna.tsinghua.edu.cn/simple镜像源只是下载加速不会改变库的版本和用法。3.4 验证环境安装完成后在命令行执行python -c import requests; print(requests.__version__)如果正常输出版本号说明环境已经就绪。看到这里你可能会问不需要装 Scrapy 吗先不用。Scrapy 是框架适合规模化爬取但它把请求、解析、管道都封装好了直接学容易让人困惑。先用requests把原理彻底搞懂再学 Scrapy 会非常快。4. HTTP 基础与 Requests 请求4.1 HTTP 请求与响应爬虫和服务器之间的交流走的是 HTTP 协议。可以把它想象成一次点餐你提交一份请求菜单服务器返回一份响应菜品。请求里通常包含请求方法GET 表示获取资源POST 表示提交数据。URL你要访问的地址。请求头例如 User-Agent告诉服务器你是什么客户端。请求体POST 时携带的数据。响应里通常包含状态码200 表示成功403 表示禁止访问404 表示不存在500 表示服务器内部错误。响应头包括 Content-Type、Set-Cookie 等。响应体HTML、JSON、图片二进制等具体内容。爬虫第一步就是发出请求并拿到响应。4.2 发送第一个 GET 请求下面用公开测试接口 httpbin.org 演示。这个网站专门用来测试 HTTP 请求不会涉及任何敏感内容。import requests url https://httpbin.org/get response requests.get(url, timeout10) print(状态码, response.status_code) print(响应内容, response.text)运行后你会看到状态码 200以及一段 JSON 文本。这段 JSON 就是服务器告诉你“我收到了你的 GET 请求”。如果只看表面这里很容易忽略一个关键变量timeout。不加 timeout当目标服务器没响应时你的程序会一直等下去。加一个超时时间至少能避免“卡死”这种情况。4.3 响应乱码问题很多新手在处理中文页面时会遇到乱码原因是网页声明的编码和实际返回的编码不一致。requests会根据响应头猜测编码但有时会猜错。比较稳妥的写法是import requests url https://httpbin.org/get response requests.get(url, timeout10) response.encoding utf-8 print(response.text)如果之后爬具体网站可以先看response.encoding是什么再根据页面源码里的 charset 去调整。乱码问题通常不是代码写错而是编码设置不对。4.4 设置 User-Agent 和请求头有些服务器会拒绝明显不是浏览器的请求因为默认的 User-Agent 是 Python-requests。这时你需要把请求头伪装成浏览器。import requests url https://httpbin.org/user-agent 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 ) } response requests.get(url, headersheaders, timeout10) print(response.json())注意设置浏览器 User-Agent 的目的是模拟普通访客的正常访问并不是为了绕过安全限制。真正的爬虫工程里还应该遵循网站的 robots.txt 和访问频率要求这部分在最后一章展开。4.5 异常处理与超时重试网络请求不可能永远成功。常见的异常包括连接超时、SSL 证书错误、DNS 解析失败。建议在请求外层加 try-exceptimport requests from requests.exceptions import RequestException url https://httpbin.org/get try: response requests.get(url, timeout10) response.raise_for_status() except RequestException as e: print(请求失败, e) else: print(状态码, response.status_code)这里raise_for_status()会在状态码为 4xx/5xx 时抛出异常避免你拿着错误的响应继续往下解析。真实的爬虫还会加指数退避重试第一次失败了等 1 秒第二次等 2 秒第三次等 4 秒而不是一秒内连打三次。5. 页面解析从 HTML 中提取数据5.1 为什么不直接用字符串搜索请求拿到 HTML 后常见错误是直接用它做字符串查找例如html.find(标题)。这种方式对单个字段可能有效但网页结构一旦变化代码就全废。更可靠的方式是使用解析库把 HTML 转换成可查询的树形结构然后按标签、类名、属性提取数据。5.2 BeautifulSoup 基础用法BeautifulSoup 是入门阶段最友好的解析库。它能把 HTML 解析成一棵节点树然后用find、find_all、select等方式取数据。下面用一个示例 HTML 演示不依赖任何真实网站from bs4 import BeautifulSoup html ul li classitemPython/li li classitemJava/li li classitemGo/li /ul soup BeautifulSoup(html, html.parser) items soup.select(li.item) for item in items: print(item.get_text(stripTrue))这段代码的套路是BeautifulSoup(html, html.parser)把 HTML 字符串解析成对象。soup.select(li.item)用 CSS 选择器找到所有 class 为 item 的 li 标签。get_text(stripTrue)提取标签内的文字并去掉首尾空格。新手常犯的错误是直接print(item)输出整个标签结果还带着li和属性。记住想要文字就用get_text()想要属性就用item.get(href)。5.3 正则表达式什么时候用正则不是专门的 HTML 解析工具而是通用的文本匹配工具。它适合提取不太适合用 DOM 结构定位的信息比如从一段 JS 脚本里提取某个变量的值。import re text var userId 123456; match re.search(rvar userId ([^]), text) if match: print(match.group(1))但对 HTML 复杂文档来说正则表达式容易出现匹配过头或匹配不到的问题。我的建议是能用解析库就用解析库正则只在网页里有独立文本片段时使用。5.4 XPath 与 lxml 的取舍lxml 的性能比 BeautifulSoup 好而且支持 XPath 定位。XPath 的写法和 CSS 选择器不太一样但功能类似。from lxml import html html_text ul li classitemPython/li li classitemJava/li /ul doc html.fromstring(html_text) items doc.xpath(//li[classitem]/text()) print(items)xpath 表达式//li[classitem]/text()的意思是从任意位置找到 class 为 item 的 li 标签取其文本。5.5 三种解析方式对比解析方式优点缺点推荐场景正则表达式轻量、灵活复杂 HTML 容易出错提取独立字符串片段BeautifulSoup简单直观、容错强性能稍弱静态页面、新手入门lxml XPath性能好、定位精确语法需要记忆层级复杂、批量抓取实际项目里Requests BeautifulSoup 已经能覆盖很多需求。到大数据量或复杂结构时再切换到 lxml。6. 完整实战JSON 接口抓取、HTML 解析与数据存储6.1 实战目标现在把前面的知识串成一个完整项目。目标抓取公开测试接口 JSONPlaceholder 的帖子数据提取每篇文章的 id 和 title保存到 CSV 文件。这个接口是一个公开的模拟数据服务数据本身是虚构内容适合教学不涉及真实用户隐私。6.2 编写爬虫脚本新建一个文件spider_demo.py内容如下import csv import requests url https://jsonplaceholder.typicode.com/posts response requests.get(url, timeout10) response.raise_for_status() posts response.json() with open(posts.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([id, title]) for post in posts: writer.writerow([post[id], post[title]]) print(写入完成