摘要:怎么写出更短的代码并不是这次要讨论的话题。今天我们来研究一下:运行代码的计算机是如何找到目标服务器的?

相信各位 Python 开发者都用过 Requests 库,有些朋友还用过 WebSockets 库。这里回顾一下它们的基本用法,例如使用 Requests 库向目标网站发出 GET 请求:

import requests

url = "https://www.baidu.com"
resp = requests.get(url)
print(resp.status_code) # output -> 200

使用起来非常简单,我们很轻松地向目标网站发出了请求并打印输出响应状态码。当然,你还可以把它缩短:

import requests

print(requests.get("https://www.baidu.com").status_code)  # output -> 200

怎么写出更短的代码并不是这次要讨论的话题。今天我们来研究一下:运行代码的计算机是如何找到目标服务器的?

显然,你的第一映象是 IP 地址和端口号。

没错,就是 IP 地址和端口号。

但你明明输入的是 URL 地址,怎么就 IP + 端口号呢?

URL 解析的原因

一下子你也回答不上来吧?

我们可以将上方代码的逻辑,即计算机向目标服务器发出请求并拿到响应信息的过程抽象成下图:

程序输入的是 https://www.baidu.com,但最终要解析出具体的 IP 地址和端口号才能访问,例如 183.232.231.172:443

网络交互实际上属于 Socket 编程的范畴,无论是 Requests 还是 WebSockets 库,最终都会通过 Socket 与目标网站的服务器进行交互。而 Socket 编程中并不能直接使用域名,而是采用 IP + 端口号这种形式进行寻址的。

假设你现在需要编写一个网络请求库,有可能是 HTTP 协议的,也有可能是 WebSocket 协议的。你要解决的第一个问题就是解析 URL,将网址转换成 IP + 端口号,甚至还需要分割出协议类型、资源路径以及是否采用更安全的传输方式等。

URL 解析格式

以 WebSocket 协议方面的客户端库为例,在双端确认连接之前有一个「握手」的过程,这个过程之前已经需要双端的 IP 和端口号等信息了。下面的代码描述了 WebSocket 发出「握手」请求之前,双端建立连接时需要用到的基本信息:

 # aiowebsocket
reader, writer = await asyncio.open_connection(host=host, port=port, ssl=ssl)

也就是 hostportssl

大部分的 WebSocket 服务给出的都是域名,例如 wss://echo.websocket.org。「握手」时还会用到资源路径。

接下来,我们来尝试一下,如何将域名转换为 IP + 端口号和 is ssl 这样的格式。

代码实现 URL 解析

开始之前,我们先规划一下基本步骤:

然后确定要使用的标准库:解析 URL 当然要用到 urllib 库中的 url parse;解析 address 则需要用到 socket 库;为了方面取数据,可以尝试使用 collections 库中的 namedtuple。

首先引入这几个库:

# 崔庆才和韦世东邀请你关注公众号:进击的Coder
import socket
from collections import namedtuple
from urllib.parse import urlparse

然后定义输出结构,对应代码如下:

REMOTE = namedtuple('REMOTE', ['scheme', 'hostname', 'address', 'port', 'resource', 'ssl'])

然后定义一个方法,我们传入 URL,获得解析好的 REMOTE 对象。方法定义如下:

def parses(url: str) -> REMOTE:
pass

待会我们在 pass 处编写属于该方法的代码。

最开始要解析 URL,获得 scheme 和 hostname,对应代码如下:

url = urlparse(url)

urlparse 方法会返回一个 ParseResult 对象,对象大体格式如下:

ParseResult(scheme='wss', netloc='echo.websocket.org', path='', params='', query='', fragment='')

有了 scheme 和 hostname 后,就可以得到 portis ssl 和 address。对应代码如下:

# 崔庆才和韦世东邀请你关注公众号:进击的Coder
scheme = url.scheme
address = url.hostname
port = url.port or (443 if scheme == 'wss' else 80)
ssl = True if scheme == 'wss' else False

WebSocket 协议中只有两种协议头:wswss。它们对应的端口分别是 80443,这里借助 scheme 的值进行判断即可得到答案。同理,也直接得到了 is ssl 答案。

拿到 hostname 后,调用 socket 库的 getbyhostname 方法就能够得到目标服务器的 IP 地址了。对应代码如下:

address = socket.gethostbyname(hostname)

至于资源路径,它早已存在于 ParseResult 对象中,直接取出即可:

resource = url.path

要注意的是,有些 URL 中还会携带请求正文(即参数和值)。所以这里需要取 query,并将其拼接到 resource 中:

if url.query:
resource += '?' + url.query

至此,我们已经拿到了所需的所有数据。

现在将它们装在到 REMOTE 结构中,返回给调用方:

return REMOTE(scheme, hostname, address, port, resource, ssl)

此时,调用 parses 方法后就会拿到 REMOTE 结构,它的取值方式很舒服,用 . 符号取值即可。例如:

# 夜幕团队邀请你关注公众号:NightTeam
res = parses("ws://echo.websocket.org?sign=i9878")
print(res.address, res.port, res.resource)

代码运行结果如下:

174.129.224.73
80
?sign=i9878

这样,我们就完成了 URL 解析的代码编写。

小结

代码虽然不多,逻辑也并不复杂。但我们完整实现了网络请求库中的 URL 解析模块,这代表着完成了编写库的基石之一。

在这个过程当中,我们了解到双端通信的基本过程和要用到的信息。在编码中学会了如何将 urlparsesocketnamedtuple 结合到一起。

而且,你今天学到了 namedtuple 这个新姿势!

作者:华为云云享专家韦世东

「Python 编程」编码实现网络请求库中的 URL 解析器的更多相关文章

  1. Retrofit网络请求库应用02——json解析

    PS:上一篇写了Retrofit网络请求库的简单使用,仅仅是获取百度的源码,来证明连接成功,这篇讲解如何解析JSON数据,该框架不再是我们之前自己写的那样用JsonArray等来解析,这些东西,我们都 ...

  2. 自己动手写一个iOS 网络请求库的三部曲[转]

    代码示例:https://github.com/johnlui/Swift-On-iOS/blob/master/BuildYourHTTPRequestLibrary 开源项目:Pitaya,适合大 ...

  3. 浅论Android网络请求库——android-async-http

    在iOS开发中有大名鼎鼎的ASIHttpRequest库,用来处理网络请求操作,今天要介绍的是一个在Android上同样强大的网络请求库android-async-http,目前非常火的应用Insta ...

  4. swift中第三方网络请求库Alamofire的安装与使用

    swift中第三方网络请求库Alamofire的安装与使用 Alamofire是swift中一个比较流行的网络请求库:https://github.com/Alamofire/Alamofire.下面 ...

  5. [转]Android各大网络请求库的比较及实战

    自己学习android也有一段时间了,在实际开发中,频繁的接触网络请求,而网络请求的方式很多,最常见的那么几个也就那么几个.本篇文章对常见的网络请求库进行一个总结. HttpUrlConnection ...

  6. Android之网络请求库

    自己学习android也有一段时间了,在实际开发中,频繁的接触网络请求,而网络请求的方式很多,最常见的那么几个也就那么几个.本篇文章对常见的网络请求库进行一个总结. HttpUrlConnection ...

  7. iOS开发——实战篇Swift篇&UItableView结合网络请求,多线程,数据解析,MVC实战

    UItableView结合网络请求,多线程,数据解析,MVC实战 学了这么久的swift都没有做过什么东西,今天就以自己的一个小小的联系,讲一下,怎么使用swift在实战中应用MVC,并且结合后面的高 ...

  8. 【转载】一步一步搭建自己的iOS网络请求库

    一步一步搭建自己的iOS网络请求库(一) 大家好,我是LastDay,很久没有写博客了,这周会分享一个的HTTP请求库的编写经验. 简单的介绍 介绍一下,NSURLSession是iOS7中新的网络接 ...

  9. Android进阶笔记02:Android 网络请求库的比较及实战(二)

    一.Volley        既然在android2.2之后不建议使用HttpClient,那么有没有一个库是android2.2及以下版本使用HttpClient,而android2.3及以上版本 ...

随机推荐

  1. Hive数据仓库你了解了吗

    在工作中我们经常使用的数据库,数据库一般存放的我们系统中常用的数据,一般为百万级别.如果数据量庞大,达到千万级.亿级又需要对他们进行关联运算,该怎么办呢? 前面我们已经介绍了HDFS和MapReduc ...

  2. 掌握git命令的正确使用姿势

    前言 最近在团队内部发起了一个小的python项目(用tkinter实现一个小工具),但是发现大家对git的使用还不太熟悉,不知道怎么同步代码.解决冲突等等.因为我觉得对测试工程师来说,git应该是必 ...

  3. Html5在网页中引入视频音频的方法

    1.<video> 标签定义视频,比如电影片段或其他视频流. 一段简单的 HTML5 视频: <video src="movie.ogg" controls=&q ...

  4. Linux 常用命令 | free 详解

    free命令可以显示Linux系统中空闲的.已用的物理内存及swap内存,及被内核使用的buffer.在Linux系统监控的工具中,free命令是最经常使用的命令之一.本文介绍free命令的使用方法和 ...

  5. jquey写进度条问题

    jquey写进度条问题可以直接按照 总时间和进度条宽度之比来做 不管他是不是y=kx 反正时间到0 进度肯定是0了

  6. win7/win10系列的office安装与激活

    Windows系列电脑安装office傻瓜式教程 一.    下载与安装 下载 (1).所需工具:迅雷 下载链接:http://xl9.xunlei.com/ 显示界面如下,点击“立即下载”即可,然后 ...

  7. 《计算机网络 自顶向下方法》 第6章 链路层和局域网 Part2

    待补充完善 Web 页面的请求历程 应用层     报文.主机 运输层     报文段. 网络层     数据报.路由器.IP 地址 链路层     以太网帧.交换机.MAC 地址 步骤 1)到 4) ...

  8. PHP Laravel-包含你自己的帮助函数

    你可能想创建一个在应用的任何地方都可以访问的函数,这个教程将帮你实现

  9. Python日志模块logging简介

    日志处理是项目的必备功能,配置合理的日志,可以帮助我们了解系统的运行状况.定位位置,辅助数据分析技术,还可以挖掘出一些额外的系统信息. 本文介绍Python内置的日志处理模块logging的常见用法. ...

  10. CSS如何设置列表样式属性

    列表样式属性 在HTML中有2种列表.无序列表和有序列表,在工作中无序列表比较常用,无序列表就是ul标签和li标签组合成的称之为无序列表,那什么是有序列表呢?就是ol标签和li标签组合成的称之为有序列 ...