Python 爬虫入门:Requests 请求与 XPath 数据解析
今天开始学习 Python 爬虫。爬虫的主要工作可以简单概括为:
发送请求 → 获取网页 → 解析内容 → 提取数据
这几个步骤看起来简单,但每一步都有很多细节。今天主要学习了 requests 库、网页响应对象、HTML 节点、XPath 路径表达式以及常用 XPath 函数。
一、什么是爬虫
爬虫程序可以自动访问网页,并从网页中提取我们需要的信息。
例如:
- 获取新闻标题;
- 抓取商品名称和价格;
- 提取电影评分;
- 下载图片;
- 统计网页中的链接;
- 获取公开网页中的表格数据。
浏览器访问网页时,背后也会向服务器发送 HTTP 请求。服务器处理请求后,会返回 HTML、JSON、图片或其他内容。Python 爬虫做的事情,就是用代码模拟这种请求,再解析服务器返回的数据。
一个简单的爬虫流程如下:
准备目标网址
↓
使用 requests 发送请求
↓
获取 response 响应对象
↓
读取 HTML 内容
↓
使用 XPath 定位节点
↓
清洗并保存数据
需要注意,爬虫只能抓取公开且允许访问的内容,不能绕过登录、验证码、权限控制或网站的访问限制。
二、使用 requests 发送 GET 请求
安装 requests:
pip install requests
发送 GET 请求:
import requests
url = "https://www.example.com"
response = requests.get(url)
print(response)
response 是一个响应对象,类型通常是:
requests.models.Response
如果输出:
<Response [200]>
说明服务器正常返回了响应。
常见状态码包括:
| 状态码 | 含义 |
|---|---|
| 200 | 请求成功 |
| 301、302 | 重定向 |
| 403 | 没有权限访问 |
| 404 | 页面不存在 |
| 500 | 服务器内部错误 |
发送请求时,建议设置超时时间,避免服务器迟迟不响应:
response = requests.get(
url,
timeout=10
)
也可以先判断请求是否成功:
response.raise_for_status()
如果响应状态码是 4xx 或 5xx,raise_for_status() 会抛出异常。
三、Response 响应对象
服务器返回的内容会保存在 response 对象中。
1. response.encoding
表示响应内容使用的编码格式:
response.encoding = "utf-8"
如果网页出现中文乱码,可以尝试设置正确编码。
2. response.text
以字符串形式返回网页源码:
html = response.text
print(html)
这是解析 HTML 时最常用的属性。
3. response.content
以二进制形式返回响应内容:
content = response.content
content 适合处理图片、压缩文件或其他非文本数据:
with open("image.jpg", "wb") as file:
file.write(response.content)
4. response.url
返回最终访问的网址:
print(response.url)
当网页发生重定向时,最终 URL 可能与最初请求的 URL 不同。
5. response.status_code
返回 HTTP 状态码:
print(response.status_code)
6. response.headers
返回响应头:
print(response.headers)
响应头中可能包含内容类型、服务器信息、编码格式和缓存信息。
四、设置请求参数
如果网址中有查询参数,可以使用 params:
params = {
"page": 1,
"keyword": "Python"
}
response = requests.get(
"https://www.example.com/search",
params=params
)
print(response.url)
Requests 会自动把参数拼接到 URL 后面。
有些网站会根据请求头判断访问者是不是浏览器。可以设置 headers:
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
)
}
response = requests.get(
url,
headers=headers,
timeout=10
)
如果需要使用代理,可以通过 proxies 参数:
proxies = {
"http": "http://127.0.0.1:7890",
"https": "http://127.0.0.1:7890"
}
response = requests.get(
url,
proxies=proxies,
timeout=10
)
代理地址需要根据实际环境填写,没有代理时不需要设置。
五、POST 请求
GET 请求通常用于获取数据,POST 请求常用于提交数据。
data = {
"username": "test",
"password": "123456"
}
response = requests.post(
"https://www.example.com/login",
data=data,
timeout=10
)
如果接口接收 JSON 数据,可以使用:
payload = {
"name": "小明",
"age": 18
}
response = requests.post(
url,
json=payload,
timeout=10
)
实际爬虫中,要根据网页请求方式选择 GET 或 POST。不能看到一个网址就默认使用 GET。
六、认识 HTML 节点
网页本质上是由 HTML 标签组成的。
例如:
<html>
<body>
<div class="article">
<h1>Python 爬虫入门</h1>
<p>这是文章内容</p>
</div>
</body>
</html>
这些标签在 XPath 中叫作节点。
常见节点类型包括:
1. 元素节点
例如:
<div></div>
<h1></h1>
<p></p>
2. 属性节点
例如:
<div class="article"></div>
这里的 class="article" 就是属性。
3. 文本节点
例如:
<button>百度一下</button>
其中的“百度一下”就是文本节点。
4. 根节点
整个 HTML 文档的最外层节点通常是根节点:
<html></html>
节点之间存在层级关系:
- 父节点;
- 子节点;
- 兄弟节点;
- 根节点。
例如:
<div>
<p>第一段</p>
<p>第二段</p>
</div>
div 是两个 p 的父节点,两个 p 互相是兄弟节点。
七、使用 lxml 解析网页
Python 中常使用 lxml 配合 XPath 解析网页:
pip install lxml
基本代码如下:
import requests
from lxml import etree
url = "https://www.example.com"
response = requests.get(
url,
headers=headers,
timeout=10
)
response.encoding = "utf-8"
html = etree.HTML(
response.text
)
result = html.xpath(
"//h1/text()"
)
print(result)
etree.HTML() 会把网页源码转换成可以使用 XPath 查询的 HTML 树。
XPath 查询的结果通常是列表,即使只找到一个节点,也可能返回:
["Python 爬虫入门"]
八、XPath 的基本语法
1. 选择元素节点
/html/body/div
表示从根节点开始,依次查找 html、body 和 div。
2. 使用 //
//div
表示查找文档中任意位置的 div,不需要写出完整层级。
3. 使用 .
.
表示当前节点。
4. 使用 ..
..
表示当前节点的父节点。
5. 使用 @
//div/@class
表示获取 div 节点的 class 属性。
6. 获取文本
//button/text()
表示获取 button 节点中的文本。
例如:
titles = html.xpath(
"//h1/text()"
)
for title in titles:
print(title.strip())
九、XPath 的节点选择
可以按照标签、属性和文本选择节点:
# 选择所有div元素
html.xpath("//div")
# 选择class为article的div
html.xpath("//div[@class='article']")
# 选择所有包含class属性的div
html.xpath("//div[@class]")
# 选择button中的文本
html.xpath("//button/text()")
如果属性值包含多个 class,直接使用等号可能不够准确:
<div class="article item"></div>
可以使用 contains():
//div[contains(@class, 'article')]
在代码中:
items = html.xpath(
"//div[contains(@class, 'article')]"
)
十、XPath 运算符
XPath 中常见的路径运算符如下:
1. 单斜杠 /
/html/body/div
表示按照完整层级查找。
2. 双斜杠 //
//div
表示从当前范围内任意位置查找 div。
3. 点 .
.
表示当前节点。
4. 双点 ..
..
表示当前节点的父节点。
5. @
//a/@href
表示获取链接节点的 href 属性。
例如:
links = html.xpath(
"//a/@href"
)
for link in links:
print(link)
十一、XPath 下标和谓词
XPath 可以使用中括号进行筛选。
选择第一个 li:
//ul/li[1]
选择最后一个 li:
//ul/li[last()]
选择前四个 li:
//ul/li[position() < 5]
选择包含指定属性的节点:
//div[@class='item']
选择文本内容:
//button[text()='百度一下']
对应 Python 代码:
items = html.xpath(
"//ul/li[position() < 5]/text()"
)
print(items)
需要注意,XPath 下标从 1 开始,而 Python 列表下标从 0 开始。这是初学者经常混淆的地方。
十二、XPath 常用字符串函数
1. contains()
检查字符串是否包含指定内容:
//div[contains(@class, 'item')]
Python 示例:
nodes = html.xpath(
"//div[contains(@class, 'item')]"
)
2. starts-with()
检查字符串是否以指定内容开头:
//a[starts-with(@href, 'https')]
3. string-length()
返回字符串长度:
//p[string-length(text()) > 10]
这表示选择文本长度大于 10 的 p 节点。
这些函数适合处理 class 名称不完全固定、链接前缀相同或者文本长度不同的网页。
十三、XPath 数字函数和逻辑运算符
XPath 也支持一些数字函数:
sum()
floor()
ceiling()
例如:
sum(//span/@data-price)
可以尝试计算多个属性值的总和。
逻辑运算符包括:
and
or
not()
例如:
//div[@class='item' and @data-type='book']
表示同时满足两个条件。
//div[@class='book' or @class='movie']
表示满足其中一个条件。
//div[not(@hidden)]
表示选择没有 hidden 属性的节点。
十四、一个简单的网页提取案例
下面模拟提取网页中的文章标题和链接:
import requests
from lxml import etree
url = "https://www.example.com"
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
)
}
response = requests.get(
url,
headers=headers,
timeout=10
)
response.raise_for_status()
response.encoding = "utf-8"
html = etree.HTML(response.text)
titles = html.xpath(
"//h2[contains(@class, 'title')]/text()"
)
links = html.xpath(
"//h2[contains(@class, 'title')]/a/@href"
)
for title, link in zip(titles, links):
print({
"title": title.strip(),
"link": link
})
这里的流程是:
- 使用
requests.get()请求网页; - 使用
raise_for_status()检查请求是否成功; - 设置编码;
- 将网页源码转换为 HTML 树;
- 使用 XPath 获取文章标题;
- 使用 XPath 获取链接;
- 使用
zip()将标题和链接对应起来。
如果网页结构发生变化,XPath 也需要相应调整。
十五、爬虫中的常见问题
1. 网页乱码
可以尝试:
response.encoding = "utf-8"
有些网站的编码可以从响应头或网页源码中判断。
2. 请求超时
应该设置:
timeout=10
并使用异常处理:
try:
response = requests.get(
url,
timeout=10
)
except requests.RequestException as error:
print(f"请求失败:{error}")
3. XPath 找不到数据
首先检查:
- XPath 是否写错;
- 网页是否使用 JavaScript 动态加载;
- 节点是否位于 iframe 中;
- 返回的网页源码是否与浏览器看到的内容相同;
- class 是否包含多个值。
4. 请求被拒绝
可以检查状态码:
print(response.status_code)
也可以设置合理的 User-Agent,并降低请求频率。
十六、总结
今天学习的内容可以分成两部分。
第一部分是 requests:
get()发送 GET 请求;post()发送 POST 请求;text获取字符串形式的网页源码;content获取二进制数据;status_code获取状态码;headers获取响应头;encoding设置编码格式;url获取最终访问地址;params传递查询参数;proxies设置代理。
第二部分是 XPath:
/按照完整路径查找;//从任意位置查找;.表示当前节点;..表示父节点;@选择属性;text()获取文本;contains()检查是否包含字符串;starts-with()检查字符串开头;last()选择最后一个节点;position()按位置筛选节点;and、or、not()进行逻辑判断。
爬虫的核心并不是简单地“复制网页内容”,而是先理解网页结构,再选择合适的请求和解析方式。掌握 requests 和 XPath 后,就可以完成很多静态网页的数据提取任务。后续还可以继续学习 BeautifulSoup、正则表达式、动态网页处理,以及数据保存到 CSV、Excel 和数据库。
看到最后奖励一个后羿采集器,直接文章搜索下载就行,爬虫神器,嘿嘿嘿
更多推荐

所有评论(0)