[Python之路] HTTP协议复习笔记
一、HTTP请求的直观了解
我们使用网络调试助手来模拟一个TCP Server,然后使用浏览器来访问对应的IP:Port。
启动后,我们使用谷歌浏览器来访问192.168.1.8:8080:
我们可以看到浏览器一直在等待服务器返回数据。
我们可以在 网络调试助手上看到接收到的HTTP请求:
GET / HTTP/1.1 Host: 192.168.1.8:8080 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9
解释:
1.GET是浏览器向服务器请求数据的一种方式,一般有GET和POST。
2.GET后面是一个空格(不可少)
3.空格后面的"/"表示请求的内容,如果我们请求时使用http://192.168.1.8:8080/index.html,则这里就是"/index.html"。
4."HTTP/1.1"是使用的HTTP协议版本,这里是1.1版本。
5.Host是请求的目的,即服务器地址。
6.Connection是链接的类别,keep-alive是长连接。
7.User-Agent是浏览器客户端的版本信息,普通用户使用标准浏览器访问时,都会有这个字段,如果没有该字段说明可能是爬虫程序。主要用于反爬虫。
8.Accept表示浏览器可以接受的数据类型。
9.Accept-Encoding表示浏览器可以接受的数据压缩格式(用于数据量比较大时,服务器进行压缩传输)。
10.Accept-Language表示浏览器可以接受的文本语言。
二、观察HTTP的响应头
我们使用谷歌浏览器访问百度首页,并打开F12来查看交互流程:
可以在其中找到我们需要的请求头、响应头信息:
请求头:
GET / HTTP/1.1 Host: www.baidu.com Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: BAIDUID=F5E42E8A08547BB39711F21232455CB0:FG=1; BIDUPSID=F5E42E8A08547BB39711F21232455CB0; PSTM=1569579669; sugstore=1; MCITY=-75%3A; BD_UPN=12314753; H_PS_PSSID=1451_21123_29568_29221_26350; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=0; BD_CK_SAM=1; PSINO=7; BDSVRTM=137; BCLID=10618646423443952060; BDSFRCVID=g8COJeCmH6VwoRJwYAdxT9UxGmKK0gOTHllvFiwowP8T7G8VJeC6EG0Ptf8g0KubFTPRogKK0gOTH6KF_2uxOjjg8UtVJeC6EG0Ptf8g0M5; H_BDCLCKID_SF=tJkD_I_hJKt3qn7I5KToh4Athxob2bbXHDo-LIvjfpOcOR5JhfA-3R-e046fXxobMNP8-4nTalvvhb3O3M7ShbKzMqJy-h8OQnvxVUQF5l8-sq0x0bOte-bQypoa0q3TLDOMahkM5h7xOKQoQlPK5JkgMx6MqpQJQeQ-5KQN3KJmfbL9bT3YjjISKx-_J6LeJRbP
响应头:
HTTP/1.1 200 OK Bdpagetype: 1 Bdqid: 0xda48b61a00089ab4 Cache-Control: private Connection: Keep-Alive Content-Encoding: gzip Content-Type: text/html Cxy_all: baidu+1b1b86535b4f42dc5821baca812fd9c4 Date: Wed, 27 Nov 2019 15:07:07 GMT Expires: Wed, 27 Nov 2019 15:06:46 GMT Server: BWS/1.1 Set-Cookie: delPer=0; path=/; domain=.baidu.com Set-Cookie: BDSVRTM=0; path=/ Set-Cookie: BD_HOME=0; path=/ Set-Cookie: H_PS_PSSID=1451_21123_29568_29221_26350; path=/; domain=.baidu.com Strict-Transport-Security: max-age=172800 Traceid: 1574867227025529191415729021921377426100 Vary: Accept-Encoding X-Ua-Compatible: IE=Edge,chrome=1 Transfer-Encoding: chunked
响应头解释:
1.HTTP/1.1 200 OK表示我们要请求的页面存在,可以成功返回。
2.Content-Encoding表示返回数据使用的压缩格式为gzip。
3.Date表示服务器当前的时间。
4.Content-Type表示返回数据的格式为text/html,这里也可能附带编码,例如utf-8。
5.Server表示服务器的名称和版本,这里的BWS/1.1是百度服务器的名称和版本。一般情况下为Apache、Nginx、Tengine等。
6.Set-Cookie见后面的详细解释...
Set-Cookie解释:
实际例子:假设我们使用JD搜索了"刮胡刀",为什么第二天我们再次打开JD的时候,就会给我们推送很多刮胡刀的广告。
1.第一次访问JD的时候,我们浏览器发送的请求头是很短的,只有最基本的一些信息,例如请求目录、浏览器版本等。
2.JD服务器再响应时,会检查请求头中是否存在Cookie,如果不存在,则会在响应头中使用Set-Cookie关键字传输一些信息给浏览器。例如上述响应头中的"Set-Cookie: H_PS_PSSID=1451_21123_29568_29221_26350; path=/; domain=.baidu.com"
3.Set-Cookie中的H_PS_PSSID、path和domain都是key,而后面的1451_***_26350等都是value。浏览器会将这些Cookie数据存在本地缓存中,domain表示下次访问关于baidu域名的网页时,将携带这些Cookie在请求头中。
4.第二天访问JD时,浏览器就将前一次存放的Cookie信息放在请求头中,传递给了服务器。其中的一些Cookie可能就是代表这个未登陆的用户ID(例如上述请求头中Cookie里的BAIDUID),服务器通过这个Cookie中携带的ID,就知道是昨天搜索刮胡刀的人,那么就可以给他推送刮胡刀广告。
三、查看响应体
在响应头后面空一行(浏览器就是根据这个空行来分隔响应头和响应体)之后,就是响应体。响应体主要是服务器返回的真正的页面信息,一般是html文本。
我们可以在F12中看到响应体:
四、使用python实现一个简单的静态Server
import socket leo_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 监听端口8900 leo_sock.bind(('192.168.1.8', 8900)) leo_sock.listen(5) print('starting....') conn, addr = leo_sock.accept() print(conn) print('client addr', addr) print('ready to read msg') client_msg = conn.recv(1024) print('client msg: %s' % client_msg) # 换行符是\r\n reponse_msg = "HTTP/1.1 200 OK\r\n\r\n<h1>hahaha</h1>" # TCP网络传输二进制数据 conn.send(reponse_msg.encode(encoding="utf-8")) # 关闭连接 conn.close() # 关闭socket leo_sock.close()
使用浏览器访问192.168.1.8:8900:
服务器成功返回了内容。