这是我们访问一个网站的大概逻辑截图
当我们在浏览器中输入某个网址如baiducom回车后,计算机会把这个请求HTTP发送出来,当然最后的信息都是物理上的,在更高层面的网络应用协议上则是HTTP协议
HTTP协议的本质是一路有规范的文本,可以看下面的这一段内容就是一下简单的HTTP协议
POST /api/user HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 45
{"username": "john", "password": "secretpassword"}
前面我们使用requests来实现网页或者网络请求的数据获取如下:
rimport requests
es = requests.get(
f'https://www.google.com/search?q={city}&oq={city}&aqs=chrome.0.35i39l2j0l4j46j69i60.6128j1j7&sourceid=chrome&ie=UTF-8', headers=headers)
print("Searching...\n")
但是在这个代码中并没有看到发着HTTP协议 的内容。
Socket
我们上网的时候,www.baodu.com的服务器会有一个IP地址,我们的电脑通过DNS找到侬个地址后会向淘宝的电脑80端口发送HTTP协议的数据,端口可以理解一个屋子的窗户,它只处理指定格格式的数据。
这进我们的电脑浏览器客户端本地分配一个动态端口来与淘宝服务器进行数据交换,这样的一组ipport的信息就构成了一个socket的构成部分socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
Socket其实为我们连接计算机网络中的一种通信机制,它与TCP/IP解决的是不同层面 的问题。
request是如何使用socket的
def get(url, params=None, **kwargs)
return request("get", url, params=params, **kwargs)
看一下request的方法
class Session(SessionRedirectMixin):
def __init__(self):
self.headers = default_headers()
self.auth = None
self.proxies = {
self.mount('https://', HTTPAdapter())
self.mount('http://', HTTPAdapter())
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def prepare_request(self, request):
cookies = request.cookies or {}
p = PreparedRequest()
p.prepare(
)
return p
def request(self, method, url,
params=None, data=None, headers=None, cookies=None, files=None,
auth=None, timeout=None, allow_redirects=True, proxies=None,
hooks=None, stream=None, verify=None, cert=None, json=None):
# Create the Request.
req = Request(
method=method.upper(),
)
settings = self.merge_environment_settings(
prep.url, proxies, stream, verify, cert
)
# Send the request.
send_kwargs = {
'timeout': timeout,
'allow_redirects': allow_redirects,
}
send_kwargs.update(settings)
resp = self.send(prep, **send_kwargs)
return resp
def get(self, url, **kwargs):
kwargs.setdefault('allow_redirects', True)
return self.request('GET', url, **kwargs)
在哪里发送请求resp = self.send(prep, **send_kwargs)
send_kwargs = {
'timeout': timeout,
'allow_redirects': allow_redirects,
}
send_kwargs.update(settings)
resp = self.send(prep, **send_kwargs)
return resp
经过Session-->HTTPAdapter--> HTTPConnection 这一串类的查找我们最后发现了这样一个方法
def _new_conn(self):
"""Establish a socket connection and set nodelay settings on it.
:return: New socket connection.
"""
extra_kw = {}
if self.source_address:
extra_kw["source_address"] = self.source_address
if self.socket_options:
extra_kw["socket_options"] = self.socket_options
try:
conn = connection.create_connection(
(self._dns_host, self.port), self.timeout, **extra_kw
)
最终一个所谓的HTTPconnection对应提一个socket
def create_connection(
address,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
source_address=None,
socket_options=None,
):
host, port = address
try:
host.encode("idna")
except UnicodeError:
return six.raise_from(
LocationParseError(u"'%s', label empty or too long" % host), None
)
for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
sock = None
try:
sock = socket.socket(af, socktype, proto)
# If provided, set socket level options before connecting.
_set_socket_options(sock, socket_options)
if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT:
sock.settimeout(timeout)
if source_address:
sock.bind(source_address)
sock.connect(sa)
return sock
except socket.error as e:
err = e
if sock is not None:
sock.close()
sock = None
可以看到Socket是如何创建的
for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
sock = None
try:
sock = socket.socket(af, socktype, proto)
所以发送http协议还是依旧使用的是socket编程接口
socket的使用
- 确定目标主机和端口
- 连接这个目标主机端口,
- 发送数据,
- 接收目标主机的返回数据
我们使用socket来获取百度的页面数据
import socket
target_host = "www.baidu.com"
target_port = 80 # create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect the client
client.connect((target_host,target_port))
# send some data
request = "GET / HTTP/1.1\r\nHost:%s\r\n\r\n" % target_host
print(request)
client.send(request.encode())
# receive some data
while 1:
response = client.recv(4096)
if response:
# print(type(response))
http_response = response.decode()
http_response_len = len(http_response)
print(http_response)
可以看到返回的内容如下: