文章目录
HTTP协议介绍
HyperText Transfer Protocol
是浏览器与web服务器通信的格式。
基于TCP协议,浏览器作为客户端,web服务器作为服务端。
因此,通信的时候有下面的流程:
- 通过DNS——域名解析服务器将www.baidu.com这样的域名解析成ip地址
- 获得百度的ip地址,一般端口号默认成80,浏览器与web服务器请求连接
- 客户端根据http协议按照一定格式发送请求
- web服务器向自己的数据中请求获取资源
- 数据返回资源给web服务器
- web服务器按照http协议的格式把自己得到的资源给客户端
URL
Uniform Resource Locator=网址
网址由以下内容组成:
- 协议:https://是对http://的加密版。而且http的默认端口号是80,而https的默认端口号是443。
- 域名:www.baidu.com
- 资源路径
- 查询参数部分,一般是?参数1= …&参数2= …,可以没有
浏览器发给web服务器的http请求报文
两种类型:
- get:获取数据
- post:提交数据(比如登录的时候发送用户名密码
get请求报文格式
比如baidu.com的:
GET / HTTP/1.1
Host: www.baidu.com
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/80.0.3987.149 Safari/537.36
Sec-Fetch-Dest: document
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
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
cookie: …
GET / HTTP/1.1是请求行
第一个斜杠表示资源路径,即百度的首页
1.1是HTTP的版本
后面是请求头
Host:如果没有写端口号,就是自动80
Connection: keep-alive指的是请求后,客户端与服务端不断开连接,但是一段时间后,服务端会自动断开连接。
User-Agent: 爬虫中可以根据user-agent进行反爬。指的是如果服务器看到user-agent视感浏览器,就给数据;如果是爬虫,就不给数据。
accept告诉服务端程序自己可以接受的数据类型
accept-encoding告诉服务端自己可以接受的压缩算法
还有一长段cookie:用来标识客户端用户身份,每个人都不一样。
最后是一个空行
而且请求报文每句话最后都要写\r\n,最后必须有一个空行,也要写\r\n。
因此http的get请求报文格式:
1.请求行
2.请求头
3.空行
且每个信息后都要有\r\n。
post请求报文格式
比如登录圆通快递官网失败。
POST的请求,端口号80。
POST /api/user/login?randValue=7c299c65-8c77-4c3a-b0f5-f1cc9982833f&code=1551&username=…&password=adda HTTP/1.1
Host: ec.yto.net.cn
Connection: keep-alive
Content-Length: 0
Accept: application/json, text/plain, /
source: PC
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
trace: {“loginSource”:“12”,“terminalModel”:“01”}
Origin: http://ec.yto.net.cn
Referer: http://ec.yto.net.cn/userlogin
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: …
这些与get是一样的,唯一不同的是多了一个请求体Form data,就是浏览器发送给服务器的用户名密码数据。
因此http的post请求报文格式:
1.请求行
2.请求头
3.空行
4.请求体
且每个信息后都要有\r\n。
post可以没有请求体,但一般都有
web服务器发送给浏览器的http响应报文
比如百度
响应行
HTTP/1.1 200 OK
响应头
Bdpagetype: 2
Bdqid: 0xe2a7978200001d14
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Thu, 20 May 2021 03:20:15 GMT
Expires: Thu, 20 May 2021 03:20:15 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=324; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=33764_33816_31253_34004_33772_33676_33607_26350_22158; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1621480815059020980216332189158283943188
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
空行
响应体 给浏览器的数据,html文件
HTTP/1.1 200 OK中的200 OK表示OK的状态码。
transfer-encoding: chunked即服务器发送给客户端的数据不确定长度,但有结束标识0\r\n,或者还有一个参数content-length,是可以确认长度的。
data服务器时间,是格林威治时间,差8小时。
除了这些以及熟悉的,其他都是网站自定义的,需要客户端服务端约定好。
响应体在Response里面:
每段信息\r\n。
因此http的响应报文格式:
1.响应行
2.响应头
3.空行
4.响应体
且每个信息后都要有\r\n。
HTTP常见状态码
比如前面的响应报文中的200
是服务器返回给客户端自己的状态。
200:成功
307:重定向,请求的网址不提供服务,改成了别的网址。
400:错误的请求,可能是请求中的request headers的请求头的资源地址写错了,也有可能是请求体里面的参数名写错了。
404:请求资源在服务器中不存在
500:服务器崩溃
静态web服务器
为浏览器提供静态文档(不会变的数据)的服务器。
通过cmd搭建静态web服务器
前提:在windows系统上。
打开cmd
首先一定要cd到自己的那堆网页存储的文件夹
然后输入python -m http.server 端口号
即可。
其中输入的地方,很多地方讲要输入python3,我试了好多次都不行,发现改成python后便可以了。
如图:
然后可以检测一下自己的服务器有没有被创建,在浏览器中输入localhost:端口号,即可访问自己的web服务器。
通过python开发静态web服务器
也就是开发一个TCP服务端程序。
返回固定页面
先把原来的那堆html网页给拖进来。
import socket
if __name__ == '__main__':
tcp_serve_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_serve_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_serve_socket.bind(("", 8000))
tcp_serve_socket.listen(128)
while True:
# 等待客户端链接请求,返回新的套接字和客户端ip与客户端端口号
# 注意,每有一个客户端请求连接,服务端就会创建一个新的套接字与客户端通信,这里这个新的套接字被命名为new_client
# accept接受链接,并返回新的服务端socket
new_client, ip_port = tcp_serve_socket.accept()
print("客户端的ip和端口号为:", ip_port)
# 注意这里的元组传参格式,只有一个参数不能直接写,要写(new_client,)
recv_data = new_client.recv(4096)
print("客户端发送的数据为:", recv_data)
# 使用with open可以不用写file.close,可以自动关闭
with open('static/index.html', 'r') as file:
file_data = file.read()
# 开始发送index.html
# 需要弄成http格式
# 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 响应头
response_head = "Server: PWS/1.0\r\n"
# 空行
# 响应体
response_body = file_data
# 拼接
response = response_line + response_head + '\r\n' + response_body
# 编码
response_data = response.encode('utf-8')
new_client.send(response_data)
new_client.close()
即不论浏览器想访问哪个网页,都给它index.html的数据。
返回指定页面
首先看一下客户端发送的数据是什么:
这就是请求报文,里面的grand.html表示了要访问的地址。
前面的b表示是二进制的格式,我们看之前的程序,确实还没有解码,因此应该再加上解码的语句。
import socket
if __name__ == '__main__':
tcp_serve_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_serve_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_serve_socket.bind(("", 8000))
tcp_serve_socket.listen(128)
while True:
# 等待客户端链接请求,返回新的套接字和客户端ip与客户端端口号
# 注意,每有一个客户端请求连接,服务端就会创建一个新的套接字与客户端通信,这里这个新的套接字被命名为new_client
# accept接受链接,并返回新的服务端socket
new_client, ip_port = tcp_serve_socket.accept()
print("客户端的ip和端口号为:", ip_port)
# 注意这里的元组传参格式,只有一个参数不能直接写,要写(new_client,)
recv_data = new_client.recv(4096)
# 解码,这里多试了一下,
recv_content = recv_data.decode("gbk")
print("客户端发送的数据为:", recv_content)
# 如果发来了无意义的数据,为了防止之后分割的时候报错,终止套接字即可
if len(recv_content) == 0:
new_client.close()
continue
# 获取请求内容,并分割
request_list = recv_content.split(" ", maxsplit=2)
# 得到请求的资源路径
request_path = request_list[1]
print("请求的路径为:", request_path)
if request_path == '/':
request_path = '/index.html'
# 使用with open可以不用写file.close,可以自动关闭,rb是为了兼容打开图片的请求,打开的数据成了二进制的格式
with open('static' + request_path, 'rb') as file:
file_data = file.read()
# 开始发送请求的页面
# 需要弄成http格式
# 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 响应头
response_head = "Server: PWS/1.0\r\n"
# 空行
# 响应体
response_body = file_data
# 拼接,因为前面的file_data是二进制,所以其他也要转成二进制才能拼接
response_data = (response_line + response_head + '\r\n').encode('utf-8') + response_body
new_client.send(response_data)
new_client.close()