Http协议简介
因为最近刚刚接触了python爬虫,想要系统的学习一下,在初次使用requests库时有一些无法理解的地方,于是就去简要了解了一点点http协议的基础知识。
Hyper Text Transfer Protocol 超文本传输协议
基于 TCP/IP 协议簇来传递数据,位于应用层的协议之一
TCP建立连接——三次握手
- 客户端(client)发送位码syn=1,随机产生 seq_number 的数据包到服务器(server),服务器由 syn=1 了解该客户端要求建立连接
- 服务器收到请求后,向客户端发送 ack_number = seq_number+1,syn=1,ack=1,随机产生 seq_number 的数据包到客户端
- 客户端收到数据包,通过 ack_number 确定正确,以及确认 ack 是否为1;均满足后客户端发送 ack_number = seq_number+1,ack=1到服务器,服务器收到后确认ack_number和ack=1正确后,建立连接
客户端和服务器就像我们打电话时,客户端先问:“你听得到吗?”服务器回答:“听得到,你呢?”客户端说:“我听得到。”然后再开始对话。
Http message (Http报文)
所有 Http报文 都可以分成两类:request message (请求报文) 和 response message (响应报文)。请求报文会向Web服务器请求一个动作,响应报文会将请求的结果返回给客户端。请求和相应报文的基本报文结构相同。
Http Requests (Http请求)
在客户端与服务器三次握手成功后,客户端就可以向服务器发送 Requests请求,请求报文的格式如下:
<method> <request-URL> <version>
<headers>
<entity-body>
比如在Edge浏览器中访问百度时,发送的第一个请求如下:
由 burpsuite 抓包获得
GET / HTTP/1.1
Host: www.baidu.com
Sec-Ch-Ua: "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"
Sec-Ch-Ua-Mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5
Connection: close
我们对该请求进行逐步分析:
Request Attributes(请求属性)
第一行是 请求行:
GET / HTTP/1.1
其中 GET 是请求的方法(Method); / 是请求的路径(URL); HTTP/1.1 表明该请求是基于 HTTP1.1 版本(Version)。
Http method
有一些常用的 http 方法:
方法 | 描述 |
---|---|
GET | 从服务器获取一份文档 |
HEAD | 只从服务器获取文档的首部 |
POST | 向服务器发送需要处理的数据 |
PUT | 将请求的主体部分存储在服务器上 |
TRACE | 对可能经过代理服务器传送到服务器上的报文进行追踪 |
PATCH | 向服务器提交局部修改请求 |
DELETE | 从服务器上删除一份文档 |
在较早的http版本中,一些方法会缺失。
除了上述方法之外,服务器还可能会实现一些自己的请求方法。
URL
URL 是浏览器寻找信息时所需的资源位置,常见的URL语法如下:
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
组件 | 描述 |
---|---|
scheme | 访问服务器时使用的协议,如https协议 |
user | 服务器的用户名,默认为匿名 |
password | 服务器的密码 |
host | 服务器的主机名或ip地址 |
port | 服务器正在监听的端口号,Http的默认端口号为80,Https的默认端口号为443 |
path | 服务器上资源的本地名 |
params | 指定输入参数,为键值对 |
query | 传递参数,为键值对 |
frag | 资源片段的名字 |
如在百度中搜索python时,url如下:
https://www.baidu.com/s?wd=python
其中
- scheme = https
- user 省略,则默认为匿名
- host = www.baidu.com
- port 省略,则取 https的默认端口号 443
- path = /s
- params 省略
- query = 'wd=python'
- frag 省略
这也是我们见到的大多数https的 url格式:
https://<host>/<path>?<query>
Http version
常见的http版本有:
- HTTP 0.9
- HTTP 1.0
- HTTP 1.1
- HTTP 2.0
(感觉基本上都是 http 1.1 和 http 2.0 了)
Request Headers(请求首部)
从第二行开始到结尾,就都是 请求的首部:
Host: www.baidu.com
Sec-Ch-Ua: "Chromium";v="92", " Not A;Brand";v="99", "Microsoft Edge";v="92"
Sec-Ch-Ua-Mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5
Connection: close
首部都是 "名字:值" 的形式,如:
Host: www.baidu.com
即服务器的域名为 www.baidu.com,也因此请求消息中的URL并没有传递主机名。之所以这样设计是因为虚拟主机技术的发展,使得同一台物理服务器上可以存在多个虚拟主机,而他们共享一个ip地址。使用host字段传递主机名,就可以将请求发往同一个服务器上的不同网站。
如:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.73
User-Agent这一项在爬虫中修改较多,该字段是告诉服务器用户是使用何种工具发送的请求。这个示例是edge浏览器的值,不同的浏览器有不同的值。
Http Response (Http响应)
在接收到客户端发送的 request 请求后,服务器端会返回一个 response相应给客户端。响应报文的格式如下:
<version> <status> <reason-phrase>
<headers>
<entity-body>
例如在发送完上述请求给百度服务器后,返回的 response 如下:
burpsuite 抓包获取
HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0xa6f9ca590000f9cd
Cache-Control: private
Content-Type: text/html;charset=utf-8
Date: Mon, 16 Aug 2021 10:02:09 GMT
Expires: Mon, 16 Aug 2021 10:01:13 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Server: BWS/1.1
Set-Cookie: BAIDUID=DFF935C15B376DD72F4785A559548401:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=DFF935C15B376DD72F4785A559548401; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1629108129; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BAIDUID=DFF935C15B376DD7F38BDD53843DD693:FG=1; max-age=31536000; expires=Tue, 16-Aug-22 10:02:09 GMT; domain=.baidu.com; path=/; version=1; comment=bd
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=34399_34380_34370_34377_34004_34092_34094_26350_34419_34246_34390_34366; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1629108129043925146612031870363097954765
X-Frame-Options: sameorigin
X-Ua-Compatible: IE=Edge,chrome=1
Connection: close
Content-Length: 308542
<html代码块>
Response Attributes(响应属性)
第一行是响应的属性:
HTTP/1.1 200 OK
其中 HTTP/1.1 是HTTP协议的版本,与请求中的版本一致; 200 是状态码,这里表示操作成功; OK 为状态码200提供了文本形式的解释,表明操作成功。
stauts(状态码)
状态码可以告诉客户端,在请求数据过程中发生了什么事情。状态码的分类如下:
整体范围 | 已定义范围(可能不完整) | 分类 |
---|---|---|
100 ~ 199 | 100 ~ 102 | 信息提示 |
200 ~ 299 | 200 ~ 208 | 成功 |
300 ~ 399 | 300 ~ 308 | 重定向 |
400 ~ 499 | 400 ~ 417、421、422、424、425、426 | 客户端错误 |
500 ~ 599 | 500 ~ 508、511 | 服务器错误 |
如果收到了不认识的状态码,可能是有人将其作为当前协议的扩展定义的,可以根据其所处范围来判断分类
reason-phrase(原因短语)
为状态码提供文本形式的解释,如 状态码200 对应的 原因短语 是 "OK",状态码404 对应的 原因短语 是"Not Found"
Response Headers(响应首部)
响应的首部 和 请求的首部 类似,均是采用键值对的形式,不同键值对之间以换行符('\n')隔开。
entity-body(实体部分)
该部分是可选部分,在 request 和 response 中均可存在。
在本次示例response中,实体(entity-body)部分为百度首页的html代码:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="always" name="referrer">
<meta name="theme-color" content="#2932e1">
<meta name="description" content="全球领先的中文搜索引擎、致力于让网民更便捷地获取信息,找到所求。百度超过千亿的中文网页数据库,可以瞬间找到相关的搜索结果。">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="search" type="application/opensearchdescription+xml" href="/content-search.xml" title="百度搜索" />
<link rel="icon" sizes="any" mask href="//www.baidu.com/img/baidu_85beaf5496f291521eb75ba38eacbd87.svg">
<link rel="dns-prefetch" href="//dss0.bdstatic.com"/><link rel="dns-prefetch" href="//dss1.bdstatic.com"/>
<link rel="dns-prefetch" href="//ss1.bdstatic.com"/><link rel="dns-prefetch" href="//sp0.baidu.com"/>
<link rel="dns-prefetch" href="//sp1.baidu.com"/><link rel="dns-prefetch" href="//sp2.baidu.com"/>
<title>百度一下,你就知道</title>
<!--省略:css文件-->
</head>
<!--太长省略-->
</html>
除了 html代码之外,视频、音频、json文件、……等多种文件均可置于实体部分中,作为request中上传的数据或response中获取的数据。
TCP断开连接——四次挥手
- 客户端(client)发送位码fin=1,随机产生 seq_number = a 的数据包到服务器(server),服务器由 fin=1 了解该客户端要断开连接
- 服务器收到请求后,向客户端发送 ack_number = a+1,ack=1,随机产生 seq_number = b 的数据包到客户端,表面收到了客户端的断开连接请求
- 服务器随后发送位码fin=1,发送 ack_number = a+1,ack=1,随机产生 seq_number = c 的数据包到客户端
- 客户端收到数据包,通过 ack_number 确定正确,以及确认 ack和fin 是否为1;均满足后客户端发送 ack_number = c+1,seq_number = a+1, ack=1到服务器,服务器收到后确认ack_number和ack=1正确后,断开连接
- 客户端等待一定时间后,若服务器无报文到达,则说明自己的响应成功到达服务器端,客户端也断开连接
仍然存在的一些问题
在学习过程中,仍然存在一些问题,暂时无法理解,先记录下来,今后的学习中有机会再来弄懂。
- 百度主页的url是 https://www.baidu.com/ , 主机名(host) 是 www.baidu.com,资源的本地名(path) 是 /;但是 / 是服务器根目录,并不是一个文件,是服务器自动重定向到index.html之类的文件吗?
- Mozilla/5.0、AppleWebKit/537.36、……等都是浏览器内核的版本号,为什么edge浏览器发送的user-agent要包括它没用到的内核?