需求:

 (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. C++ 内存拷贝函数 memcpy

    在C/C++中经常会遇到对一段固定的连续内存进行拷贝操作,   这时候我们就需要用到   <cstring>  头文件  中的  memcpy  函数. 具体使用如下: 其中   ,   ...

  2. linux中文件上传下载

    windows篇 linux文件下载到windows sz命令 登录到linux服务器使用 sz log.log 命令,弹出对话框选择下载文件的目录,点击确定即可. windows文件上传到linux ...

  3. ssh-add时候提示Could not open a connection to your authentication agent

    先执行下ssh-agent bash  

  4. JS Array.filter()方法

    1.filter()接收的函数可以有多个参数.通常我们只使用第一个参数,第二参数和第三个参数表示元素的位置和数组本身: //去重 var arr = ["1", "2&q ...

  5. LeetCode Majority Element Python

    Given an array of size n, find the majority element. The majority element is the element that appear ...

  6. stenciljs 学习七 路由

    stenciljs路由类似react router 安装 npm install @stencil/router --save 使用 导入包 import "@stencil/router& ...

  7. 引用,引用形參,指针形參与指向指针的引用形參,内存泄露及free相关

    (另:关于"引用"更具体的讨论.见此.) 由做UVa133引发的一系列问题及讨论 1.引用类型    C++ Primer P51 引用就是对象的还有一个名字,使用多个变量名指向同 ...

  8. S5PV210 移植无线wifi网卡 MT7601

    一.准备工作 1.MT7601驱动下载 点击下载 2.插入usb WiFi 启动开发板linux,lsusb查看usb驱动 Bus 001 Device 003: ID 148f:7601看到的是该驱 ...

  9. setsockopt IP_ADD_MEMBERSHIP error!No such device的解决方案

    /mnt # ./onvifserver Happytime onvif server version 2.6Onvif server running at 192.168.1.10:8000crea ...

  10. 调试 FastAdmin 出现 Failed to parse SourceMap

    看到群里有人说在调试 FastAdmin出现 SourceMap 出错. 报错信息为: Failed to parse SourceMap 来自 Karson 说明: 这个文件是用于匹配原有less中 ...