1、HTTP和HTTPS的区别

一、 核心概念速览

  1. HTTP (超文本传输协议)

    • 它是互联网上应用最广泛的网络协议,用于在 Web 浏览器和服务器之间传递信息。
    • 致命弱点明文传输。在 HTTP 下,你的数据(包括密码、银行卡号)就像是在寄一张没有任何包装的明信片,网络链路上的任何人(路由器、基站、网络运营商、黑客)都可以轻易截获并看到上面的内容。
  2. HTTPS (安全超文本传输协议)

    • 简单来说,HTTPS = HTTP + SSL/TLS 协议
    • 它在 HTTP 和底层的 TCP 请求之间加了一层安全层(SSL/TLS)。数据在发送前会被加密,接收后才会被解密,相当于给明信片加了一个上了锁的坚固保险箱。

二、 从网络底层模型看区别(OSI 模型层级)

要理解它们的底层区别,首先要知道网络通信是分层的。

  • HTTP 的传输过程: 应用层 (HTTP: 组装明文报文) -> 传输层 (TCP: 建立三次握手,分发数据) -> 网络层 (IP) -> 物理链路。 在这里,HTTP 直接把明文数据交给了 TCP 通道。

  • HTTPS 的传输过程: 应用层 (HTTP) -> 安全层 (SSL/TLS: 对明文进行加密) -> 传输层 (TCP) -> 网络层 (IP) -> 物理链路。 HTTP 被夹在中间,它不知道数据被加密了,TCP 也不知道它传输的是加密数据。核心的魔法全在中间的 SSL/TLS 层


三、 HTTPS 底层加密原理:SSL/TLS 握手(核心!)

如果单纯用一种加密方式,会有很多问题。HTTPS 底层非常聪明地结合了对称加密非对称加密哈希算法。它的底层建立连接的过程(TLS 握手)是这样的:

1. 为什么不能只用一种加密?
  • 对称加密(如 AES):加密解密用同一把钥匙。速度极快,适合传输大量数据。但问题是:你怎么安全地把这把钥匙送给对方?(如果钥匙被拦截,加密就废了)。
  • 非对称加密(如 RSA):有公钥(公开)和私钥(只有自己有)。公钥加密的数据,只有私钥能解。极其安全,但计算极其缓慢,用来传输大文件电脑会卡死。
2. HTTPS 的神仙操作:混合加密机制

HTTPS 把这两种结合了起来:用缓慢的“非对称加密”去安全地协商出一把“对称加密的钥匙”,然后用这把“对称加密的钥匙”去飞速加密后续聊天内容。

具体步骤如下(底层握手过程):

  1. 打个招呼 (Client Hello / Server Hello)
    • 客户端(浏览器/Python 程序):向服务器打招呼,发送自己支持的加密算法列表和一个随机数 A
    • 服务器:选定一种加密算法,生成一个随机数 B,连同自己的数字证书(里面包含公钥)一起发给客户端。
  2. 验证身份并生成“第三个随机数”
    • 客户端:验证你的证书是不是合法的(有没有过期,是不是伪造的)。验证通过后,客户端生成一个随机数 C(Pre-master secret)
    • 客户端:使用证书里的公钥,把随机数 C 加密,发送给服务器。
  3. 计算真正的对称密钥
    • 服务器:用自己死死捏在手里的私钥,解密得到了随机数 C
    • 这个时候,客户端和服务器手里都同时拥有了:随机数 A、随机数 B、随机数 C。双方使用相同的算法,把这三个随机数混合,生成一把最终的**“对称密钥”(Session Key)**。
  4. 安全加密通讯开始
    • 从此以后,双方就用这把新生成的、且只有他俩知道的**“对称密钥”**,使用对称加密算法对 HTTP 明文进行极其快速的加密和解密。

四、 HTTP 与 HTTPS 的直观数据对比

特性 HTTP HTTPS
底层协议 建立在 TCP 之上 建立在 SSL/TLS 之上,再建立在 TCP 之下
端口号 默认 80 端口 默认 443 端口
安全性 明文传输,极不安全(易被窃听、篡改、伪造) 密文传输,极高安全性(防窃听、防篡改、防伪造)
资源消耗 较小(只需要 TCP 三次握手) 较大(TCP 三次握手 + TLS 多次握手带来 CPU 和时间消耗)
成本 免费 数字证书通常需要向 CA 机构购买(现在也有 Let's Encrypt 等免费证书)

五、 在 Python 中是如何体现底层的?

既然你要学习 Python,我们可以看看在代码层面,这两者的底层差异。

1. Python 的 Socket 层(最底层)

在 Python 中,所有的网络请求最终都会调用内置的 socket 模块。

  • HTTP: 就是建立一个普通的 TCP Socket 连接对方的 80 端口。
    import socket
    
    # 原生 TCP 建立 HTTP 连接
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(('example.com', 80))
    
    s.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") # 发送的就是这段明文字符串

  • HTTPS: 在普通的 TCP Socket 外面,必须套一层 ssl 模块的包装
    import socket
    
    import ssl
    
    # 1. 先建立普通的 TCP 连接 (443端口)
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    s.connect(('example.com', 443))
    
    # 2. 核心区别:用 ssl 模块对这个普通 socket 进行包装,进行 TLS 握手!
    
    context = ssl.create_default_context()
    
    secure_socket = context.wrap_socket(s, server_hostname='example.com')
    
    # 3. 现在再发送数据,底层会自动用协商好的密钥加密数据
    
    secure_socket.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
2. Python 的高级库(如 requests

在平时写 Python (比如爬虫、Web 后端) 时,你通常会用到 requests 这个强大的库。

  • 当你请求 http:// 时,requests 底层使用的就是普通 socket。
  • 当你请求 https:// 时,requests 底层会自动调用 ssl 模块帮你完成那些复杂的证书验证、密钥交换等握手过程。
  • 这也是为什么初学 Python 爬虫时,如果遇到自签名证书的 HTTPS 网站会报错 SSLError,因为底层的 ssl 模块验证证书失败了。你通常需要加上 requests.get('https://...', verify=False) 来强行关闭证书验证(实际开发中不推荐,因为这会失去 HTTPS 防伪造的意义)。

总结: 理解 HTTP 和 HTTPS 的本质,其实就是理解网络数据是不是在“裸奔”。在后续学习 Python 的 requests 库、urllib 库或者开发 Django/Flask/FastAPI 后端时,你虽然不用自己手写加密算法,但理解了这层原理,你就能轻易看懂为什么有时会报证书错误,为什么 HTTPS 爬虫比 HTTP 稍微慢一点点,以及为什么要配置 SSL 证书。


2、HTTP状态码

HTTP 状态码是客户端(比如你的 Python 爬虫/浏览器)和服务器之间的“暗号”。它是3位数,分为五大类(五大门派)。

一、 核心分类速记(五大门派)

  • 1xx (信息类):“请求我已收到,还在处理中,请稍等。”(在 Python 开发中几乎不可见,底层 TCP/HTTP 处理掉了)。
  • 2xx (成功类):“没问题,搞定了!”这是你最希望看到的。
  • 3xx (重定向):“你要找的东西搬家了,去新的地址找吧。”
  • 4xx (客户端错误):“你的请求有问题,是你弄错了!”(比如网址错了、没权限)。
  • 5xx (服务端错误):“哎呀,服务器崩溃了 / 出 Bug 了!”(对面的错,你的请求没问题)。

二、 Python 爬虫/后端 开发中最常遇到的状态码剖析

🟢 1. 成功门派 (2xx)
  • 200 OK (一切正常)
    • 底层含义:服务器成功处理了请求,并且把你想要的数据(网页 HTML、JSON 数据等)放在了响应体里发给你了。
    • Python 应对:当你看到 response.status_code == 200,意味着爬虫拿到了数据,可以开始用 BeautifulSoup 或正则去解析了。如果是写后端,这意味着你成功地向用户返回了页面。
🔵 2. 搬家门派 (3xx)
  • 301 Moved Permanently (永久重定向)
    • 底层含义:你要找的网址 A 已经永久作废,以后请去网址 B 找。搜索引擎看到这个会更新自己的索引。
  • 302 Found / Temporary Redirect (临时重定向)
    • 底层含义:你要找的东西暂时放在了网址 B,以后可能还会回网址 A。比如你在未登录状态下访问淘宝购物车页面,它会给你一个 302,把你临时重定向到登录页面。
    • Python 应对:极其省心!在 requests 库中,遇到 3xx 状态码默认是自动处理的(allow_redirects=True)。Python 会自动默默去请求那个新网址,最终给你返回 200。如果你想拦截并看看它重定向到哪了,可以设置 allow_redirects=False,然后通过 response.headers['Location'] 查看新网址。
🟠 3. 你的锅 (4xx) - 最常因为反爬或代码写错遇到
  • 400 Bad Request (错误的请求)
    • 底层含义:服务器看不懂你发过来的数据。通常是因为你的参数格式不对。
    • Python 应对:检查你 requests.post() 里面传的 data 或 json 字典,是不是少了必填字段,或者类型错了(人家要数字你给了字符串)。
  • 403 Forbidden (禁止访问) - 爬虫的噩梦
    • 底层含义:服务器懂你的请求,地址也对,但服务器果断拒绝了你,不给你看!
    • Python 应对:在爬虫中这是最典型的被反爬拦截了
      • 原因 1:你没带 User-Agent,服务器一眼看出你是没穿马甲的 Python 脚本。解决:伪装请求头 headers={'User-Agent': '...'}
      • 原因 2:需要登录(你的 Cookie 没带或者过期了)。
      • 原因 3:你的 IP 抓取太快,被服务器拉黑了。解决:使用代理 IP 或放慢速度 (time.sleep())。
  • 404 Not Found (未找到)
    • 底层含义:服务器上根本没有这个 URL 对应的资源。也许是文章被删了,也许是你把网址拼错了。
    • Python 应对:检查你的代码逻辑,是不是拼接 URL 时多了一个斜杠 / 或者少了一个字母。
🔴 4. 对方的锅 (5xx)
  • 500 Internal Server Error (服务器内部错误)
    • 底层含义:你的请求完全合法,但对面的服务器(比如对面的 Python Django 后端程序)在处理你的请求时,代码出 Bug 报错崩溃了
    • Python 应对:如果是你自己的网站,赶紧去服务器上看 Python 报错日志(Traceback)。如果是爬别人的网站,你无能为力,这说明这网站本身就有 Bug,只能等对方程序员修复。
  • 502 Bad Gateway / 503 Service Unavailable
    • 底层含义:网关错误或服务不可用。通常是因为对方服务器流量太大被挤爆了、正在被 DDos 攻击、或者后端程序挂掉正在重启。
    • Python 应对:这通常是临时性故障。在爬虫代码里,遇到 502/503 应该等待几秒后再次重试

三、 Python 代码中的终极杀招

如果你用的是最常用的 requests 库,可以通过一行非常优雅的代码来处理这些错综复杂的状态码,避免写一大堆 if-else

import requests

response = requests.get("https://example.com/api/data")

# 终极杀招:只有遇到 4xx 或 5xx 的错误时,才会直接主动报错 (抛出 HTTPError 异常)!

# 如果是 200 或 300,就当什么事都没发生,继续往下执行。

response.raise_for_status()

# 如果没报错,说明一定是成功的,你可以放心大胆地提取数据了

print(response.json())

3、TCP三次握手和四次挥手

一、 三次握手 (建立连接) —— “你在吗?我在。好,我开始说了。”

为什么要握手? TCP 是一种**“可靠”**的协议。在发送任何真正的数据(比如 HTTP 报文里的网页内容)之前,客户端(你的电脑)必须百分之百确定:对面的服务器活着,并且它准备好接我的数据了。

假设客户端是 A(你),服务器是 B(淘宝服务器)。

  • 第一次握手(A -> B): “喂,能听见吗?”
    • 专业术语:客户端发送 SYN (Synchronize Sequence Numbers) 包。
    • 大白话:A 问 B:“哥们,你在吗?我想跟你建立连接传数据了。”
  • 第二次握手(B -> A): “能听见,你准备好发了吗?”
    • 专业术语:服务器发回 SYN + ACK 包。
    • 大白话:B 收到 A 的消息了,并且回答:“我在的!能听见!你随时可以发!”(ACK 是确认 A 的消息,SYN 是 B 反过来问 A 准备好没)。
  • 第三次握手(A -> B): “我也准备好了,咱们开始吧!”
    • 专业术语:客户端回一个 ACK 包。
    • 大白话:A 收到 B 的回复了,A 回复 B:“好的!我也准备好了,连接正式建立!”

为什么一定是三次?两次不行吗? 绝对不行。你想想如果只有两次: A 喊了一句“能听见吗?”,B 回了一句“能听见”。 这时候 B 知道“A 能发消息,我能收消息”。但是 B 根本不知道 A 到底有没有听见自己的那句回复啊! 万一 B 的回复在网络上丢了呢?B 傻傻地在那等 A 传数据,A 却因为没收到回复而放弃了。 所以,只有 A 再回一句“我听见了你的回复”,双方才能 100% 确认彼此都能发消息并且能听到对方的声音

Python 视角: 当你写下 s = socket.socket() 然后 s.connect(('taobo.com', 80)) 时,这看似只有一毫秒的代码,底层就是在疯狂进行这三次握手的过程!如果握手失败(比如淘宝服务器崩了或者没开 80 端口),Python 就会在那行代码直接抛出 ConnectionRefusedError 报错。


二、 四次挥手 (断开连接) —— “我讲完了,我要挂了。”

既然数据传完了,为了不占用宝贵的服务器资源,通道必须关掉。断开连接的过程比建立连接要复杂一点,需要四次

咱们还是拿 A(客户端)和 B(服务器)举例。第一次挥手(A -> B): “B 哥,我说完了,我要挂断了啊。”

    • 专业术语:A 发出 FIN (Finish) 包。
  • 第二次挥手(B -> A): “好的,我知道你要挂了。(等我一下哈,我这还有点数据没传完呢)”
    • 专业术语:B 回复 ACK 包。
    • 注意:此时,A 已经不能再发送新的数据给 B 了,但 B 还可以继续把没发完的数据发给 A。这叫做*“半关闭状态”**。*
  • 第三次挥手(B -> A): “A 老弟,我这边的数据终于也发完了,我这边也要挂断了啊。”
    • 专业术语:B 等自己数据传完后,也发出一个 FIN 包。
  • 第四次挥手(A -> B): “收到,再见!”
    • 专业术语:A 回复 ACK 包。
    • 此时,挥手全部结束!连接彻底关闭。

为什么挥手需要四次,而握手只要三次? 核心原因出在 “半关闭状态”。 建立连接时,B 收到 A 的请求后,可以把 SYN(我想连接你)和 ACK(我收到了你的请求)合并成一个包发回去(就是第二次握手)。 但是在断开连接时,A 说完“我要挂了”,B 只能先赶紧回复一个 ACK(“我知道了”),因为 B 可能还有没传完的网页数据。B 必须等自己把所有最后的数据顺着管子都传干净了,才能再发那个 FIN(“我也要挂了”)。所以确认包(ACK)和断开包(FIN)不能合并,也就分成了四步。

Python 视角: 在爬虫或者 Web 开发中,通常在你调用 socket.close() 或者 response.close() 时,底层网络栈就会默默地为你执行这优雅的四次挥手过程,把连接干干净净地释放掉,不留下一片云彩。


4.RESTful API

在 Python 开发里(尤其是用 Flask、Django、FastAPI 写后端,或者用 requests 写爬虫),你每天都会和它打交道。

原理篇:它到底是什么?

首先,API (应用程序接口) 就像是餐厅里的服务员。 你要点菜(前端请求数据),你不能直接冲进后厨自己炒菜(直接操作数据库),你得通过服务员(API)把你的需求传给后厨,然后服务员再把做好的菜端给你。

而 RESTful 不是一种新技术,也不是一种协议。它是一种约定俗成的“江湖规矩”,或者说是一种代码设计风格

大家为了不让各种乱七八糟的 API 把人搞疯,就商量了一套极其优雅的规范:把网络上的所有东西,都看作是“资源(Resource)”,然后用 HTTP 自带的动词去操作它们。


核心规矩(三原则)

如果一个 API 遵循了 RESTful 风格,它必须符合这三个最明显的特点:

1. 网址(URL)只代表“名词”(这是什么资源)

在非 RESTful 时代,大家起名字很随意:

  • ❌ 坏例子:
    • 获取所有用户:https://api.com/getAllUsers
    • 创建一个用户:https://api.com/createUser
    • 删除一个用户:https://api.com/deleteUser?id=1

在 RESTful 时代,URL 里绝对不能包含动词,只能是复数名词!

  • ✅ 好例子:
    • 资源就是用户:https://api.com/users
    • 某一个特定的用户:https://api.com/users/1
2. 用 HTTP “动词”来代表动作(增删改查)

既然 URL 里没有动词了,服务器怎么知道你是想查数据,还是想删数据呢? 这就用到了 HTTP 协议自带的 4 个核心请求方法(也是你在 Python 爬虫里最常用的):

  • GET(查):获取资源。
  • POST(增):新建资源。
  • PUT(改):更新整个资源(有时候也用 PATCH 局部更新)。
  • DELETE(删):删除资源。

神仙配合来了! 结合前面两点,看看 RESTful 有多优雅:

客户端发出的请求 (HTTP 动词 + URL 名词) 这句话的含义 后端数据库操作
GET /users 服务员,给我看看全部用户名单 SELECT * FROM users
GET /users/1 服务员,我要看 ID=1 的那个用户 SELECT * FROM users WHERE id=1
POST /users 服务员,我要新建一个用户(数据在请求体里) INSERT INTO users ...
PUT /users/1 服务员,把 ID=1 的用户资料全部替换 UPDATE users SET ... WHERE id=1
DELETE /users/1 服务员,把 ID=1 的用户给我删了 DELETE FROM users WHERE id=1

你看!同样的网址 /users/1,你换一个请求动词,它执行的就是完全不同的操作。这就是 RESTful 的魅力:极度清晰、优雅

3. 必须精准使用 HTTP 状态码(我们刚学过的!)

服务器处理完后,不应该随便返回一个 "操作失败" 的文字。它必须依靠我们上一节提到的 HTTP 状态码 来汇报。

如果客户端发送了 POST /users(新建用户):

  • 成功了:应该返回状态码 201 Created(专门表示新建成功),而不是只返回 200。
  • 如果前端少传了必填参数:应该返回 400 Bad Request
  • 如果前端没带 Token 没权限:应该返回 403 Forbidden
  • 如果想删除 DELETE /users/99,但早就没这个人了:应该返回 404 Not Found

实战篇:在 Python 里怎么体现?

视角 1:如果你是写爬虫的(客户端,用 requests

遇到 RESTful 的接口,你的感受会极度舒适。如果你想抓取文章,又想给文章点赞(假设点赞是新建一个 like 关系),你只需要:

import requests

# 查文章:用 GET

res = requests.get('https://api.example.com/articles/123')

print("文章内容:", res.json())

# 给这篇文章点赞(新建一个资源):换成 POST!URL不变!

my_data = {'user_id': 999}

res_like = requests.post('https://api.example.com/articles/123/likes', json=my_data)

if res_like.status_code == 201:

print("点赞成功!")
视角 2:如果你是写后端的(服务端,用 FastAPI

现在最火的 Python 后端框架 FastAPI,天生就是强迫你写出完美的 RESTful 接口的。

from fastapi import FastAPI

app = FastAPI()

# 当有人向 /users 发送 GET 请求时,执行这个函数(查)

@app.get("/users")

def get_users():

return [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]

# 当有人向 /users 发送 POST 请求时,执行这个函数(增)

@app.post("/users", status_code=201) # 严格规范回复 201 状态码!

def create_user():

return {"message": "用户创建成功!"}

总结一下: RESTful API 就是一套“有教养的接口规范”。 它强制要求:利用 URL 讲清楚“目标是谁”,利用 HTTP 动词讲清楚“你要干嘛”,利用 HTTP 状态码讲清楚“结果如何”。

这就是现代前端(Vue/React)和后端(Python/Java/Go)分离开发时,最核心的交流语言!


5.GET和POST的区别

这是最常被问到、也是平时写爬虫和做后端开发最常用的两个 HTTP 方法。


一、 核心区别大白话

特性 GET (获取资源) POST (提交/新建资源)
主要目的 向服务器数据 (查网页内容、读文章) 向服务器数据 (注册账号、发帖、点赞)
参数放哪? 全暴露在网址 (URL) 里
例子:?name=root&page=1
藏在 HTTP 报文里的“身体(Body)”里
网址里干干净净,外人看不见参数。
能传多少数据? 非常少! (受浏览器 URL 长度限制,通常最多 2KB) 无限大! (几 GB 的超大视频文件也能通过 POST 发)
能不能传文件? 绝对不行!只能传极短的文本参数。 能! 这是它最重要的绝招之一。
安不安全? 超级危险! 万万不可用 GET 传密码,别人看一眼你浏览器的网址栏就知道密码了。 相对安全。(参数不在网址上,但这不代表绝对安全,必须配合 HTTPS 才能真正防抓包窃听)
收藏与刷新 可以放进浏览器收藏夹;随便刷新也不会有提示。 不能收藏为书签;如果你按 F5 刷新一个 POST 页面(比如刚付完款),浏览器会弹窗警告:“确认要重新提交表单吗?”

二、 在 Python 爬虫里,这到底意味着什么?

当你用 Python 写爬虫的时候,这是你必须要跨过的第一道坎。

1. 爬 GET 请求的网页
import requests

# 比如你在百度搜 Python,网址是 https://www.baidu.com/s?wd=python

url = "https://www.baidu.com/s"

params = {

"wd": "python", # 搜索词 kwargs

"pn": 10 # 第2页

}

# requests 的 get 方法,参数用 params= 传进去,它会自动帮你拼接到 URL 后面

response = requests.get(url, params=params)
2. 爬 POST 请求的接口 

一旦涉及“登录提交账号密码”,或者“提交大段文字去翻译”,网站绝不会用 GET,绝对是用 POST!因为数据量大,而且要藏在请求体(Body)里。

import requests

url = "https://api.example.com/login"

# 你的账号密码数据,要装进字典里

user_data = {

"username": "admin",

"password": "super_secret_password"

}

# requests 的 post 方法,核心不同点:参数用 data= 或者 json= 传进去!

# 它会把这块数据塞进 HTTP 报文的心脏(Body)里,静悄悄地发过去。

response = requests.post(url, data=user_data)

# (如果对方接口要求 JSON 格式,就把 data= 换成 json=)

三、 经典误区纠正

很多新手会被一些过时的教程误导。咱们说两个真实的底层原理:

  1. “POST 比 GET 更安全吗?”

    • 错! 如果你不套上咱们之前讲的 HTTPS(SSL锁),它俩一样都是明文裸奔
    • GET 的数据只不过是写在前额头(URL)上,POST 的数据写在肚皮(Body)上。如果不穿衣服 (没有 HTTPS) ,黑客抓包工具一打开,不论额头还是肚皮上的明文,一览无余。
  2. “GET 和 POST 在底层的 TCP 连接上有区别吗?”

    • 网上有些八股文会说:“GET 请求只发一个 TCP 数据包,POST 会发两个 TCP 数据包(先发头部,再发身体)”。
    • 这也是错的/过时的! HTTP 是一层协议,底层的 TCP 怎么打包纯粹看浏览器或客户端的具体实现(比如 requests 库发 POST 就只用一个包发完,根本不发两次)。不要死记硬背这个。本质上,它们都是通过那一条建立好的 TCP 通道在传递数据罢了。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐