Python网络爬虫实战项目:从基础到进阶
简介:网络爬虫是获取互联网数据的重要工具,Python凭借其简洁语法和丰富库支持,成为爬虫开发的首选语言。本文系统讲解Python网络爬虫的核心知识,涵盖HTTP/HTTPS协议基础、HTML结构解析、常用爬虫库(如Requests、BeautifulSoup、Scrapy、Selenium)、爬虫流程设计、反反爬策略以及实战案例。通过本项目学习,读者能够掌握从网页抓取到数据存储的完整流程,并具备应对复杂网页结构和反爬机制的能力。
1. 网络爬虫基础概念
1.1 网络爬虫的定义与作用
网络爬虫(Web Crawler)是一种按照一定规则,自动抓取互联网信息的程序或脚本。其核心作用在于从网页中提取结构化或非结构化数据,为后续的数据分析、搜索引擎索引、舆情监控等提供原始数据支持。
从技术角度而言,爬虫的本质是一个自动化访问 Web 服务器并解析返回内容的客户端程序。它通过 HTTP 协议向目标服务器发起请求,获取 HTML 页面、JSON 数据或其他资源,并从中提取所需信息。
随着大数据与人工智能的发展,爬虫技术已成为数据驱动型应用的基础设施之一。无论是电商比价、金融数据采集,还是搜索引擎的索引构建,都离不开高效的爬虫系统。
2. HTTP/HTTPS协议详解
在构建网络爬虫系统的过程中,理解 HTTP 和 HTTPS 协议是基础且关键的一环。HTTP(HyperText Transfer Protocol)作为互联网数据传输的标准协议,规定了客户端与服务器之间的通信规则;而 HTTPS(HTTP Secure)则在此基础上加入了加密层,保障了通信的安全性。本章将深入剖析 HTTP/HTTPS 的工作原理、安全机制、常用方法及状态码,并结合抓包工具的使用,帮助读者全面掌握爬虫开发中涉及的网络通信核心知识。
2.1 HTTP协议基础
HTTP 是客户端-服务器模型中用于传输超文本的标准协议。它定义了客户端(如浏览器或爬虫程序)如何向服务器发送请求,以及服务器如何响应这些请求。理解 HTTP 协议的结构、请求方法和状态码分类,有助于更有效地编写爬虫程序并处理网络通信中的异常。
2.1.1 HTTP请求与响应结构
HTTP 通信由请求和响应组成。客户端发送请求,服务器返回响应。每个请求和响应都有特定的结构,包括起始行、头部(Headers)和主体(Body)。
请求结构
一个 HTTP 请求通常由以下三部分组成:
- 请求行(Request Line) :包括请求方法、请求的资源路径(URI)和使用的 HTTP 版本。
- 请求头(Headers) :包含客户端发送给服务器的附加信息,如 Host、User-Agent、Content-Type 等。
- 请求体(Body) :在某些请求方法(如 POST、PUT)中,请求体包含要发送的数据。
示例请求报文:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml
响应结构
HTTP 响应同样由三部分组成:
- 状态行(Status Line) :包括 HTTP 版本、状态码和状态消息。
- 响应头(Headers) :提供服务器返回的元数据,如 Content-Type、Content-Length、Set-Cookie 等。
- 响应体(Body) :返回的实体内容,如 HTML 页面、JSON 数据等。
示例响应报文:
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2025 12:28:53 GMT
Server: Apache/2.4.1 (Unix)
Content-Type: text/html
Content-Length: 1234
<!DOCTYPE html>
<html>
<head><title>Example</title></head>
<body><h1>Hello, World!</h1></body>
</html>
请求与响应结构对比表:
| 部分 | 请求报文 | 响应报文 |
|---|---|---|
| 起始行 | 请求行(方法 URI 版本) | 状态行(版本 状态码 描述) |
| 头部 | 客户端发送的元信息 | 服务器返回的元信息 |
| 主体(Body) | 通常用于 POST/PUT 请求 | 返回的数据内容 |
参数说明:
- 请求方法 :常见方法包括 GET、POST、PUT、DELETE 等。
- URI :统一资源标识符,表示请求的目标资源路径。
- HTTP 版本 :当前多为 HTTP/1.1 或 HTTP/2。
- Host :指定请求的目标主机名。
- User-Agent :标识客户端的类型与版本。
- 状态码 :如 200 表示成功,404 表示未找到资源。
- Content-Type :告知接收方发送的数据类型。
2.1.2 常用请求方法(GET、POST、PUT、DELETE等)
HTTP 定义了多种请求方法,每种方法代表不同的操作语义。以下是几种最常用的请求方法及其用途:
常见请求方法一览表:
| 方法 | 描述 |
|---|---|
| GET | 获取指定资源。请求参数附在 URL 后面,适合获取数据。 |
| POST | 向服务器提交数据,常用于创建新资源。数据放在请求体中。 |
| PUT | 替换指定资源的全部内容。数据放在请求体中。 |
| DELETE | 删除指定资源。 |
| PATCH | 更新资源的部分内容。 |
| HEAD | 获取资源的头部信息,不返回响应体。 |
| OPTIONS | 获取服务器支持的 HTTP 方法等信息。 |
示例:使用 Python 的 requests 库发送 GET 和 POST 请求
import requests
# 发送 GET 请求
response_get = requests.get('https://api.example.com/data', params={'id': 1})
print(response_get.status_code)
print(response_get.json())
# 发送 POST 请求
data = {'username': 'test', 'password': '123456'}
response_post = requests.post('https://api.example.com/login', data=data)
print(response_post.status_code)
print(response_post.cookies)
代码逻辑分析:
-
GET 请求 :
- 使用requests.get()方法向服务器发送 GET 请求。
-params参数用于附加查询字符串到 URL。
-.json()方法将响应内容解析为 JSON 格式。 -
POST 请求 :
- 使用requests.post()方法提交数据。
-data参数用于传递表单数据。
-.cookies属性可获取服务器设置的 Cookie。
参数说明:
-
url:请求的目标地址。 -
params:GET 请求的查询参数。 -
data:POST 请求的表单数据。 -
json:用于发送 JSON 格式的数据(自动设置 Content-Type 为 application/json)。
2.1.3 状态码含义与分类(2xx、3xx、4xx、5xx)
HTTP 状态码用于表示请求的处理结果。根据状态码的不同,可以判断请求是否成功、是否需要重定向或出现错误。
状态码分类表:
| 分类 | 状态码范围 | 描述 |
|---|---|---|
| 1xx | 100-199 | 信息性状态码,表示请求已被接收,继续处理 |
| 2xx | 200-299 | 成功状态码,请求已被成功接收、理解并处理 |
| 3xx | 300-399 | 重定向状态码,客户端需进一步操作以完成请求 |
| 4xx | 400-499 | 客户端错误状态码,请求有误或无法被服务器处理 |
| 5xx | 500-599 | 服务器错误状态码,服务器无法完成合法请求 |
常见状态码示例:
| 状态码 | 描述 |
|---|---|
| 200 | 请求成功 |
| 301 | 永久重定向 |
| 302 | 临时重定向 |
| 400 | 错误请求 |
| 401 | 未授权 |
| 403 | 禁止访问 |
| 404 | 资源未找到 |
| 500 | 内部服务器错误 |
| 502 | 网关错误 |
| 503 | 服务不可用 |
示例:使用 Python 处理状态码
import requests
response = requests.get('https://api.example.com/data')
if response.status_code == 200:
print("请求成功,返回数据:", response.json())
elif response.status_code == 404:
print("资源未找到,请检查 URL 是否正确。")
elif response.status_code == 500:
print("服务器内部错误,请稍后再试。")
else:
print(f"请求失败,状态码:{response.status_code}")
代码逻辑分析:
- 使用
requests.get()发送请求。 - 使用
response.status_code获取状态码。 - 通过条件判断处理不同的状态码,进行相应的逻辑处理。
参数说明:
-
status_code:响应的状态码,类型为整数。 -
json():将响应内容解析为 JSON 格式。 -
text:获取响应的原始文本内容。
2.2 HTTPS与安全通信
随着网络安全问题日益突出,HTTPS 已成为现代 Web 通信的标准协议。HTTPS 在 HTTP 协议的基础上引入了 SSL/TLS 加密层,确保数据在传输过程中不会被窃取或篡改。理解 HTTPS 的工作原理、数字证书机制以及在 Python 中如何处理 HTTPS 请求,是构建安全爬虫系统的关键。
2.2.1 HTTPS的工作原理与SSL/TLS协议
HTTPS 的核心在于 SSL/TLS 协议提供的加密通信机制。其工作流程如下:
- 客户端发起 HTTPS 请求 :浏览器或爬虫程序向服务器发起 HTTPS 连接请求。
- 服务器返回证书 :服务器将自身的数字证书发送给客户端。
- 客户端验证证书 :客户端检查证书是否由可信的 CA(证书颁发机构)签发,是否在有效期内,域名是否匹配等。
- 密钥交换与加密通道建立 :客户端与服务器通过非对称加密交换对称密钥,之后使用该密钥进行加密通信。
HTTPS通信流程图(mermaid格式):
sequenceDiagram
participant Client
participant Server
Client->>Server: 发起 HTTPS 请求
Server-->>Client: 返回数字证书
Client->>Client: 验证证书有效性
Client->>Server: 使用公钥加密对称密钥
Server->>Client: 使用私钥解密对称密钥
Client<->Server: 使用对称密钥进行加密通信
参数说明:
- SSL/TLS :用于加密通信的协议,TLS 是 SSL 的继任者。
- 数字证书 :包含服务器的公钥和身份信息,由 CA 签发。
- 非对称加密 :使用公钥加密,私钥解密,用于安全交换对称密钥。
- 对称加密 :使用同一个密钥进行加密和解密,效率高,用于实际数据传输。
2.2.2 数字证书与加密机制
HTTPS 安全性的核心依赖于数字证书和加密算法。以下是几个关键概念:
数字证书结构:
- 颁发者(Issuer) :签发证书的 CA 名称。
- 主体(Subject) :证书所有者的身份信息,如域名。
- 有效期(Validity) :证书的起始和截止时间。
- 公钥(Public Key) :用于加密的密钥。
- 签名(Signature) :CA 对证书内容的数字签名,确保证书未被篡改。
加密机制:
- 对称加密(AES、DES) :通信双方使用相同密钥加解密,速度快。
- 非对称加密(RSA、ECC) :使用公钥加密,私钥解密,用于安全交换密钥。
- 哈希算法(SHA-256) :用于生成证书指纹,确保数据完整性。
2.2.3 使用Python处理HTTPS请求的注意事项
在使用 Python 发送 HTTPS 请求时,需要注意以下几点:
示例:使用 requests 发送 HTTPS 请求并忽略 SSL 证书验证
import requests
response = requests.get('https://self-signed.badssl.com/', verify=False)
print(response.status_code)
代码逻辑分析:
-
verify=False:禁用 SSL 证书验证,适用于测试环境或自签名证书。 - 注意 :生产环境中应避免禁用 SSL 验证,以防止中间人攻击。
示例:指定本地 CA 证书路径
response = requests.get('https://self-signed.badssl.com/', verify='/path/to/cert.pem')
print(response.status_code)
参数说明:
-
verify:指定 CA 证书路径或布尔值。默认为True,表示验证服务器证书。
异常处理示例:
try:
response = requests.get('https://invalid.example.com', timeout=5)
except requests.exceptions.SSLError as e:
print("SSL证书验证失败:", e)
except requests.exceptions.Timeout:
print("请求超时")
except requests.exceptions.RequestException as e:
print("请求异常:", e)
参数说明:
-
timeout:设置请求超时时间。 -
SSLError:SSL 证书验证失败时抛出的异常。 -
Timeout:请求超时时抛出的异常。 -
RequestException:所有请求异常的基类。
3. Requests库使用与HTTP请求发送
3.1 Requests库简介与安装
3.1.1 Requests的优势与特点
Requests 是 Python 中用于发送 HTTP 请求的第三方库,它简化了与 Web 服务的交互过程,使得开发者能够以更自然、更易读的方式进行网络请求操作。其核心优势包括:
- 简洁易用的 API :Requests 提供了简洁的函数接口,如
get()、post()等,使得代码更易维护和理解。 - 自动处理常见事务 :例如自动解码响应内容、保持会话(Session)、自动处理 Cookie、自动重定向等。
- 支持多种请求方式 :包括 GET、POST、PUT、DELETE、HEAD 和 OPTIONS。
- 良好的异常处理机制 :提供了清晰的异常类,便于错误捕获和处理。
- 支持文件上传与下载 :通过
files参数可以轻松实现文件上传,下载内容也可以直接写入文件。 - 兼容性好 :支持 Python 2.7 和 Python 3.x,广泛应用于各类网络爬虫、API 接口测试等场景。
3.1.2 安装与环境配置
安装 Requests 库非常简单,推荐使用 pip 包管理器进行安装。在终端或命令行中输入以下命令即可完成安装:
pip install requests
安装完成后,可以通过 Python 交互式环境验证是否安装成功:
import requests
print(requests.__version__)
输出版本号即表示安装成功。
在某些企业或学校网络环境下,可能需要使用代理或指定镜像源来加速安装,可以使用以下命令:
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
3.2 发送GET与POST请求
3.2.1 参数传递方式(params、data)
在实际开发中,我们经常需要向服务器传递参数。GET 请求通常将参数附加在 URL 上,而 POST 请求则将数据放在请求体中。Requests 提供了 params 和 data 两个参数来分别处理这两种情况。
GET 请求传递参数
使用 params 参数可以将字典或元组列表转换为查询字符串,并自动进行 URL 编码。
import requests
params = {
'query': 'requests tutorial',
'page': 1
}
response = requests.get('https://api.example.com/search', params=params)
print(response.url)
输出示例:
https://api.example.com/search?query=requests+tutorial&page=1
代码逻辑分析:
- params 参数将字典转换为查询字符串,附加在 URL 后面。
- response.url 展示了最终请求的完整 URL。
- Requests 自动处理了空格转义和 URL 编码。
POST 请求传递参数
POST 请求通常用于提交数据,使用 data 参数传递表单数据。
import requests
data = {
'username': 'admin',
'password': '123456'
}
response = requests.post('https://api.example.com/login', data=data)
print(response.text)
代码逻辑分析:
- data 参数用于传递表单数据,默认以 application/x-www-form-urlencoded 格式发送。
- response.text 返回服务器响应的文本内容。
3.2.2 自定义请求头与用户代理(User-Agent)
HTTP 请求头(Headers)是客户端与服务器通信时的重要元数据,其中 User-Agent 用于标识客户端浏览器类型。在爬虫中,设置合适的 User-Agent 可以避免被网站识别为机器人。
import requests
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',
'Accept-Language': 'en-US,en;q=0.9',
'Referer': 'https://www.google.com/'
}
response = requests.get('https://api.example.com/data', headers=headers)
print(response.request.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',
'Accept-Language': 'en-US,en;q=0.9',
'Referer': 'https://www.google.com/'
}
代码逻辑分析:
- headers 字典用于自定义请求头。
- response.request.headers 展示了最终发送的请求头内容。
- 设置合适的 User-Agent 可以绕过部分网站的反爬机制。
3.2.3 文件上传与下载处理
Requests 支持文件上传与下载功能,适用于处理图像、文档等二进制资源。
文件上传
使用 files 参数上传文件,适用于 multipart/form-data 格式。
import requests
url = 'https://api.example.com/upload'
file_path = 'example.txt'
with open(file_path, 'rb') as f:
files = {'file': f}
response = requests.post(url, files=files)
print(response.status_code)
print(response.text)
代码逻辑分析:
- 使用 with open() 打开文件为二进制模式。
- files 参数接受一个字典,键为表单字段名,值为文件对象。
- 服务器返回状态码和响应内容。
文件下载
下载文件时可以使用 stream=True 参数避免一次性加载大文件。
import requests
url = 'https://example.com/large_file.zip'
response = requests.get(url, stream=True)
with open('downloaded_file.zip', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print("Download completed.")
代码逻辑分析:
- stream=True 表示以流式方式下载,适用于大文件。
- iter_content() 方法按块读取响应内容。
- chunk_size=8192 指定每次读取的大小(单位为字节)。
3.3 响应处理与异常捕获
3.3.1 响应内容解析与编码处理
HTTP 响应内容通常包括文本、JSON、HTML 等格式,Requests 提供了多种方法来解析响应内容。
import requests
response = requests.get('https://api.example.com/data')
# 获取文本内容
print(response.text)
# 获取字节内容
print(response.content)
# 获取JSON数据
try:
data = response.json()
print(data)
except requests.exceptions.JSONDecodeError:
print("Response is not JSON format.")
代码逻辑分析:
- response.text :自动解码响应内容,适用于文本数据。
- response.content :原始字节流,适用于二进制数据。
- response.json() :尝试将响应内容解析为 JSON 格式,若失败则抛出异常。
3.3.2 状态码判断与重定向处理
HTTP 状态码用于表示请求的结果,常见的有 2xx(成功)、3xx(重定向)、4xx(客户端错误)、5xx(服务器错误)。
import requests
response = requests.get('https://api.example.com/data')
# 判断状态码
if response.status_code == 200:
print("请求成功")
elif 300 <= response.status_code < 400:
print("重定向中")
elif 400 <= response.status_code < 500:
print("客户端错误")
elif 500 <= response.status_code < 600:
print("服务器错误")
else:
print("未知错误")
# 查看是否发生重定向
if response.history:
print("请求发生了重定向")
for resp in response.history:
print(f"重定向到: {resp.url}")
代码逻辑分析:
- response.status_code 获取响应状态码。
- response.history 存储了所有重定向的响应对象。
3.3.3 异常类型与错误处理机制
Requests 提供了丰富的异常类,便于开发者捕获并处理不同类型的错误。
import requests
from requests.exceptions import ConnectionError, Timeout, HTTPError
try:
response = requests.get('https://api.example.com/data', timeout=5)
response.raise_for_status()
except ConnectionError:
print("连接失败,请检查网络")
except Timeout:
print("请求超时")
except HTTPError as e:
print(f"HTTP错误:{e.response.status_code}")
except Exception as e:
print(f"发生其他错误:{e}")
代码逻辑分析:
- raise_for_status() :如果响应状态码不是 2xx,抛出 HTTPError 。
- timeout :设置请求超时时间(单位秒)。
- 使用多个 except 捕获不同类型的异常,提高程序健壮性。
3.4 会话对象与Cookie管理
3.4.1 Session对象的使用
Requests 提供了 Session 对象用于持久化请求,可以跨请求保持 Cookie、设置默认请求头等。
import requests
# 创建会话对象
session = requests.Session()
# 设置默认请求头
session.headers.update({
'User-Agent': 'MyApp/1.0'
})
# 发送请求
response1 = session.get('https://api.example.com/page1')
response2 = session.get('https://api.example.com/page2')
print(response1.cookies.get_dict())
print(response2.cookies.get_dict())
代码逻辑分析:
- Session 对象在整个会话期间保持 Cookie。
- 可以设置默认请求头、认证信息等,减少重复代码。
3.4.2 持久化请求与Cookie自动管理
Session 会自动管理 Cookie,适用于需要登录状态的爬虫。
import requests
session = requests.Session()
# 登录请求
login_data = {
'username': 'test',
'password': '123456'
}
session.post('https://api.example.com/login', data=login_data)
# 后续请求自动携带 Cookie
profile = session.get('https://api.example.com/profile')
print(profile.text)
代码逻辑分析:
- 登录后,服务器返回的 Cookie 被 Session 自动保存。
- 后续请求无需手动设置 Cookie,Session 会自动附加。
3.4.3 手动设置Cookie进行身份验证
有时需要手动设置 Cookie,例如从浏览器复制 Cookie 进行调试。
import requests
session = requests.Session()
# 手动设置 Cookie
session.cookies.update({
'sessionid': 'abc123xyz',
'csrftoken': 'csrf123'
})
# 发送请求
response = session.get('https://api.example.com/protected')
print(response.text)
代码逻辑分析:
- 使用 session.cookies.update() 方法设置 Cookie。
- 可用于模拟登录或绕过部分验证机制。
本章总结
第三章详细介绍了 Python 中 Requests 库的使用方法,包括 GET 和 POST 请求的参数传递、自定义请求头、文件上传下载、响应处理与异常捕获,以及会话管理与 Cookie 操作。这些内容构成了网络请求的基础,为后续爬虫项目开发打下坚实基础。
4. BeautifulSoup解析HTML实战
在现代网络爬虫开发中,HTML解析是数据提取的关键环节。BeautifulSoup 作为 Python 中最强大的 HTML 解析库之一,凭借其灵活的 API 和对复杂 HTML 结构的友好支持,广泛应用于网页数据提取任务中。本章将从安装与基础使用入手,逐步深入讲解 HTML 解析的核心技术、实战技巧以及性能优化策略,帮助开发者掌握从简单页面提取到复杂结构处理的完整流程。
4.1 BeautifulSoup库概述与安装
BeautifulSoup 是一个用于解析 HTML 和 XML 文档的 Python 库,特别适合从网页中提取数据。它能够自动将输入文档转换为 Unicode 编码,并构建文档树,使得开发者可以轻松地通过标签名、类名、ID 等方式查找和操作页面元素。
4.1.1 安装与基本用法
要使用 BeautifulSoup,首先需要通过 pip 安装:
pip install beautifulsoup4
安装完成后,可以通过以下方式导入并使用:
from bs4 import BeautifulSoup
html = """
<html>
<head><title>示例页面</title></head>
<body>
<h1 class="title">Hello BeautifulSoup</h1>
<p id="content">这是一个用于演示的段落。</p>
</body>
</html>
soup = BeautifulSoup(html, "html.parser")
print(soup.title.string) # 输出:示例页面
print(soup.h1['class']) # 输出:['title']
print(soup.p.text) # 输出:这是一个用于演示的段落。
代码解释:
-
BeautifulSoup(html, "html.parser"):创建一个 BeautifulSoup 对象,使用内置的html.parser解析器。 -
soup.title.string:获取<title>标签内的文本内容。 -
soup.h1['class']:访问<h1>标签的 class 属性。 -
soup.p.text:提取<p>标签的文本内容。
4.1.2 支持的解析器(html.parser、lxml、html5lib)
BeautifulSoup 支持多种解析器,开发者可以根据需求选择最适合的一种:
| 解析器 | 描述 | 优点 | 缺点 |
|---|---|---|---|
html.parser | Python 标准库中的 HTML 解析器 | 无需额外安装 | 解析速度较慢,容错性一般 |
lxml | 使用 C 实现的解析器,支持 HTML 和 XML | 解析速度快,容错性强 | 需要安装 lxml 模块 |
html5lib | 按照 HTML5 规范解析文档,适合解析不规范的 HTML | 极强的容错能力,生成标准 DOM | 速度慢,依赖较多外部库 |
安装 lxml:
pip install lxml
使用 lxml 解析 HTML:
soup = BeautifulSoup(html, "lxml")
mermaid 流程图展示解析器选择逻辑:
graph TD
A[开始] --> B{是否需要快速解析?}
B -->|是| C[lxml]
B -->|否| D{是否需要兼容 HTML5?}
D -->|是| E[html5lib]
D -->|否| F[html.parser]
4.2 HTML结构解析与元素定位
掌握 HTML 结构的解析与元素定位技巧,是使用 BeautifulSoup 进行数据提取的基础。BeautifulSoup 提供了多种方式来查找和操作 HTML 元素,包括基于标签名、属性、类名等方式的查找。
4.2.1 标签查找与层级遍历
可以通过 find() 和 find_all() 方法查找标签:
# 查找第一个 <p> 标签
first_p = soup.find("p")
print(first_p)
# 查找所有 <p> 标签
all_ps = soup.find_all("p")
for p in all_ps:
print(p)
层级遍历:
BeautifulSoup 提供了多种方式遍历文档树:
# 父节点
print(first_p.parent.name)
# 子节点
children = first_p.children
for child in children:
print(child)
# 所有后代节点
descendants = first_p.descendants
for d in descendants:
print(d)
4.2.2 属性获取与内容提取
提取标签的属性值和文本内容是数据提取的核心操作:
# 获取属性
h1_tag = soup.find("h1")
print(h1_tag.get("class")) # 输出:['title']
# 获取文本内容
print(h1_tag.text) # 输出:Hello BeautifulSoup
4.2.3 多标签匹配与结果过滤
可以通过传递多个标签名或使用正则表达式进行更灵活的匹配:
# 查找 <h1> 或 <p> 标签
tags = soup.find_all(["h1", "p"])
for tag in tags:
print(tag.name, tag.text)
# 查找 class 包含 'title' 的所有标签
title_tags = soup.find_all(class_="title")
for tag in title_tags:
print(tag.name, tag.text)
代码解释:
-
find_all(["h1", "p"]):同时匹配多个标签。 -
class_="title":由于class是 Python 的关键字,因此使用class_来匹配 class 属性。
4.3 数据提取实战技巧
在实际项目中,HTML 页面往往结构复杂,数据嵌套深、格式不规范。掌握高级提取技巧对于高效爬取数据至关重要。
4.3.1 提取文本与属性值
在实际开发中,往往需要提取多个字段并组合成结构化数据:
data = {
"title": soup.title.string,
"h1_text": soup.h1.text,
"p_content": soup.find("p", id="content").text
}
print(data)
4.3.2 处理嵌套结构与复杂页面
当页面中存在多层嵌套结构时,可通过多层查找提取数据:
# 示例 HTML
html_complex = """
<div class="article">
<h2>文章标题</h2>
<div class="content">
<p>正文内容1</p>
<p>正文内容2</p>
</div>
</div>
soup_complex = BeautifulSoup(html_complex, "lxml")
# 提取嵌套结构中的内容
article_title = soup_complex.find("div", class_="article").h2.text
content_ps = soup_complex.find("div", class_="content").find_all("p")
print("标题:", article_title)
for p in content_ps:
print("段落:", p.text)
4.3.3 结合正则表达式提取非结构化信息
有时候页面中包含不规则结构的信息,可以结合 re 模块进行模糊匹配:
import re
# 提取所有以 "段落" 开头的文本
pattern = re.compile(r"段落.*")
matches = soup.find_all(text=pattern)
for match in matches:
print(match)
4.4 性能优化与常见问题处理
在处理大型 HTML 页面或高频爬取任务时,BeautifulSoup 的性能表现直接影响程序效率。合理优化解析策略和处理异常情况是保障程序稳定运行的关键。
4.4.1 提高解析效率的技巧
- 选择高效解析器 :优先使用
lxml解析器。 - 避免全量解析 :只解析需要的部分 HTML 内容。
- 缓存查找结果 :避免重复查找相同标签。
# 避免重复查找
all_links = soup.find_all("a")
for link in all_links:
print(link.get("href"))
4.4.2 大型页面处理策略
处理大型页面时,可以采用分段解析或流式解析策略:
from bs4.builder._parser import ParserRejectedMarkup
try:
with open("large_page.html", "r", encoding="utf-8") as f:
html_large = f.read()
soup_large = BeautifulSoup(html_large, "lxml")
except ParserRejectedMarkup:
print("页面内容被解析器拒绝,尝试其他方式")
4.4.3 解析失败的常见原因与解决方案
| 问题原因 | 表现 | 解决方案 |
|---|---|---|
| 页面内容为空或不完整 | 报错或返回 None | 检查请求是否成功,使用异常处理 |
| 使用了不支持的解析器 | 报错或解析结果异常 | 更换为 lxml 或 html5lib |
| 页面使用了 JavaScript 渲染 | HTML 中无目标数据 | 改用 Selenium 或 Puppeteer |
| 标签名称或属性书写错误 | 无法找到目标元素 | 检查 HTML 源码,使用调试工具 |
异常处理示例:
try:
title = soup.title.string
except AttributeError:
print("标题标签不存在")
以上内容从 BeautifulSoup 的基础安装与使用出发,逐步深入到 HTML 解析、数据提取、性能优化和异常处理等多个方面,帮助开发者构建完整的数据提取能力。后续章节将继续介绍更高级的爬虫框架与实战技巧。
5. Scrapy框架搭建与项目结构
Scrapy 是一个用于大规模网络爬取的强大框架,专为高效抓取网页数据而设计。它不仅提供了完整的请求调度、数据解析、持久化等功能,还支持中间件机制、异步处理、日志系统和分布式部署。本章将从 Scrapy 的安装配置、项目结构解析、核心组件剖析到请求调度与中间件机制进行深入探讨,最后讲解如何部署和调试 Scrapy 项目,帮助开发者构建稳定高效的爬虫系统。
5.1 Scrapy框架概述与环境搭建
5.1.1 Scrapy的特点与优势
Scrapy 是一个基于 Python 的开源网络爬虫框架,其设计目标是高效、可扩展和可维护。主要特点如下:
| 特性 | 说明 |
|---|---|
| 异步处理 | 基于 Twisted 库实现异步 I/O,提升爬取效率 |
| 内置中间件 | 支持请求拦截、代理设置、User-Agent轮换等 |
| 可扩展性强 | 可自定义 Spider、Pipeline、Middleware 等组件 |
| 数据流清晰 | 从请求到解析再到存储流程清晰 |
| 支持多种导出格式 | JSON、CSV、XML、MongoDB、MySQL 等 |
| 社区活跃 | 文档完善,社区支持广泛,生态丰富 |
5.1.2 安装与依赖配置
Scrapy 可通过 pip 安装,但需注意依赖环境的配置:
pip install scrapy
常见问题及解决方案:
- Windows 系统缺少依赖 :需要安装 Microsoft Visual C++ Build Tools。
- Twisted 安装失败 :建议使用
pip install twisted单独安装后再装 Scrapy。 - PyOpenSSL 报错 :升级或安装
pyopenssl。
验证安装:
scrapy version
输出类似:
Scrapy 2.6.1
5.2 Scrapy项目结构与核心组件
5.2.1 工程目录结构详解
创建一个 Scrapy 项目非常简单,使用如下命令:
scrapy startproject example
生成的目录结构如下:
example/
├── scrapy.cfg
├── example/
│ ├── __init__.py
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders/
│ └── __init__.py
└── tutorials/
各文件作用:
| 文件 | 作用 |
|---|---|
scrapy.cfg | 项目配置文件,用于部署和运行 |
items.py | 定义数据模型(Item) |
middlewares.py | 自定义中间件逻辑 |
pipelines.py | 数据处理流水线 |
settings.py | 配置项(如 User-Agent、下载延迟等) |
spiders/ | 存放爬虫类文件 |
5.2.2 Spider爬虫类与解析逻辑
Spider 是 Scrapy 的核心组件,负责发送请求和解析响应。以下是一个基本的 Spider 示例:
import scrapy
class ExampleSpider(scrapy.Spider):
name = 'example'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com/']
def parse(self, response):
yield {
'title': response.xpath('//h1/text()').get(),
'url': response.url
}
代码解析:
-
name:爬虫名称,启动时使用。 -
allowed_domains:限制爬取域名。 -
start_urls:起始爬取地址。 -
parse方法:解析响应内容,返回提取的数据。
yield 机制:
yield 是 Python 的生成器语法,Scrapy 使用它将提取的数据传入后续的 Pipeline 处理流程。
5.2.3 Item数据模型定义
Item 是结构化数据的容器,定义数据字段:
import scrapy
class ExampleItem(scrapy.Item):
title = scrapy.Field()
url = scrapy.Field()
content = scrapy.Field()
优势:
- 数据字段清晰,便于后期处理。
- 支持字段验证与类型转换。
- 提高代码可维护性。
5.2.4 Pipeline数据处理流程
Pipeline 用于处理提取的数据,如清洗、验证、存储等。以下是一个存储到 JSON 的示例:
import json
class JsonPipeline:
def open_spider(self, spider):
self.file = open('output.json', 'w')
def close_spider(self, spider):
self.file.close()
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item
Pipeline 方法说明:
-
open_spider():爬虫启动时执行。 -
close_spider():爬虫结束时执行。 -
process_item():每条数据都会经过此方法。
启用 Pipeline:
在 settings.py 中配置:
ITEM_PIPELINES = {
'example.pipelines.JsonPipeline': 300,
}
5.3 Scrapy请求调度与中间件
5.3.1 下载器中间件配置
中间件(Middleware)用于拦截请求和响应,实现如代理设置、User-Agent 轮换、请求重试等。
启用中间件:
在 settings.py 中:
DOWNLOADER_MIDDLEWARES = {
'example.middlewares.RandomUserAgentMiddleware': 543,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
}
自定义中间件示例:
import random
class RandomUserAgentMiddleware:
def process_request(self, request, spider):
ua_list = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15'
]
request.headers['User-Agent'] = random.choice(ua_list)
说明:
-
process_request():在请求发送前处理。 - 设置随机 User-Agent,有助于规避反爬机制。
5.3.2 请求调度机制与去重策略
Scrapy 使用 Scheduler 模块管理请求队列,支持去重机制。
去重原理:
- 默认使用
RFPDupeFilter,基于指纹去重。 - 使用
scrapy.dupefilters包,支持自定义去重逻辑。
开启去重:
DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'
自定义去重逻辑:
from scrapy.dupefilters import RFPDupeFilter
class CustomDupeFilter(RFPDupeFilter):
def request_seen(self, request):
# 自定义指纹生成逻辑
fp = request.url.split('?')[0]
if fp in self.fingerprints:
return True
self.fingerprints.add(fp)
return False
5.3.3 自定义中间件开发实践
中间件是 Scrapy 框架中最为灵活的组件之一,可用于请求拦截、响应修改、代理轮换等。
示例:请求重试中间件
class RetryMiddleware:
def process_response(self, request, response, spider):
if response.status in [500, 502, 503]:
retries = request.meta.get('retry_times', 0)
if retries < 3:
request.meta['retry_times'] = retries + 1
return request.copy()
return response
说明:
- 当响应码为 500 类错误时,自动重试最多 3 次。
- 利用
meta字段传递状态信息。
5.4 Scrapy部署与调试
5.4.1 日志查看与调试技巧
Scrapy 内置了强大的日志系统,可通过配置日志级别进行调试:
LOG_LEVEL = 'DEBUG' # 可选 DEBUG、INFO、WARNING、ERROR、CRITICAL
日志输出示例:
2023-10-01 12:00:00 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://www.example.com/>
调试技巧:
- 使用
scrapy shell进入交互式调试环境。 - 使用
response.css()和response.xpath()快速测试解析逻辑。 - 设置
DOWNLOAD_DELAY延迟请求,避免被封禁。
5.4.2 数据导出格式配置(JSON、CSV等)
Scrapy 支持多种数据导出格式:
scrapy crawl example -o output.json
常用格式:
| 格式 | 示例 |
|---|---|
| JSON | -o output.json |
| CSV | -o output.csv |
| XML | -o output.xml |
| MySQL | 配合 Pipeline 写入数据库 |
CSV 导出示例:
scrapy crawl example -o output.csv -t csv
CSV 输出内容:
title,url
"Example Domain","http://www.example.com/"
5.4.3 使用Scrapyd进行分布式部署
Scrapyd 是 Scrapy 的部署服务,支持远程调度、任务管理和日志查看。
安装 Scrapyd:
pip install scrapyd
配置 Scrapyd 服务:
创建 scrapyd.conf 文件:
[scrapyd]
eggs_dir = eggs
logs_dir = logs
items_dir = items
jobs_to_keep = 5
打包项目并部署:
scrapy deploy
远程运行爬虫:
curl http://localhost:6800/schedule.json -d project=default -d spider=example
部署架构流程图:
graph TD
A[Scrapy 项目] --> B(打包部署)
B --> C[Scrapyd 服务]
C --> D[任务调度]
D --> E[爬虫执行]
E --> F[日志与结果输出]
通过本章的详细讲解,我们从 Scrapy 的安装配置入手,逐步深入到项目结构、核心组件、请求调度、中间件开发以及部署调试等多个方面,全面掌握了 Scrapy 框架的使用方式和构建爬虫系统的流程。下一章我们将介绍 Selenium 的使用,解决动态页面抓取难题。
6. Selenium模拟浏览器操作
在现代网页中,越来越多的页面内容依赖于 JavaScript 动态加载,传统的基于 HTTP 请求的爬虫(如 Requests)往往无法获取完整的页面内容。Selenium 是一个强大的浏览器自动化工具,能够模拟用户操作浏览器,真实地渲染页面内容,是处理动态加载网页的理想选择。
本章将详细介绍 Selenium 的使用方法,涵盖环境搭建、元素定位、页面操作、动态页面抓取等内容,帮助开发者高效构建可应对复杂网页结构的爬虫系统。
6.1 Selenium概述与环境准备
Selenium 是一个开源的自动化测试工具,广泛用于 Web 应用的功能测试和 UI 自动化。它通过控制真实浏览器(如 Chrome、Firefox、Edge 等)来模拟用户的操作行为,从而可以获取完整的页面 DOM 结构和动态渲染内容。
6.1.1 Selenium的优势与适用场景
Selenium 的核心优势在于其 真实浏览器渲染能力 ,适用于以下场景:
| 场景 | 描述 |
|---|---|
| 动态页面抓取 | 页面内容通过 JavaScript 异步加载(如 AJAX) |
| 用户行为模拟 | 模拟点击、输入、下拉、滚动等用户操作 |
| 登录验证绕过 | 自动填写用户名密码完成登录 |
| 页面截图与测试 | 获取网页截图用于测试或存档 |
| 复杂表单交互 | 自动化填写、提交复杂表单 |
Selenium 支持多种浏览器,如 Chrome、Firefox、Edge、Safari 等,并提供多语言接口,包括 Python、Java、C# 等,非常适合 Python 爬虫开发。
6.1.2 安装与浏览器驱动配置
安装 Selenium
使用 pip 安装 Selenium:
pip install selenium
浏览器驱动配置
Selenium 依赖浏览器驱动来与浏览器进行通信。不同浏览器需要对应的驱动程序:
| 浏览器 | 驱动名称 | 官网 |
|---|---|---|
| Chrome | chromedriver | https://sites.google.com/chromium.org/driver/ |
| Firefox | geckodriver | https://github.com/mozilla/geckodriver |
| Edge | edgedriver | https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ |
安装完成后,将驱动程序路径添加到系统环境变量中,或在代码中指定驱动路径。
示例:启动 Chrome 浏览器
from selenium import webdriver
# 指定chromedriver路径
driver = webdriver.Chrome(executable_path='/path/to/chromedriver')
# 打开网页
driver.get("https://www.baidu.com")
# 关闭浏览器
driver.quit()
代码逻辑分析:
-
webdriver.Chrome()初始化一个 Chrome 浏览器实例。 -
get()方法用于打开指定的 URL。 -
quit()方法关闭浏览器并释放资源。
6.2 元素定位与页面操作
Web 页面由 HTML 构成,Selenium 通过定位 HTML 元素来进行交互。准确的元素定位是实现自动化操作的前提。
6.2.1 定位方式(ID、XPath、CSS选择器等)
Selenium 提供多种元素定位方式,常用的有:
| 定位方式 | 语法 | 示例 |
|---|---|---|
| ID | find_element(By.ID, "id") | find_element(By.ID, "username") |
| Name | find_element(By.NAME, "name") | find_element(By.NAME, "password") |
| XPath | find_element(By.XPATH, "xpath") | find_element(By.XPATH, "//input[@id='submit']") |
| CSS选择器 | find_element(By.CSS_SELECTOR, "selector") | find_element(By.CSS_SELECTOR, "input[type='text']") |
| Link Text | find_element(By.LINK_TEXT, "text") | find_element(By.LINK_TEXT, "登录") |
| Partial Link Text | find_element(By.PARTIAL_LINK_TEXT, "部分文本") | find_element(By.PARTIAL_LINK_TEXT, "登") |
示例:使用 CSS 选择器定位百度搜索框并输入内容
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
# 定位搜索框并输入内容
search_box = driver.find_element(By.CSS_SELECTOR, "#kw")
search_box.send_keys("Selenium 教程")
# 提交搜索
search_box.submit()
driver.quit()
代码逻辑分析:
-
find_element()用于定位页面元素。 -
send_keys()方法模拟键盘输入。 -
submit()方法用于提交表单。
6.2.2 常见操作(点击、输入、下拉等)
点击操作
button = driver.find_element(By.XPATH, "//button[@id='submit']")
button.click()
输入操作
username = driver.find_element(By.NAME, "username")
username.send_keys("test_user")
下拉菜单选择
使用 Select 类处理下拉菜单:
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element(By.ID, 'country'))
select.select_by_visible_text('China')
6.2.3 显式等待与隐式等待机制
由于网页加载速度不一致,直接定位元素可能失败。Selenium 提供了两种等待机制:
隐式等待(Implicit Wait)
全局等待,适用于整个 WebDriver 生命周期。
driver.implicitly_wait(10) # 最多等待10秒
显式等待(Explicit Wait)
针对特定条件等待,更加灵活可靠。
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
try:
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.ID, "result"))
)
finally:
driver.quit()
流程图:显式等待执行流程
graph TD
A[开始等待] --> B{元素是否出现?}
B -->|是| C[返回元素]
B -->|否| D[等待指定时间]
D --> B
C --> E[执行下一步操作]
6.3 浏览器自动化与动态页面抓取
现代网页大量使用 JavaScript 动态加载内容,传统爬虫无法获取完整页面数据。Selenium 可以通过执行 JavaScript、处理 AJAX 请求、模拟用户交互等方式,实现动态页面的完整抓取。
6.3.1 动态加载页面的处理方案
方案一:等待元素出现(推荐)
使用 WebDriverWait 等待关键元素加载完成。
element = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CLASS_NAME, "dynamic-content"))
)
方案二:滚动页面触发加载
模拟用户滚动页面以触发内容加载:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
6.3.2 执行JavaScript脚本
Selenium 支持直接执行 JavaScript 脚本,常用于操作页面、获取数据、绕过反爬等。
示例:获取页面滚动高度
height = driver.execute_script("return document.body.scrollHeight")
print(f"页面高度为:{height}")
示例:修改页面样式
driver.execute_script("document.getElementById('header').style.display='none';")
6.3.3 截图与页面状态保存
Selenium 支持截图功能,方便调试和记录页面状态。
全屏截图
driver.save_screenshot("screenshot.png")
元素截图
element = driver.find_element(By.ID, "login-form")
element.screenshot("form_screenshot.png")
页面源码获取
html = driver.page_source
with open("page.html", "w", encoding="utf-8") as f:
f.write(html)
总结与延伸
Selenium 是处理动态网页内容的强大工具,通过模拟真实浏览器操作,能够有效绕过反爬机制、抓取异步加载内容、模拟用户行为等。结合 WebDriverWait、JavaScript 脚本执行和截图功能,开发者可以构建出高度自动化、稳定可靠的网页爬虫系统。
在下一章中,我们将探讨如何将爬取的数据进行持久化存储,包括 CSV、JSON 和 MySQL 数据库的写入方式,为构建完整的数据采集系统打下基础。
7. 数据存储:CSV、JSON、MySQL写入实战
在爬虫开发过程中,数据的采集只是第一步,如何高效、安全地存储和管理数据是整个流程中至关重要的环节。本章将详细介绍爬虫数据的几种常见存储方式:CSV、JSON 和 MySQL,并通过实际操作示例,展示如何将爬取的数据写入这些存储介质。
7.1 数据存储方式概述
在数据存储中,我们需要根据数据量、结构化程度、查询需求和长期维护等因素选择合适的存储格式或数据库。
| 存储方式 | 特点 | 适用场景 |
|---|---|---|
| CSV | 简单、轻量,适合表格型数据 | 小型数据集、快速导出、Excel兼容 |
| JSON | 支持嵌套结构,可读性好 | API接口数据、非结构化数据 |
| MySQL | 结构化强、查询高效、支持事务 | 需频繁查询、更新、多用户并发的数据系统 |
在实际项目中,常常会将多种方式结合使用,例如先将原始数据以 JSON 格式暂存,再清洗后导入 MySQL 数据库进行长期管理。
7.2 CSV与JSON存储实践
7.2.1 使用Python标准库写入CSV
Python 标准库 csv 提供了便捷的 CSV 文件操作方法。以下是一个将爬取的书籍信息写入 CSV 的示例:
import csv
# 假设这是爬取到的数据
data = [
{'title': 'Python编程从入门到实践', 'author': 'Eric Matthes', 'price': '69.00'},
{'title': '流畅的Python', 'author': 'Luciano Ramalho', 'price': '109.00'},
{'title': '算法导论', 'author': 'Thomas H. Cormen', 'price': '128.00'}
]
# 写入CSV
with open('books.csv', mode='w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['title', 'author', 'price'])
writer.writeheader() # 写入表头
writer.writerows(data) # 写入数据行
参数说明:
-
fieldnames: 定义CSV列的顺序。 -
newline='': 防止在Windows系统下出现空行。 -
DictWriter: 支持字典格式写入,适用于结构化数据。
7.2.2 JSON格式构建与数据序列化
json 模块用于将 Python 对象序列化为 JSON 字符串或文件。适用于需要嵌套结构或API交互的场景。
import json
# 写入JSON文件
with open('books.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
参数说明:
-
ensure_ascii=False: 保证中文字符不被转义。 -
indent=4: 美化输出格式,增加缩进,便于阅读。
7.2.3 大文件写入优化策略
当数据量较大时(如百万级以上),建议采用以下策略优化写入效率:
- 分块写入(Chunking) :将数据按批次写入,避免一次性加载内存。
- 使用生成器 :逐条生成数据逐条写入,节省内存。
- 使用
csv.writer而非DictWriter:在不需字段名时,writer性能更高。
7.3 数据库存储与MySQL操作
MySQL 是结构化数据库的代表,适用于需要持久化、支持查询、事务等操作的场景。Python 中可通过 PyMySQL 或 SQLAlchemy 实现数据库写入。
7.3.1 MySQL数据库连接与配置
首先确保安装 MySQL 服务器并创建数据库和表结构:
CREATE DATABASE crawler_db;
USE crawler_db;
CREATE TABLE books (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
author VARCHAR(255),
price DECIMAL(10, 2)
);
7.3.2 使用PyMySQL与SQLAlchemy进行数据写入
PyMySQL 示例:
import pymysql
# 连接数据库
conn = pymysql.connect(
host='localhost',
user='root',
password='yourpassword',
database='crawler_db',
charset='utf8mb4'
)
cursor = conn.cursor()
# 插入数据
for item in data:
sql = "INSERT INTO books (title, author, price) VALUES (%s, %s, %s)"
cursor.execute(sql, (item['title'], item['author'], float(item['price'])))
conn.commit()
cursor.close()
conn.close()
SQLAlchemy 示例(推荐):
from sqlalchemy import create_engine, Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 定义模型
Base = declarative_base()
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String(255))
author = Column(String(255))
price = Column(Float)
# 创建引擎与会话
engine = create_engine('mysql+pymysql://root:yourpassword@localhost/crawler_db')
Session = sessionmaker(bind=engine)
session = Session()
# 插入数据
for item in data:
book = Book(title=item['title'], author=item['author'], price=float(item['price']))
session.add(book)
session.commit()
session.close()
7.3.3 数据去重与批量插入优化
为避免重复插入相同数据,可在数据库中添加唯一索引:
ALTER TABLE books ADD UNIQUE (title, author);
批量插入时可使用以下方法:
cursor.executemany(sql, values_list) # 批量执行SQL语句
7.3.4 异常处理与事务管理
良好的数据库操作应包含异常处理与事务控制:
try:
for item in data:
sql = "INSERT INTO books (title, author, price) VALUES (%s, %s, %s)"
cursor.execute(sql, (item['title'], item['author'], float(item['price'])))
conn.commit()
except Exception as e:
conn.rollback() # 出错回滚
print("插入失败:", e)
finally:
cursor.close()
conn.close()
简介:网络爬虫是获取互联网数据的重要工具,Python凭借其简洁语法和丰富库支持,成为爬虫开发的首选语言。本文系统讲解Python网络爬虫的核心知识,涵盖HTTP/HTTPS协议基础、HTML结构解析、常用爬虫库(如Requests、BeautifulSoup、Scrapy、Selenium)、爬虫流程设计、反反爬策略以及实战案例。通过本项目学习,读者能够掌握从网页抓取到数据存储的完整流程,并具备应对复杂网页结构和反爬机制的能力。
更多推荐
所有评论(0)