需求:

 (1)使用socket及ssl模块写通用的web客户端
(2)向服务器发起请求
(3)接受响应内容并解析出状态码、消息报头、响应正文
(4)最核心的函数: 输入一个url,返回状态码、消息报头、响应正文;当然这也是最后实现的效果

知识储备:

网络基础知识

python的web编程(socket)

最后实现代码:

 # __author__ = "wyb"
# date: 2018/6/5
# 代码: 高内聚低耦合 -> 使用函数封装一些逻辑代码 -> 功能函数 import socket
import ssl
"""
在 Python3 中,bytes 和 str 的互相转换方式是
str.encode('utf-8')
bytes.decode('utf-8') send 函数的参数和 recv 函数的返回值都是 bytes 类型 一、使用 https
1, https 请求的默认端口是 443
2, https 的 socket 连接需要 import ssl
并且使用 s = ssl.wrap_socket(socket.socket()) 来初始化 二、HTTP 协议的 301 状态
请求豆瓣电影 top250 (注意协议)
http://movie.douban.com/top250
返回结果是一个 301
301 状态会在 HTTP 头的 Location 部分告诉你应该转向的 URL
所以, 如果遇到 301, 就请求新地址并且返回
HTTP/1.1 301 Moved Permanently
Date: Sun, 05 Jun 2016 12:37:55 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Keep-Alive: timeout=30
Location: https://movie.douban.com/top250
Server: dae
X-Content-Type-Options: nosniff <html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html> https 的默认端口是 443, 所以你需要在 get 函数中根据协议设置不同的默认端口
""" # 功能函数:
# 解析url产生protocol、host、port、path
def parsed_url(url):
"""
:param url: 字符串, 可能的值如下
'g.cn'
'g.cn/'
'g.cn:3000'
'g.cn:3000/search'
'http://g.cn'
'https://g.cn'
'http://g.cn/'
:return: 返回一个 tuple, 内容: (protocol, host, port, path)
"""
protocol = "http"
if url[:7] == "http://":
u = url.split("://")[1]
elif url[:8] == "https://":
protocol = "https"
u = url.split("://")[1]
else:
u = url # 检查默认path
i = u.find("/")
if i == -1:
host = u
path = "/"
else:
host = u[:i]
path = u[i:] # 检查端口
port_dict = {
"http": 80,
"https": 443,
}
# 默认端口
port = port_dict[protocol]
if ":" in host:
h = host.split(":")
host = h[0]
port = int(h[1]) return protocol, host, port, path # 根据协议返回socket实例
def socket_by_protocol(protocol):
"""
根据协议返回socket实例
:param protocol: 协议
:return: socket实例
"""
if protocol == "http":
s = socket.socket() # 生成一个socket对象 else:
# HTTPS 协议需要使用 ssl.wrap_socket 包装一下原始的 socket
# 除此之外无其他差别
s = ssl.wrap_socket(socket.socket())
return s # 根据socket对象接受数据
def response_by_socket(s):
"""
接受数据
:param s: socket实例
:return: response
"""
response = b""
buffer_size = 1024
while True:
r = s.recv(buffer_size)
if len(r) == 0:
break
response += r
return response # 把 response 解析出 状态码 headers body 返回
def parsed_response(r):
"""
解析response对象获取状态码、headers、body
:param r: response
:return: tuple(status_code, headers, body)
"""
header, body = r.split('\r\n\r\n', 1)
h = header.split('\r\n')
# headers的头部: HTTP/1.1 200 OK
status_code = h[0].split()[1]
status_code = int(status_code) headers = {}
for line in h[1:]:
k, v = line.split(': ')
headers[k] = v
return status_code, headers, body # 主逻辑函数:
# 把向服务器发送 HTTP 请求并且获得数据这个过程封装成函数 -> 复杂的逻辑(具有重用性)封装成函数
def get(url):
"""
使用 socket 连接服务器,获取服务器返回的数据并返回
:param url: 链接地址,url的值如下:
'g.cn'
'g.cn/'
'g.cn:3000'
'g.cn:3000/search'
'http://g.cn'
'https://g.cn'
'http://g.cn/'
:return: 返回tuple(status_code, headers, body)
"""
protocol, host, port, path = parsed_url(url) # 得到socket对象并连接服务器
s = socket_by_protocol(protocol)
s.connect((host, port)) # 发送请求
request = 'GET {} HTTP/1.1\r\nhost: {}\r\nConnection: close\r\n\r\n'.format(path, host)
encoding = 'utf-8'
s.send(request.encode(encoding)) # 获得响应
response = response_by_socket(s)
r = response.decode(encoding) # 解析响应
status_code, headers, body = parsed_response(r)
# 当状态码为301或302时表示为重定向
if status_code in [301, 302]:
url = headers['Location']
return get(url) return status_code, headers, body # 单元测试:
def test_parsed_url():
"""
parsed_url函数很容易出错,我们写测试函数来运行检测是否正确运行
"""
http = "http"
https = "https"
host = "g.cn"
path = "/"
test_items = [
('http://g.cn', (http, host, 80, path)),
('http://g.cn/', (http, host, 80, path)),
('http://g.cn:90', (http, host, 90, path)),
('http://g.cn:90/', (http, host, 90, path)),
('https://g.cn', (https, host, 443, path)),
('https://g.cn:233', (https, host, 233, path)),
]
for t in test_items:
url, expected = t
u = parsed_url(url)
# assert 是一个语句, 名字叫 断言
# 如果断言成功, 条件成立, 则通过测试, 否则为测试失败, 中断程序报错
e = "parsed_url ERROR, ({}) ({}) ({})".format(url, u, expected)
assert u == expected, e def test_get():
"""
测试是否能正确处理 HTTP 和 HTTPS
"""
urls = [
'http://movie.douban.com/top250',
'https://movie.douban.com/top250',
]
for u in urls:
res = get(u)
print(res) # 使用:
def main():
url = 'http://movie.douban.com/top250'
# r = get(url)
# print(r)
status_code, headers, body = get(url)
print("status_code: ", status_code)
print("headers: ", headers)
print("body: ", body) if __name__ == '__main__':
# test_parsed_url()
# test_get()
main()

原生socket请求url获取状态码、消息报头、响应正文的更多相关文章

  1. php 请求url获取状态码

    function get_http_code($url) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); //设置URL c ...

  2. Java发送Http请求并获取状态码

    通过Java发送url请求,查看该url是否有效,这时我们可以通过获取状态码来判断. try { URL u = new URL("http://10.1.2.8:8080/fqz/page ...

  3. 用Java发起HTTP请求与获取状态码(含状态码列表)

    转自:https://blog.csdn.net/xyw591238/article/details/51072697 在使用Java请求Web程序比如访问WebService接口时,通常需要先判断访 ...

  4. 接口测试——HttpClient工具的https请求、代理设置、请求头设置、获取状态码和响应头

    目录 https请求 代理设置 请求头设置 获取状态码 接收响应头 https请求 https协议(Secure Hypertext Transfer Protocol) : 安全超文本传输协议, H ...

  5. HTTP/1.1标准请求方法和状态码

    HTTP/1.1标准自从1999年制定以来至今仍然是一个应用广泛并且通行的标准 相关文档 RFC2616:Hypertext Transfer Protocol -- HTTP/1.1 在RFC658 ...

  6. status 返回当前请求的http状态码

    status属性返回当前请求的http状态码,此属性仅当数据发送并接收完毕后才可获取.完整的HTTP状态码如下: 100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分 101 ...

  7. HTTP 基础(特性、请求方法、状态码、字段)

    1. HTTP 简介(含义.特性.缺点) 2. HTTP 报文 3. GET 和 POST 4. 状态码 5. HTTP 头字段 1. HTTP 简介 HTTP 的含义 HTTP (HyperText ...

  8. drf复习(一)--原生djangoCBV请求生命周期源码分析、drf自定义配置文件、drf请求生命周期dispatch源码分析

    admin后台注册model  一.原生djangoCBV请求生命周期源码分析 原生view的源码路径(django/views/generic/base.py) 1.从urls.py中as_view ...

  9. 获取响应状态Status信息、获取状态码Status Code

    一般服务器的响应状态有以下几种: 200 正常 400 未找到页面 403 拒绝 500 服务器错误 比如我们请求bootstrap中文网, 此时的状态码是200 OK表示正常,后面的from cac ...

随机推荐

  1. ZOJ2067 经典 DP(单调队列优化)

    题目:一个由‘.’和‘#’组成矩形,统计里面'.'组成的矩形的个数. 点击打开链接 自己写挂了,懒得搞了 #include <stdio.h> #include <string.h& ...

  2. php 加载字体 并保存成图片

    // Set the content-type header("Content-type: image/png"); // Create the image $im = image ...

  3. 有向图与无向图的合并操作区别D(递归与并查集)

    有向图的合并,典型问题:通知小弟(信息只能单向传播)https://www.nowcoder.com/acm/contest/76/E 无向图的合并,典型问题:修道路问题 由于无向图只要二者有联系即可 ...

  4. hdu1331&&hdu1579记忆化搜索(DP+DFS)

    这两题是一模一样的``` 题意:给了一系列递推关系,但是由于这些递推很复杂,所以递推起来要花费很长的时间,所以我要编程序在有限的时间内输出答案. w(a, b, c): 如果a,b,c中有一个值小于等 ...

  5. Tomcat:Several ports are already in use问题

    Several ports (8005, 8080, 8009) required by Tomcat v6.0 Server at localhost are already in use. The ...

  6. day20 python sys os time json pickl 正则

    字符组 : [字符组] 在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示 字符分为很多类,比如数字.字母.标点等等. 假如你现在要求一个位置....9这10个数之一. 量词 几 ...

  7. 转 DataTorrent 1.0每秒处理超过10亿个实时事件

    DataTorrent是一个实时的流式处理和分析平台,它每秒可以处理超过10亿个实时事件. 与Twitter平均每秒大约6000条微博相比,最近发布的DataTorrent 1.0似乎已经超出了需求, ...

  8. 根据IP获得城市信息(百度API的运用)

    /**      * 根据IP获取城市      * @param string $ip ip地址      * @return array      * http://api.map.baidu.c ...

  9. enjoy dollar vs cash dollar

    當 enJoy 卡 客 戶 憑 enJoy 卡 於 enJoy 卡 「 特 約 商 戶 」 簽 賬 消 費 , 累 積 之 enJoy Dollars 及 Cash Dollars 可 在 同 一 交 ...

  10. java关键字,保留字

    Java语言有51个保留关键字,其中const和goto虽然被保留但未被使用.你不能使用保留关键字来命名类.方法或变量. 一.保留关键字 数据类型: Boolean   int   long   sh ...