一、回顾与本篇目标
上一篇我们学了文件读写,你的 Python 程序可以把数据持久化到硬盘上了。这一篇,我们要学一个让 Python 声名大噪的技能——网络爬虫。
爬虫,通俗地说就是让程序自动访问网页,把上面的数据抓取下来。比如自动抓取新闻标题、批量下载图片、监控商品价格变化、收集招聘信息。Python 是爬虫领域的王者语言,因为它的语法简洁、网络请求库强大、HTML 解析工具丰富。
如果你之前跟着《后端零基础入门》系列学过 Node.js,你可能记得后端服务是接收请求然后返回数据。爬虫正好反过来了——它是发出请求,然后解析别人返回的数据。
本篇的目标:
- 理解 HTTP 请求和响应的基本概念(爬虫的底层逻辑)
- 安装并使用
requests库发送 GET 和 POST 请求 - 学会用
BeautifulSoup解析 HTML 并提取数据 - 处理翻页,抓取多页数据
- 设置请求头,伪装成浏览器
- 写出第一个完整的爬虫脚本
二、爬虫的底层逻辑:HTTP 请求和响应
在你写爬虫代码之前,先理解爬虫到底在干什么。这和你在浏览器里访问网页是同一件事。
当你在浏览器地址栏输入 https://www.example.com 并按回车,浏览器做了这些事:
- 向服务器发送一个 HTTP 请求(Request)。
- 服务器收到请求后,返回一个 HTTP 响应(Response)。
- 浏览器解析响应中的 HTML 代码,渲染成你看到的网页。
爬虫做的事情完全一样——只不过把浏览器换成了 Python 程序。Python 程序发送请求,拿到服务器返回的 HTML,然后从中提取出你需要的数据。
一个 HTTP 请求包含几个关键信息:
- URL:你要访问哪个网页(如
https://www.example.com/news)。 - 请求方法:最常见的是 GET(获取数据)和 POST(提交数据)。
- 请求头(Headers):附加信息——用的什么浏览器、接受什么格式的数据等。
- 请求体(Body):只有 POST 请求才有,是提交给服务器的数据。
一个 HTTP 响应包含:
- 状态码:200 表示成功,404 表示页面不存在,500 表示服务器出错。
- 响应头:服务器返回的附加信息。
- 响应体:服务器返回的实际内容——HTML 代码、JSON 数据等。
爬虫本质上就是:组装请求 → 发送 → 接收响应 → 解析响应体 → 提取数据。
三、安装 requests 库
Python 自带的 urllib 也能发 HTTP 请求,但用起来比较繁琐——需要手动构建请求对象、设置编码、处理异常。requests 是第三方库,把这些繁琐的细节全部封装好了,代码量减少一半以上。它是 Python 爬虫的事实标准。
在虚拟环境中安装:
pip install requests
验证安装:
python -c "import requests; print(requests.__version__)"
如果输出版本号,说明安装成功。
四、你的第一个爬虫:获取网页内容
import requests
# 发送 GET 请求
response = requests.get('https://www.example.com')
# 状态码
print(f'状态码:{response.status_code}')
# 响应头
print(f'内容类型:{response.headers["Content-Type"]}')
# 响应体(HTML 代码)
print(f'网页内容(前 200 个字符):{response.text[:200]}')
逐行解释:
requests.get(url):发送一个 GET 请求到指定的 URL。这是爬虫中最常用的操作。response.status_code:HTTP 状态码。200 表示请求成功,404 表示页面不存在。response.headers:服务器返回的响应头,是一个字典。通过'Content-Type'键可以知道服务器返回的是什么类型的内容。response.text:响应体的文本内容(通常是 HTML 代码)。它是 Python 字符串,你可以对它做任何字符串操作——切片、查找、替换。response.content:响应体的二进制内容(用于下载图片、视频等非文本数据)。
几个常用的 response 属性:
| 属性 | 含义 | 示例 |
|---|---|---|
response.status_code |
HTTP 状态码 | 200、404、500 |
response.text |
响应体的文本内容 | HTML 字符串 |
response.content |
响应体的二进制内容 | 图片、视频的字节数据 |
response.json() |
把 JSON 响应体解析成 Python 字典 | API 返回的 JSON 数据 |
response.headers |
响应头字典 | response.headers['Content-Type'] |
response.encoding |
响应的编码 | 'utf-8' |
response.url |
最终请求的 URL | 可能和请求时的 URL 不同(发生了重定向) |
处理请求异常
网络请求可能失败——服务器挂了、网络断了、URL 写错了。一个健壮的爬虫需要处理这些异常:
import requests
url = 'https://www.example.com'
try:
response = requests.get(url, timeout=10) # 设置超时时间
response.raise_for_status() # 如果状态码不是 200,抛出异常
print('请求成功')
print(response.text[:200])
except requests.exceptions.Timeout:
print('请求超时,请检查网络连接')
except requests.exceptions.ConnectionError:
print('连接失败,请检查 URL 是否正确')
except requests.exceptions.HTTPError as e:
print(f'HTTP 错误:{e}')
except requests.exceptions.RequestException as e:
print(f'请求出错:{e}')
关键点:
timeout=10:设置超时时间(秒)。如果服务器 10 秒内没响应,就放弃等待。永远不要省略超时设置,否则程序可能永远卡住。response.raise_for_status():如果 HTTP 状态码是 4xx 或 5xx,自动抛出异常。比手动检查状态码更简洁。
五、解析 HTML:BeautifulSoup
requests 只能拿到网页的原始 HTML 代码——一大坨字符串。要从里面提取出具体的数据(比如所有新闻标题、所有图片地址),需要解析 HTML。
BeautifulSoup 是 Python 中最流行的 HTML 解析库。它能把 HTML 字符串变成一棵可遍历的树,让你像用 CSS 选择器一样提取元素。
安装 BeautifulSoup
pip install beautifulsoup4
BeautifulSoup 还需要一个解析器来实际分析 HTML。推荐 lxml,速度快:
pip install lxml
基本用法
from bs4 import BeautifulSoup
# 假设这是我们从网页上拿到的 HTML
html = '''
欢迎来到我的网站
第一篇文章
这是第一篇文章的摘要
第二篇文章
这是第二篇文章的摘要
'''
# 创建 BeautifulSoup 对象
soup = BeautifulSoup(html, 'lxml')
# 1. 获取标题标签
print(soup.title) #
print(soup.title.text) # '测试页面' —— 只取文字内容
# 2. 获取 h1 标签
print(soup.h1.text) # '欢迎来到我的网站'
# 3. 获取所有链接
for link in soup.find_all('a'):
print(link['href'], link.text) # /post/1 第一篇文章 /post/2 第二篇文章
# 4. 按类名查找
articles = soup.find_all('div', class_='article')
print(len(articles)) # 2
逐行解释:
BeautifulSoup(html, 'lxml'):把 HTML 字符串解析成一棵文档树。'lxml'是解析器的名字。soup.title:通过标签名直接访问,拿到第一个匹配的标签。类似 JavaScript 的document.querySelector('title')。标签.text:获取标签内部的纯文本内容,去掉所有 HTML 标签。soup.find_all('a'):找到所有<a>标签,返回一个列表。类似 JavaScript 的document.querySelectorAll('a')。标签['href']:访问标签的属性,和字典的键访问语法一样。class_='article':注意class后面跟了一个下划线。因为class是 Python 的关键字,BeautifulSoup 用class_来避免冲突。
BeautifulSoup 的四种查找方式
| 方法 | 作用 | 返回 |
|---|---|---|
soup.find('标签名') |
查找第一个匹配的标签 | 单个标签对象 或 None |
soup.find_all('标签名') |
查找所有匹配的标签 | 列表 |
soup.select('CSS选择器') |
用 CSS 选择器查找 | 列表 |
soup.select_one('CSS选择器') |
用 CSS 选择器查找第一个 | 单个标签对象 或 None |
如果你熟悉前端开发,推荐用 select() 和 select_one()——直接写 CSS 选择器,和 document.querySelectorAll() 完全一致:
# CSS 选择器方式(前端开发者更熟悉)
soup.select('.article') # 类选择器
soup.select('#main') # ID 选择器
soup.select('div.article > h2 > a') # 后代选择器
soup.select('a[href^="/post"]') # 属性选择器
六、实战:爬取一个真实网页
下面我们用所学知识,写一个完整的爬虫——抓取一个示例网站的文章标题和链接。
import requests
from bs4 import BeautifulSoup
# 目标:抓取一个提供示例文章的网站
url = 'https://books.toscrape.com/'
# 发送请求
response = requests.get(url, timeout=10)
response.raise_for_status()
# 解析 HTML
soup = BeautifulSoup(response.text, 'lxml')
# 找到所有书籍
books = soup.select('.product_pod')
print('===== 书籍列表 =====')
for book in books[:10]: # 只取前 10 本
# 书名在 img 标签的 alt 属性中
title = book.select_one('img')['alt']
# 价格在 .price_color 标签中
price = book.select_one('.price_color').text
print(f'《{title}》 - {price}')
print(f'\n共找到 {len(books)} 本书')
代码解析:
soup.select('.product_pod'):用 CSS 类选择器找到所有书籍容器。每个.product_pod包含一本书的信息。book.select_one('img')['alt']:在每本书的容器内,找到<img>标签,提取alt属性的值作为书名。book.select_one('.price_color').text:找到价格标签,提取其中的文本内容。
七、处理翻页:抓取多页数据
大多数网站的数据不是一页展示完的,需要翻页。爬虫需要自动构造每一页的 URL 并依次抓取。
翻页通常有两种模式:
- URL 中有页码参数:如
?page=1、?page=2。直接用 for 循环改页码就行。 - 下一页按钮:从页面中找到“下一页”的链接,提取它的 URL,继续抓取。
我们演示第二种——更通用:
import requests
from bs4 import BeautifulSoup
base_url = 'https://books.toscrape.com/catalogue/'
current_url = base_url + 'page-1.html'
page_count = 0
max_pages = 5 # 限制最多抓 5 页,防止爬太猛
while current_url and page_count < max_pages:
page_count += 1
print(f'\n===== 第 {page_count} 页 =====')
# 发送请求
response = requests.get(current_url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'lxml')
# 抓取当前页的书籍
books = soup.select('.product_pod')
for book in books[:5]:
title = book.select_one('img')['alt']
price = book.select_one('.price_color').text
print(f' 《{title}》 - {price}')
# 找到“下一页”按钮
next_button = soup.select_one('.next > a')
if next_button:
# 提取下一页的相对路径,拼成完整 URL
next_href = next_button['href']
current_url = base_url + next_href
else:
current_url = None # 没有下一页了,停止循环
print(f'\n抓取完成,共抓取 {page_count} 页')
关键逻辑:
current_url保存当前要抓取的页面 URL,每抓完一页就更新为下一页的 URL。soup.select_one('.next > a'):查找“下一页”的链接。.next > a是 CSS 选择器,表示“类名为 next 的元素下的直接子元素 a”。- 如果找不到“下一页”链接,
next_button是None,循环自然结束。 max_pages设置了抓取上限——实际写爬虫时要控制抓取速度,不要给目标服务器造成太大压力。
八、设置请求头:伪装成浏览器
有些网站会检测访问者是不是爬虫,如果是就拒绝访问。最简单的反爬措施是检查 User-Agent 请求头——它告诉服务器“我是什么浏览器”。
requests 默认的 User-Agent 是 python-requests/2.x.x,等于直接告诉服务器“我是爬虫”。我们可以把它改成浏览器的 User-Agent:
# 不伪装(服务器一眼就看出你是爬虫)
# response = requests.get(url)
# 伪装成浏览器
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',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
response = requests.get(url, headers=headers, timeout=10)
这个 User-Agent 是从哪里来的? 打开你浏览器的开发者工具(F12),切换到 Network 标签,随便点一个请求,在 Request Headers 里就能找到你自己的 User-Agent,直接复制过来用。
除了 User-Agent,有时候还需要加其他头信息(如 Referer、Cookie),具体要看目标网站的要求。
九、发送 POST 请求
有些数据不是直接展示在页面上的,需要先提交表单才能获取——比如登录后才能看到的数据、搜索框里输入关键词后返回的结果。
POST 请求就是用来提交数据的。以下是一个模拟登录的示例(使用一个专门用于测试的网站):
import requests
# 目标:一个专门用于测试 POST 请求的网站
url = 'https://httpbin.org/post'
# POST 请求的数据
data = {
'username': '张三',
'password': '123456',
'remember': 'on'
}
# 发送 POST 请求
response = requests.post(url, data=data, timeout=10)
# 查看响应(这个网站会把收到的数据原样返回)
result = response.json()
print(f'发送的数据:{result["form"]}')
如果目标 API 接受 JSON 格式的数据,用 json 参数代替 data 参数:
# 发送 JSON 数据(目标 API 需要 JSON 格式时使用)
response = requests.post(url, json={'key': 'value'}, timeout=10)
json 参数会自动把字典转成 JSON 字符串,并设置 Content-Type: application/json 请求头。
十、下载图片等二进制文件
爬虫不只是抓文本,也经常需要下载图片、PDF、视频等二进制文件。这些数据不能用 response.text(那是给文本用的),要用 response.content:
import requests
image_url = 'https://www.python.org/static/img/python-logo.png'
response = requests.get(image_url, timeout=10)
response.raise_for_status()
# 用 'wb' 模式写入二进制数据
with open('python_logo.png', 'wb') as file:
file.write(response.content)
print('图片下载完成')
注意:写入文件时用 'wb' 模式(二进制写),不能用 'w'(那是文本写)。图片数据是二进制字节,不是可读的文字字符。
十一、爬虫礼仪:做一个负责任的爬虫
写爬虫是学技术,但用爬虫要讲规矩。几点基本的道德准则:
- 遵守 robots.txt:在网站根目录(如
https://www.example.com/robots.txt)有一个文件,规定了哪些路径不允许爬虫访问。尊重它。 - 不要高频请求:每次请求之间加一个间隔(
time.sleep(1)暂停一秒)。短时间内发送大量请求可能被视为攻击,导致你的 IP 被封。 - 不要爬取版权内容:文章、图片、视频都有版权。爬取公开数据用于学习和研究一般没问题,但不要爬取付费内容或未经授权分发的数据。
- 遵守网站的条款:有些网站在用户协议中明确禁止爬虫。如果网站有 API 接口,优先使用 API 而不是爬取网页。
十二、综合演示:完整的爬虫脚本
下面这个脚本综合运用了本篇学到的所有知识——发送请求、解析 HTML、处理翻页、伪装浏览器、异常处理、数据保存。它把抓取到的书籍信息保存为 JSON 文件:
import requests
from bs4 import BeautifulSoup
import json
import time
def scrape_books(max_pages=3):
"""抓取书籍信息,返回书籍列表"""
base_url = 'https://books.toscrape.com/catalogue/'
current_url = base_url + 'page-1.html'
all_books = []
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
page = 0
while current_url and page < max_pages:
page += 1
print(f'正在抓取第 {page} 页...')
try:
response = requests.get(current_url, headers=headers, timeout=10)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f'请求失败:{e}')
break
soup = BeautifulSoup(response.text, 'lxml')
books = soup.select('.product_pod')
for book in books:
title = book.select_one('img')['alt']
price = book.select_one('.price_color').text
# 获取评分(class 属性包含 'star-rating' 的 p 标签)
rating_tag = book.select_one('p[class^="star-rating"]')
rating = rating_tag['class'][1] if rating_tag else '未知'
all_books.append({
'title': title,
'price': price,
'rating': rating
})
# 找下一页
next_button = soup.select_one('.next > a')
if next_button:
current_url = base_url + next_button['href']
time.sleep(1) # 礼貌等待一秒
else:
current_url = None
return all_books
# 运行爬虫
if __name__ == '__main__':
books = scrape_books(max_pages=3)
print(f'\n共抓取 {len(books)} 本书')
# 保存为 JSON
with open('books.json', 'w', encoding='utf-8') as file:
json.dump(books, file, ensure_ascii=False, indent=2)
print('数据已保存到 books.json')
# 显示前 5 本
print('\n前 5 本书:')
for book in books[:5]:
print(f' 《{book["title"]}》 - {book["price"]} - {book["rating"]}星')
十三、本篇动手练习
练习 1:抓取新闻标题
找一个你经常看的新闻网站首页,用 requests + BeautifulSoup 抓取页面上所有的新闻标题和链接。打印出来,把结果保存为 CSV 文件。
练习 2:抓取天气数据
搜索“城市名 天气”,找一个天气网站,查看页面的 HTML 结构。用爬虫抓取当天天气(温度、湿度、风力等),打印出来。
练习 3:批量下载图片
找一个包含多张图片的网页(比如壁纸网站或摄影网站),用爬虫获取所有图片的 URL,然后批量下载到本地文件夹。提示:图片 URL 通常在 <img> 标签的 src 属性中。
练习 4:模拟搜索
找一个搜索引擎(或用之前学过的示例网站),用 POST 请求模拟搜索——提交关键词,解析搜索结果页面,打印搜索到的结果。
十四、本篇小结
这一篇你学会了 Python 爬虫的核心技能:
- 爬虫的本质:程序模拟浏览器发送 HTTP 请求,解析服务器返回的 HTML,提取数据。
requests库:发送 GET/POST 请求、获取响应文本和二进制内容、处理异常。必须设置timeout。BeautifulSoup:解析 HTML,用find()、find_all()、select()提取元素,访问标签属性和文本内容。前端开发者最熟悉select()配合 CSS 选择器。- 处理翻页:通过“下一页”按钮的链接自动拼接 URL,用 while 循环连续抓取。
- 伪装浏览器:设置 User-Agent 请求头,让爬虫看起来像普通浏览器。
- 下载文件:用
response.content获取二进制数据,用'wb'模式写入文件。 - 爬虫礼仪:控制请求频率、遵守 robots.txt、不爬取版权内容。
爬虫是 Python 的杀手锏技能。从这一篇开始,你已经能用 Python 做那些 JavaScript 做起来不太方便的事情了——自动抓取数据、批量下载文件、监控网页变化。下一篇,我们进入数据分析领域,学习用 Pandas 处理表格数据。
下一篇预告
下一篇——《数据分析入门——用 Pandas 处理表格数据》:安装 Pandas、读取 CSV/Excel 文件、数据筛选和排序、分组统计、合并多个数据源、简单的数据可视化。这是 Python 在数据科学领域最强大的能力之一。
Python 零基础入门,每周更新。











暂无评论内容