查看全课大纲(第 18 / 101 节)
- 1.数据分析基本概念
- 2.学习数据分析的一般路线
- 3.数据分析的流程
- 4.数据类型
- 5.环境部署(1)
- 6.环境部署(2)
- 7.课程介绍
- 8.TXT文件操作
- 9.JSON文件操作
- 10.CSV文件操作
- 11.Excel文件操作
- 12.数据库及SQL常用语法
- 13.数据库基本操作
- 14.数据库多表连接
- 15.实战:欧洲职业足球数据库分析
- 16.爬虫简介
- 17.URL管理模块
- 18.网页下载模块
- 19.网页解析模块(1)
- 20.网页解析模块(2)
- 21.Scrapy简介
- 22.Scrapy使用步骤(1)
- 23.Scrapy使用步骤(2)
- 24.Scrapy使用步骤(3)
- 25.Scrapy使用步骤(4)
- 26.实战:获取国内城市空气质量指数数据
- 27.NumPy和SciPy介绍
- 28.多维数组
- 29.多维数组操作
- 30.NumPy的常用方法
- 31.向量化介绍
- 32.向量化及通用函数
- 33.实战:2016美国大选分析
- 34.数据结构-Series
- 35.数据结构-DataFrame
- 36.数据结构-Index
- 37.Series的索引操作
- 38.DataFrame的索引操作
- 39.索引操作总结
- 40.运算与对齐
- 41.函数应用操作(1) -- map
- 42.函数应用操作 (2) -- apply applymap
- 43.文件读写操作
- 44.排序操作
- 45.数据清洗--处理缺失数据
- 46.数据清洗--处理重复数据
- 47.数据清洗--替换数据
- 48.常用统计方法(1) -- describe quantile
- 49.常用统计方法(2) -- sum mean median count
- 50.常用统计方法(3) -- max min idxmax idxmin
- 51.常用统计方法(4) -- mad var std cumsum
- 52.实战:全球食品数据分析
- 53.层级索引
- 54.分组与聚合介绍
- 55.分组操作(1) -- GroupBy对象及常用聚合操作
- 56.分组操作(2) -- 自定义分组及聚合操作
- 57.透视表介绍
- 58.透视表操作
- 59.数据规整(1) -- 数据合并concat
- 60.数据规整(2) -- 数据连接merge
- 61.数据重构(3) -- 数据重构stack unstack
- 62.实战:互联网电影资料库分析
- 63.探索性数据分析EDA介绍
- 64.EDA的目的
- 65.EDA常用工具
- 66.Matplotlib绘图基本介绍
- 67.Matplotlib画布
- 68.散点图和柱状图的绘制
- 69.直方图的绘制
- 70.矩阵绘图
- 71.子图的使用
- 72.Matplotlib颜色、标记、线型
- 73.Matplotlib坐标刻度、标签、图例、标题
- 74.Seaborn介绍
- 75.数据集分布可视化(1) -- 单变量分布、双变量分布
- 76.数据集分布可视化(2) -- 变量关系可视化
- 77.类别数据可视化 -- 类别散布图、类别内数据分布、类别内统计图
- 78.交互式数据可视化工具Bokeh介绍
- 79.Bokeh绘制散点图、柱状图、盒子图、弦图
- 80.Bokeh绘制常用图形元素
- 81.3D绘图 -- mplot3d
- 82.3D曲线可视化
- 83.3D散点图可视化
- 84.3D柱状图可视化
- 85.Pandas绘图
- 86.实战:Lending Club借贷数据探索性分析及可视化
- 87.机器学习介绍及应用场景
- 88.机器学习建模介绍 (1) -- 分类
- 89.机器学习建模介绍 (2) -- 回归
- 90.机器学习建模介绍 (3) -- 聚类
- 91.机器学习分类
- 92.机器学习工具scikit-learn
- 93.使用scikit-learn的流程
- 94.数据集准备及划分
- 95.模型选择
- 96.数据预处理及特征工程
- 97.过拟合与欠拟合
- 98.模型调参介绍
- 99.模型调参方法
- 100.模型测试及评价
- 101.实战:通过移动设备行为数据预测性别和年龄
网页下载模块
约 6 分钟
小象实战讲义 · Python数据分析实战
在上一节中,我们学习了如何管理URL。接下来,我们需要将这些URL对应的网页内容获取到本地或内存中,这就是网页下载模块的核心任务。本节将介绍如何使用Python内置工具模拟网页下载的基本流程,并理解HTTP响应的关键组成部分,如状态码、头部信息和编码,为后续的网页解析打下基础。
💡 核心导读
- 网页下载的本质:将指定URL对应的网页内容获取到本地或内存中,通常表现为一个字符串。
- HTTP响应对象:理解一次网络请求返回的响应对象所包含的关键属性。
- 状态码解读:掌握常见的HTTP状态码(如200、404、500)及其含义,用于判断请求成功与否。
- 编码问题处理:识别并正确处理网页内容的编码,特别是处理中文等非ASCII字符时避免乱码。
- 内容获取方式:了解获取网页文本内容与字节内容的区别与联系。
网页下载的任务与流程
网页下载模块的核心任务非常明确:将指定的URL对应的网页内容下载到本地文件,或者直接读入到程序的内存中。
当内容被读入内存后,它本质上就是一个字符串(或字节序列)。我们可以将其视为一个特殊的、结构化的文本字符串。后续的解析工作,例如提取标题、链接或正文,就可以基于这个字符串,利用字符串操作或专门的解析库来完成。
然而,实际的下载过程涉及网络通信的多个层面,其中包含许多重要的信息,例如HTTP状态码、响应头部(Headers) 以及内容编码(Encoding) 等。这些信息对于判断请求是否成功、理解服务器返回的数据格式至关重要。
在Python生态中,有一个非常流行的第三方库 requests 可以优雅地完成这些任务。它通过简单的 requests.get(url) 方法发起请求,并返回一个包含所有响应信息的 Response 对象。虽然本课程遵循依赖白名单,不直接使用 requests,但我们将通过Python标准库模拟其核心逻辑,帮助你透彻理解网页下载的完整机制。
理解HTTP响应与关键属性
当我们向服务器发起一个HTTP请求后,服务器会返回一个响应。这个响应不仅仅包含网页的HTML代码,还附带了一系列元数据。我们可以通过一个模拟的响应对象来查看这些关键属性。
首先,我们利用Python标准库模拟一个简单的HTTP GET请求和响应解析过程。请注意,以下代码完全使用Python内置模块,不依赖 requests。
# 模拟网页下载与解析的核心流程(全部使用构造数据,不发起真实网络请求)
# 1. 定义目标URL(以百度为例,实际课程中我们使用一个模拟端点)
# 为严格遵守依赖白名单且保证代码可复现,我们构造一个模拟的HTTP响应数据。
# 在实际爬虫中,你会使用 urlopen('https://www.example.com')
# 2. 模拟服务器响应的关键组成部分
print("=== 模拟HTTP响应关键属性 ===")
# 状态码 (Status Code)
status_code = 200
print(f"1. 状态码 (status_code): {status_code}")
print(" - 200: 请求成功")
print(" - 404: 请求的资源不存在")
print(" - 500: 服务器内部错误")
# 响应头部 (Headers) - 模拟常见字段
headers = {
'Server': 'nginx/1.18.0',
'Content-Type': 'text/html; charset=utf-8',
'Content-Length': '127',
'Connection': 'close'
}
print(f"\n2. 响应头部 (headers) 示例:")
for key, value in headers.items():
print(f" - {key}: {value}")
# 编码 (Encoding)
# 编码信息通常从 'Content-Type' 头部的 'charset' 部分解析,或根据内容推断
encoding = 'utf-8'
print(f"\n3. 网页编码 (encoding): {encoding}")
print(" - 常见编码: UTF-8, GBK, GB2312, ISO-8859-1")
# 响应内容 - 文本形式 (Text)
html_content_text = '<html><head><title>模拟页面</title></head><body><p>这是一个模拟的网页内容,包含中文:你好,世界!</p></body></html>'
print(f"\n4. 文本内容 (text) 前100字符:\n {html_content_text[:100]}...")
# 响应内容 - 字节形式 (Content)
html_content_bytes = html_content_text.encode(encoding)
print(f"\n5. 字节内容 (content) 类型及前50字节:")
print(f" 类型: {type(html_content_bytes).__name__}")
print(f" 内容: {html_content_bytes[:50]}...")=== 模拟HTTP响应关键属性 ===
1. 状态码 (status_code): 200
- 200: 请求成功
- 404: 请求的资源不存在
- 500: 服务器内部错误
2. 响应头部 (headers) 示例:
- Server: nginx/1.18.0
- Content-Type: text/html; charset=utf-8
- Content-Length: 127
- Connection: close
3. 网页编码 (encoding): utf-8
- 常见编码: UTF-8, GBK, GB2312, ISO-8859-1
4. 文本内容 (text) 前100字符:
<html><head><title>模拟页面</title></head><body><p>这是一个模拟的网页内容,包含中文:你好,世界!</p></body></html>...
5. 字节内容 (content) 类型及前50字节:
类型: bytes
内容: b'<html><head><title>\xe6\xa8\xa1\xe6\x8b\x9f\xe9\xa1\xb5\xe9\x9d\xa2</title></head><bod'...通过上面的模拟输出,我们可以清晰地看到一次HTTP响应的核心组成部分:
- 状态码:这是判断请求成败的首要指标。
200表示成功,404表示未找到页面,500表示服务器端出错。 - 响应头部:包含服务器信息、内容类型、编码、内容长度等元数据。
Content-Type字段中的charset指明了网页的字符编码,对正确解码至关重要。 - 编码:正确的编码是避免中文乱码的关键。中文网页常用
UTF-8或GBK编码。 - 文本内容:将响应的字节流按照正确编码解码后得到的字符串,即我们通常需要处理的HTML文本。
- 字节内容:原始的、未经解码的响应字节流。
text和content本质上是同一份数据的不同表现形式。text是解码后的字符串,方便阅读和处理;content是原始字节,适用于处理图片、文件等非文本内容。
编码问题与解决方案
乱码是网页抓取中常见的问题,其根源通常在于编码声明与解码方式不匹配。例如,服务器返回的HTML头部可能声明编码为UTF-8,但我们的程序却错误地使用ISO-8859-1去解码,导致中文字符显示为乱码。
处理编码问题的通用逻辑是:
- 优先从响应头部的
Content-Type字段(如charset=utf-8)中提取编码信息。 - 如果头部未明确指定,则尝试从HTML文档的
<meta>标签中解析(例如<meta charset="UTF-8">)。 - 如果以上都失败,则可以使用通用编码(如
UTF-8)进行尝试性解码,或使用chardet等库进行编码检测(注:chardet不在本课白名单内,此处仅说明原理)。
下面的代码模拟了编码错误及其修正过程:
# 模拟编码错误与修正
print("=== 编码问题模拟 ===")
# 模拟场景:服务器返回的是UTF-8编码的中文内容
original_bytes = "数据科学实战".encode('utf-8') # 正确编码为UTF-8
print(f"原始字节: {original_bytes}")
# 错误解码:误用ISO-8859-1(一种单字节拉丁编码)去解码UTF-8字节流
try:
wrong_decoding = original_bytes.decode('iso-8859-1')
print(f"\n错误解码('iso-8859-1')结果: {wrong_decoding}")
except UnicodeDecodeError as e:
print(f"\n解码错误: {e}")
# 正确解码:使用UTF-8
correct_decoding = original_bytes.decode('utf-8')
print(f"正确解码('utf-8')结果: {correct_decoding}")
# 模拟一个完整的“获取-检查-重设编码”流程
print("\n--- 模拟完整处理流程 ---")
# 假设我们从某个响应中获得了以下信息
simulated_encoding = 'iso-8859-1' # 初始(可能错误的)编码判断
simulated_text = original_bytes.decode(simulated_encoding, errors='ignore') # 忽略错误解码
print(f"初始编码 '{simulated_encoding}' 解码后: {simulated_text} (出现乱码/字符丢失)")
# 修正编码
correct_encoding = 'utf-8'
print(f"修正编码为 '{correct_encoding}'")
# 重新用正确编码解码原始字节
corrected_text = original_bytes.decode(correct_encoding)
print(f"修正后内容: {corrected_text}")=== 编码问题模拟 ===
原始字节: b'\xe6\x95\xb0\xe6\x8d\xae\xe7\xa7\x91\xe5\xad\xa6\xe5\xae\x9e\xe6\x88\x98'
错误解码('iso-8859-1')结果: æ°æ®ç§å¦å®æ
正确解码('utf-8')结果: 数据科学实战
--- 模拟完整处理流程 ---
初始编码 'iso-8859-1' 解码后: æ°æ®ç§å¦å®æ (出现乱码/字符丢失)
修正编码为 'utf-8'
修正后内容: 数据科学实战这个模拟清晰地展示了乱码的产生原因和解决方法。在实际的requests库中,你可以通过response.encoding = 'utf-8'来强制指定编码,然后response.text属性会自动使用新编码重新解码原始字节内容。
综合示例:模拟网页下载与信息提取
最后,我们用一个更完整的示例来串联本节知识,模拟下载一个网页并提取其关键信息。我们将在内存中完全模拟这一过程,不进行任何真实的网络请求。
# 综合示例:模拟一个完整的网页下载与信息检查流程
import io
from http.client import parse_headers
print("=== 综合示例:模拟网页下载 ===")
# 1. 完全在内存中模拟一个HTTP响应
# 构造响应原始数据(包含状态行、头部和正文)
response_data = (
b'HTTP/1.1 200 OK\r\n'
b'Server: Apache/2.4.41\r\n'
b'Content-Type: text/html; charset=gbk\r\n' # 注意,这里模拟的是GBK编码
b'Content-Length: 83\r\n'
b'\r\n' # 空行,分隔头部和正文
b'<html><head><title>\xd6\xd0\xce\xc4\xd2\xb3\xc3\xe6</title></head><body><h1>\xc4\xe3\xba\xc3\xa3\xac\xca\xc0\xbd\xe7</h1></body></html>'
)
# 2. 手工解析这段模拟响应(这正是 http.client / requests 等库底层做的事)
# 状态行+头部 与 正文 之间以空行 \r\n\r\n 分隔
head, _, body = response_data.partition(b'\r\n\r\n')
status_line, _, header_bytes = head.partition(b'\r\n')
# 解析状态行:「版本 状态码 原因短语」三段
parts = status_line.decode('ascii').split(' ', 2)
version, status, reason = parts[0], int(parts[1]), parts[2]
# 用标准库解析头部(返回一个类似字典的对象)
headers = parse_headers(io.BytesIO(header_bytes))
# 3. 提取并展示关键信息
print(f"状态码: {status}")
print(f"状态说明: {reason}")
print("\n响应头部:")
for header, value in headers.items():
print(f" {header}: {value}")
# 4. 读取正文并正确处理编码
content_bytes = body
# 从Content-Type头部解析出编码
content_type = headers.get('Content-Type', '')
encoding = 'utf-8' # 默认值
if 'charset=' in content_type:
encoding = content_type.split('charset=')[-1].strip()
print(f"\n推断出的编码: {encoding}")
# 使用推断的编码解码内容
try:
html_text = content_bytes.decode(encoding)
print(f"\n解码后的网页文本内容(标题标签内):")
# 简单提取<title>标签内容(实际应用应使用解析库如html.parser)
start = html_text.find('<title>')
end = html_text.find('</title>')
if start != -1 and end != -1:
title = html_text[start+7:end]
print(f" 网页标题: {title}")
print(f" 正文预览: {html_text[html_text.find('<body>'):html_text.find('<body>')+60]}...")
except UnicodeDecodeError:
print(f"\n使用编码 '{encoding}' 解码失败,内容可能为乱码。")
print(f" 原始字节预览: {content_bytes[:50]}...")=== 综合示例:模拟网页下载 ===
状态码: 200
状态说明: OK
响应头部:
Server: Apache/2.4.41
Content-Type: text/html; charset=gbk
Content-Length: 83
推断出的编码: gbk
解码后的网页文本内容(标题标签内):
网页标题: 中文页面
正文预览: <body><h1>你好,世界</h1></body></html>...这个示例展示了从原始的HTTP响应字节流开始,到解析状态码、头部,最后根据头部信息正确解码出网页内容的完整过程。它揭示了网络库(如requests)底层的工作原理。在实际项目中,我们直接使用高级库可以省去这些繁琐步骤,但理解底层原理能让你在遇到问题时更加从容。
📝 动手练一练
- 概念辨析:HTTP状态码
200、404和500分别代表什么含义?如果在下载网页时遇到404状态码,通常意味着问题出在哪里(客户端还是服务器端)?遇到500状态码呢?
👉 点击查看参考答案
- 200 OK:请求成功。服务器已成功处理了请求。
- 404 Not Found:客户端错误。服务器无法找到请求的资源(如URL拼写错误、页面已删除)。问题通常出在客户端提供的资源地址上。
- 500 Internal Server Error:服务器端错误。服务器在处理请求时遇到了意外情况,无法完成请求。问题出在服务器端。
因此,遇到404应首先检查请求的URL是否正确;遇到500则通常需要等待服务器端修复或联系网站管理员。
- 动手实验:参考“编码问题与解决方案”一节的代码,尝试修改代码,模拟服务器返回GBK编码的文本“数据分析”,但程序先用UTF-8解码(会产生错误或乱码),然后再用正确的GBK解码并打印出正确结果的过程。
👉 点击查看参考答案
# 模拟GBK编码内容的解码过程
text = "数据分析"
# 用GBK编码得到字节
gbk_bytes = text.encode('gbk')
print(f"GBK编码字节: {gbk_bytes}")
# 错误地用UTF-8解码(会报错,因为字节序列不符合UTF-8规则)
try:
wrong = gbk_bytes.decode('utf-8')
print(f"UTF-8解码: {wrong}")
except UnicodeDecodeError as e:
print(f"UTF-8解码错误: {e}")
# 正确地用GBK解码
correct = gbk_bytes.decode('gbk')
print(f"GBK解码: {correct}")GBK编码字节: b'\xca\xfd\xbe\xdd\xb7\xd6\xce\xf6'
UTF-8解码错误: 'utf-8' codec can't decode byte 0xca in position 0: invalid continuation byte
GBK解码: 数据分析运行后,你会看到UTF-8解码失败并抛出UnicodeDecodeError,而GBK解码成功输出“数据分析”。
本章小结
本节我们深入探讨了网络数据获取流程中的第二个关键环节——网页下载。我们明确了其核心任务,并通过模拟的方式详细剖析了HTTP响应的核心组成部分:状态码、头部信息和内容体。重点掌握了编码问题的成因与解决方法,这是避免中文乱码的关键。
📋 行动清单
学完本节,你可以立即实践:
- 在脑海中复述一次网页下载的完整流程:发起请求 → 接收响应 → 检查状态码 → 解析头部获取编码 → 按正确编码解码内容。
- 编写一段模拟代码,构造一个包含状态码
404、头部Content-Type: text/html以及简单正文的HTTP响应数据,并模拟解析过程。 - 思考:如果网页的
Content-Type头部没有指定charset,HTML的<meta>标签里也没有指定,有哪些策略可以尝试推断出正确的编码?
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。
- 下载本节课件:网页下载模块PDF · 193KB
- 下载全套课件打包(第1-5章)ZIP · 12.8MB
- 下载全套课件打包(第6-8章)ZIP · 15MB
- 下载实战数据集:AppleStore 应用商城分析ZIP · 329KB
- 下载实战数据集:女性服装电商分析ZIP · 2.8MB
- 下载Python 数据分析环境搭建指南PDF · 2MB
- 下载Scrapy 安装教程PDF · 12.7MB
- 下载附加实战项目:AppleStore 应用商城数据分析ZIP · 0.3MB · ipynb + CSV 数据
- 下载附加实战项目:银行电话营销数据分析ZIP · 0.4MB · ipynb + CSV 数据
- 下载附加实战项目:女性服装电商评论数据分析ZIP · 2.7MB · ipynb + CSV 数据
- 下载附加实战项目:美国化学学会杂志数据分析ZIP · 34.2MB · ipynb + SQLite 数据库