Python web框架开发 - WSGI协议

news2024/11/17 14:36:27

目录

浏览器请求动态页面过程

多进程web服务端代码 - 面向过程

封装对象分析

增加识别动态资源请求的功能

为什么需要 WSGI协议

WSGI协议的介绍

定义WSGI接口

编写framwork支持WSGI协议,实现浏览器显示 hello world

本次开发的完整代码如下:


浏览器进行http请求的时候,不单单会请求静态资源,还可能需要请求动态页面。

那么什么是静态资源,什么是动态页面呢?

静态资源 : 例如html文件、图片文件、css、js文件等,都可以算是静态资源
动态页面:当请求例如登陆页面、查询页面、注册页面等可能会变化的页面,则是动态页面。

浏览器请求动态页面过程

通过下图来了解一下页面HTTP请求的过程,如下:

 可以看到web服务器是用wsgi协议调用应用程序框架的,这里我们先不讲什么是wsgi协议,先看看我之前写的静态web服务端。

多进程web服务端代码 - 面向过程

#coding=utf-8
from socket import *
import re
import multiprocessing

def handle_client(client_socket):
    """为一个客户端服务"""
    # 接收对方发送的数据
    recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
    # 打印从客户端发送过来的数据内容
    #print("client_recv:",recv_data)
    request_header_lines = recv_data.splitlines()
    for line in request_header_lines:
        print(line)
     
    # 返回浏览器数据
    # 设置内容body
    # 使用正则匹配出文件路径
    print("------>",request_header_lines[0])
    print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
    ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
    if ret:
       file_path = "./html/" + ret.group(1)
       if file_path == "./html/":
          file_path = "./html/index.html"
       print("file_path *******",file_path)

    try:
       # 设置返回的头信息 header
       response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
       response_headers += "\r\n" # 空一行与body隔开
       # 读取html文件内容
       file_name = file_path # 设置读取的文件路径
       f = open(file_name,"rb") # 以二进制读取文件内容
       response_body = f.read()
       f.close()   
       # 返回数据给浏览器
       client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
       client_socket.send(response_body)   #转码utf-8并send数据到浏览器
    except:
       # 如果没有找到文件,那么就打印404 not found
       # 设置返回的头信息 header
       response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
       response_headers += "\r\n" # 空一行与body隔开
       response_body = "<h1>sorry,file not found</h1>"
       response = response_headers + response_body
       client_socket.send(response.encode("utf-8"))

    #client_socket.close()

def main():
   # 创建套接字
   server_socket = socket(AF_INET, SOCK_STREAM)
   # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
   server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
   # 设置服务端提供服务的端口号
   server_socket.bind(('', 7788))
   # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
   server_socket.listen(128) #最多可以监听128个连接
   # 开启while循环处理访问过来的请求 
   while True:
      # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
      # client_socket用来为这个客户端服务
      # server_socket就可以省下来专门等待其他新的客户端连接while True:
      client_socket, clientAddr = server_socket.accept()
      # handle_client(client_socket)
      # 设置子进程
      new_process = multiprocessing.Process(target=handle_client,args=(client_socket,))
      new_process.start() # 开启子进程

      # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
      client_socket.close()


if __name__ == "__main__":
   main()

先来回顾一下运行的情况:

 好了,看到运行也是正常的,那么下面就要来分析一下,如何将代码封装为对象。

封装对象分析

首先我需要定义一个webServer类,然后将访问静态资源的功能都封装进去。

#coding=utf-8
from socket import *
import re
import multiprocessing

class WebServer:

   def __init__(self):
       # 创建套接字
       self.server_socket = socket(AF_INET, SOCK_STREAM)
       # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
       self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
       # 设置服务端提供服务的端口号
       self.server_socket.bind(('', 7788))
       # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
       self.server_socket.listen(128) #最多可以监听128个连接

   def start_http_service(self):
       # 开启while循环处理访问过来的请求
       while True:
           # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
           # client_socket用来为这个客户端服务
           # self.server_socket就可以省下来专门等待其他新的客户端连接while True:
           client_socket, clientAddr = self.server_socket.accept()
           # handle_client(client_socket)
           # 设置子进程
           new_process = multiprocessing.Process(target=self.handle_client,args=(client_socket,))
           new_process.start() # 开启子进程
           # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
           client_socket.close()

   def handle_client(self,client_socket):
       """为一个客户端服务"""
       # 接收对方发送的数据
       recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
       # 打印从客户端发送过来的数据内容
       #print("client_recv:",recv_data)
       request_header_lines = recv_data.splitlines()
       for line in request_header_lines:
           print(line)

       # 返回浏览器数据
       # 设置内容body
       # 使用正则匹配出文件路径
       print("------>",request_header_lines[0])
       print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
       ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
       if ret:
          file_path = "./html/" + ret.group(1)
          if file_path == "./html/":
             file_path = "./html/index.html"
          print("file_path *******",file_path)

       try:
          # 设置返回的头信息 header
          response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
          response_headers += "\r\n" # 空一行与body隔开
          # 读取html文件内容
          file_name = file_path # 设置读取的文件路径
          f = open(file_name,"rb") # 以二进制读取文件内容
          response_body = f.read()
          f.close()
          # 返回数据给浏览器
          client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
          client_socket.send(response_body)   #转码utf-8并send数据到浏览器
       except:
          # 如果没有找到文件,那么就打印404 not found
          # 设置返回的头信息 header
          response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
          response_headers += "\r\n" # 空一行与body隔开
          response_body = "<h1>sorry,file not found</h1>"
          response = response_headers + response_body
          client_socket.send(response.encode("utf-8"))


def main():
    webserver = WebServer()
    webserver.start_http_service()

if __name__ == "__main__":
    main()

好了,从上面的代码来看,我已经将前面面向过程的代码修改为面向对象了。

运行一下看看有没有错误:

思考:已经封装为对象了,下一步还要优化什么呢?

请求静态资源的页面已经可以了,那么如果请求动态的页面呢?
如果web服务端是java写的话,通常http请求就是http:xxxx/xxx.jsp
如果web服务端是php写的话,通常http请求就是http:xxxx/xxx.php
那么,既然这次我使用python来写,就可以定义动态资源的请求为http:xxxx/xxx.py

那么如果来识别并执行 http:xxxx/xxx.py 的请求呢?

增加识别动态资源请求的功能

需求:识别并返回http:xxxx/xxx.py 的请求
那么让我想一下,先做个简单的,例如:我请求一个http的请求 http:xxxx/time.py 则返回一个当前服务端的时间给浏览器。

那么如果http请求了一个py结尾的请求,我需要在哪里处理呢?

 还有我可以用什么方法来判断 .py 后缀的文件呢?
用正则匹配?
其实可以使用endswith("文件后缀")的方法来判断处理。

In [1]: file_name = "time.py"

# 匹配后缀为 .html ,直接报False
In [3]: file_name.endswith(".html")
Out[3]: False

# 匹配后缀为 .py ,则报True
In [4]: file_name.endswith(".py")
Out[4]: True

那么下面就可以来写写这里判断的处理分支了。

 测试执行一下:

首先请求HTML等静态资源页面

 请求动态资源页面

 先简单地写一串HTML+当前服务器时间的内容吧。

       if file_path.endswith(".py"):
           # 请求动态资源
           print("这个是请求动态资源的!")
           # 设置返回的头信息 head
           response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
           response_headers += "\r\n" # 空一行与body隔开
           # 设置返回浏览器的body内容
           response_body = "<h1>hello this is xxx.py</h1><br>"
           response_body += time.ctime()
           response = response_headers + response_body
           # 返回数据给浏览器
           client_socket.send(response.encode("utf-8"))

 从这里已经可以正常返回动态页面的内容的了。

思考 :如果将动态处理页面的代码在web服务端不断地写,代码就会很庞大。是否可以拆分出来,服务端只接受浏览器的消息,判断静态还是动态以及其他业务功能放到另一个模块进行编写呢?

这里就涉及到 web服务端 与 业务处理服务端 之间的一个协议了,这个业界内通用的协议就是 WSGI协议。

为什么需要 WSGI协议

在讲WSGI协议之前,我先把处理动态页面的功能拆分到另一个模块文件中。

import time

def application(client_socket):
    # 请求动态资源
    print("这个是请求动态资源的!")
    # 设置返回的头信息 head
    response_headers = "HTTP/1.1 200 OK\r\n"  # 200 表示找到这个资源
    response_headers += "\r\n"  # 空一行与body隔开
    # 设置返回浏览器的body内容
    response_body = "<h1>hello this is xxx.py</h1><br>"
    response_body += time.ctime()
    response = response_headers + response_body
    # 返回数据给浏览器
    client_socket.send(response.encode("utf-8"))

那么在原来的webserver.py模块只要import该模块文件,使用application()方法就可以处理刚才的业务了。

 

 好了,做了这个解耦的操作之后,下面来运行测试一下:

 从上面的调用结果来看,的确是调用成功啦,理解大概如下图:

 可以看出来,webserver想要调用 framework处理业务的话,就要这样去写,如下:

framework.application(client_socket)

这种方式虽然可行,但是在业界中是不通用的。也就是说这种调用方法扔给别人写的框架,就无法兼容了。

例如:假设我后面改用Django、Flask框架来处理业务,此时一定就不是用这种方式来通讯调用的。

 那么该用什么方式呢?

是否可以修改服务器和架构代码而确保可以在多个架构下,保证与web服务器之间的通讯调用呢?

答案就是  Web Server Gateway Interface (或简称 WSGI,读作“wizgy”)。

WSGI协议的介绍

WSGI允许开发者将选择web框架和web服务器分开。可以混合匹配web服务器和web框架,选择一个适合的配对。
比如,可以在Gunicorn 或者 Nginx/uWSGI 或者 Waitress上运行 Django, Flask, 或 Pyramid。真正的混合匹配,得益于WSGI同时支持服务器和架构。

 web服务器必须具备WSGI接口,所有的现代Python Web框架都已具备WSGI接口,它让你不对代码作修改就能使服务器和特点的web框架协同工作。

WSGI由web服务器支持,而web框架允许你选择适合自己的配对,但它同样对于服务器和框架开发者提供便利使他们可以专注于自己偏爱的领域和专长而不至于相互牵制。其他语言也有类似接口:java有Servlet API,Ruby 有 Rack。

定义WSGI接口

WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。

我们来看一个最简单的Web版本的“Hello World!”

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'

上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

  • environ:一个包含所有HTTP请求信息的dict对象;
  • start_response:一个发送HTTP响应的函数。

整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,把底层web服务器解析部分和应用程序逻辑部分进行了分离,这样开发者就可以专心做一个领域了。

不过,这个application()函数怎么调用?如果我们自己调用,两个参数environ和start_response我们没法提供,返回的str也没法发给浏览器。

所以application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器。而我们此时的web服务器项目的目的就是做一个既能解析静态网页还可以解析动态网页的WSGI服务器。

编写framwork支持WSGI协议,实现浏览器显示 hello world

直接协议规范代码复制进去。

那么在webserver.py的部分,就需要接受application返回的信息。
首先,start_response 就是在framwork设置http请求header信息的。而return 就是返回http请求body信息的。

那么知道了这两点之后,下一步要做的。就是想办法来接受这个application的设置header以及body信息。

那么怎么处理呢?

为了方便对比查看这两个文件的代码,使用pycharm同时打开两个视图窗口来查看。

 好了,下面来继续看看。

下面来创建这两个形参:

  • environ:一个包含所有HTTP请求信息的dict对象;
  • start_response:一个发送HTTP响应的函数。

先随便写个空的,来填入WSGI规范所需要的参数。
其中response_body通过return的返回值就可以接受到了。
那么response_header该怎么处理呢?

可以从代码中看出start_response 在webserver.py 传入到 framwork.py 的application中调用。
其中在application中就直接设置header信息到start_response的参数中。然后我在webserver.py能否直接将其取出来,拼接成header信息呢?

编写start_response 接收 header 信息

那么首先编写一个类变量来保存信息,然后测试打印一下看看。

 运行测试一下看看:

 

 那么只要将其保存到self.application_header中,我就可以在类方法的任意一个地方进行拆分或者拼接成所需要的http header返回值了。

编写如下:

 运行测试看看。

这样就得到了完成的header内容啦,那么下面将其拼接body内容,然后返回浏览器中显示。

 运行测试如下:

本次开发的完整代码如下:

webserver.py

#coding=utf-8
from socket import *
import re
import multiprocessing
import time
import framework


class WebServer:

   def __init__(self):
       # 创建套接字
       self.server_socket = socket(AF_INET, SOCK_STREAM)
       # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
       self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
       # 设置服务端提供服务的端口号
       self.server_socket.bind(('', 7788))
       # 使用socket创建的套接字默认的属性是主动的,使用listen将其改为被动,用来监听连接
       self.server_socket.listen(128) #最多可以监听128个连接

   def start_http_service(self):
       # 开启while循环处理访问过来的请求
       while True:
           # 如果有新的客户端来链接服务端,那么就产生一个新的套接字专门为这个客户端服务
           # client_socket用来为这个客户端服务
           # self.server_socket就可以省下来专门等待其他新的客户端连接while True:
           client_socket, clientAddr = self.server_socket.accept()
           # handle_client(client_socket)
           # 设置子进程
           new_process = multiprocessing.Process(target=self.handle_client,args=(client_socket,))
           new_process.start() # 开启子进程
           # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
           client_socket.close()

   def handle_client(self,client_socket):
       """为一个客户端服务"""
       # 接收对方发送的数据
       recv_data = client_socket.recv(1024).decode("utf-8") #  1024表示本次接收的最大字节数
       # 打印从客户端发送过来的数据内容
       #print("client_recv:",recv_data)
       request_header_lines = recv_data.splitlines()
       for line in request_header_lines:
           print(line)

       # 返回浏览器数据
       # 设置内容body
       # 使用正则匹配出文件路径
       print("------>",request_header_lines[0])
       print("file_path---->","./html/" + re.match(r"[^/]+/([^\s]*)",request_header_lines[0]).group(1))
       ret = re.match(r"[^/]+/([^\s]*)",request_header_lines[0])
       if ret:
          file_path = "./html/" + ret.group(1)
          if file_path == "./html/":
             file_path = "./html/index.html"
          print("file_path *******",file_path)

       # 判断file_path是否py文件后缀,如果是则请求动态资源,否则请求静态资源
       if file_path.endswith(".py"):

           # framework.application(client_socket)
           # 支撑WGSI协议的调用方式
           environ = {}
           response_body = framework.application(environ, self.start_response)
           # 设置返回的头信息header
           # 1.拼接第一行HTTP/1.1 200 OK + 换行符内容
           response_headers = "HTTP/1.1 " + self.application_header[0] + "\r\n"
           # 2.循环拼接第二行或者多行元组内容:Content-Type:text/html
           for var in self.application_header[1]:
               response_headers += var[0]+":"+var[1] + "\r\n"
           # 3.空一行与body隔开
           response_headers += "\r\n"
           # 4.打印看看header的内容信息
           print("response_header=")
           print(response_headers)

           # 设置返回的浏览器的内容
           response = response_headers + response_body
           client_socket.send(response.encode("utf-8"))

       else:
           # 请求静态资源
           try:
              # 设置返回的头信息 header
              response_headers = "HTTP/1.1 200 OK\r\n" # 200 表示找到这个资源
              response_headers += "\r\n" # 空一行与body隔开
              # 读取html文件内容
              file_name = file_path # 设置读取的文件路径
              f = open(file_name,"rb") # 以二进制读取文件内容
              response_body = f.read()
              f.close()
              # 返回数据给浏览器
              client_socket.send(response_headers.encode("utf-8"))   #转码utf-8并send数据到浏览器
              client_socket.send(response_body)   #转码utf-8并send数据到浏览器
           except:
              # 如果没有找到文件,那么就打印404 not found
              # 设置返回的头信息 header
              response_headers = "HTTP/1.1 404 not found\r\n" # 200 表示找到这个资源
              response_headers += "\r\n" # 空一行与body隔开
              response_body = "<h1>sorry,file not found</h1>"
              response = response_headers + response_body
              client_socket.send(response.encode("utf-8"))

   def start_response(self,status,header):
       self.application_header = [status,header]
       print("application_header=",self.application_header)

def main():
    webserver = WebServer()
    webserver.start_http_service()

if __name__ == "__main__":
    main()
framework.py



# 支撑WGSI协议
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'

-事必有法,然后有成- 最后祝大家早日达到测试的天花板!



 以下是我收集到的比较好的学习教程资源,虽然不是什么很值钱的东西,如果你刚好需要,可以评论区,留言【777】直接拿走就好了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/705726.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习 / 数据处理:如何处理偏态数据

1 前言 当我们使用一个线性回归模型时&#xff0c;通常这个模型是在很大假设的前提下才有一个很好的结果&#xff1a; 1、假设预测因子和预测目标之间的关系是线性的2、数据不存在外在噪声&#xff1a;不存在一些极端的数据3、非共线性&#xff08; collinearity&#xff09;…

1分钟学会使用docker-compose部署 registry 以及可视化镜像

获取 docker-compose: curl -L https://github.com/docker/compose/releases/download/2.2.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose docekr-compose.yaml: version: 3.3services:registry-ui:image: joxit/docker-registry-ui:mainrestart: always…

appium如何连接多台设备

目录 前言&#xff1a; 1.我们拿两台设备来模拟操作下&#xff0c;使用&#xff1a;adb devices查看连接状况&#xff0c;获取到设备名称。 2.获取需要操作app的包名和页面名称&#xff08;前提该设备已经打开了app&#xff09; 3.设置初始配置信息 4.打开页面后操作元素&am…

嵌入式系统的不同方向及优化策略

当涉及到嵌入式系统开发时&#xff0c;可以根据具体的应用需求选择不同的方向进行优化。以下是一些常见的嵌入式系统方向及其特点&#xff1a; 单片机方向&#xff1a;这个方向主要针对使用单片机作为核心的嵌入式系统开发。单片机资源有限&#xff0c;适用于简单的控制任务&am…

基于Java的万年历(课设)

基于Java的万年历 资源链接&#xff1a;基于Java的万年历&#xff08;课设&#xff09; 文章目录 基于Java的万年历1 绪论2 需求分析3 概要设计3.1 类间组合框架3.2 布局结构示意3.3 对各个类的概述 4运行环境5 开发工具和编辑语言6 详细设计6.1 NiceCaelendar类6.2 NiceFram…

分享一个优秀的动态数据源开源库-dynamic-datasource-spring-boot-starter

分享一个优秀的动态数据源开源库-dynamic-datasource-spring-boot-starter 1.1 前言1.2 动态数据源开源库简介1.3 特性1.4 用法示例1.4.1 添加依赖1.4.2 配置数据源1.4.3 使用 DS 注解切换数据源 1.5 最佳实践 1.1 前言 在我们的Java后端研发工作中, 有时候由于业务的快速迭代…

Unity 热力图效果实现 笔记

Unity 热力图效果实现 笔记 参考文献连接&#xff1a; 1、人体热力图shader graph实现&#xff08;URP&#xff09; 超链接&#xff1a; https://www.youtube.com/watch?vKlMON4Dzq_0&t51s shader forge 翻译通用管线下 连接点实现方案&#xff1a; 2、碰撞热力图实现…

青岛大学_王卓老师【数据结构与算法】Week03_08_线性表的链式表示和实现8_学习笔记

本文是个人学习笔记&#xff0c;素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享&#xff0c;另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。如有侵权&#xff0c;请留言作删文处理。 课程视频链接&#xff1a; 数据结构与算法基础–第…

Python打包工具 Pyinstaller使用教程(将.py以及Python解释器和相关库打包可执行文件)

文章目录 pyinstaller历史背景工作原理使用方法简介使用方法详解&#xff08;请仔细阅读help文档中文翻译&#xff09;help文档help文档中文翻译 简单使用示例1. 编译打包2. 拷贝到目标系统3. 运行&#xff08;遇到问题&#xff09; 如何使用xxx.spec文件重新编译配置项示例配置…

千字提炼商业智能BI精髓,言简意赅

商业智能BI的发展和数据的增长有很强的关联性&#xff0c;而当前人工智能、大数据、云计算、物联网、互联网等技术和应用都对数据的增长有促进作用&#xff0c;所以商业智能BI也就伴随着数据的增长&#xff0c;在各行各业的企业中开始发挥作用。 商业智能&#xff08;Business…

uniapp 移动端 后台返回数据流 查看PDF

使用步骤&#xff1a; 1.官网下载地址pdf.js 2.在项目的根目录新建hybrid文件夹&#xff0c;将下载的pdf.js压缩包解压后&#xff0c;复制到hybrid下的html文件夹中 3.在page文件夹下新建一个filePreview.vue页面&#xff0c;页面代码如下&#xff1a; <template><…

C#核心知识回顾——5.结构体和类区别、抽象类和接口、核心知识图解

1.结构体和类区别 结构体和类最大的区别是在存储空间上的&#xff0c;因为结构体是值&#xff0c;类是引用&#xff0c;因此他们的存储位置一个在栈上&#xff0c;一个在堆上。 结构体和类在使用上很类似&#xff0c;结构体甚至可以用面向对象的思想来形容一类对象。 结…

SQL题型:根据逗号拆分列

例1&#xff1a; 表&#xff1a; 要实现的结果&#xff1a; 代码&#xff1a; select a.id as hyId,substring_index(substring_index(a.ch_ry_mc, ,,b.help_topic_id 1 ),,, - 1) AS CH_RY_ID FROM rsgl_hygl_hyxx a JOIN mysql.help_topic b ON b.help_topic_id < ( l…

Acwing.835 Trie字符串统计 (trie数据结构)

题目 维护一个字符串集合&#xff0c;支持两种操作: 1."Ix"向集合中插入一个字符串x; 2."Qx”询问一个字符串在集合中出现了多少次。 共有N个操作&#xff0c;输入的字符串总长度不超过 1 0 5 10^5 105&#xff0c;字符串仅包含小写英文字母。 输入格式 第一…

ChatGPT 和爬虫有什么区别?

ChatGPT是一种基于人工智能的对话模型&#xff0c;它通过训练大量的文本数据来生成自然语言回复。它可以用于实现智能对话系统&#xff0c;能够理解用户的输入并生成相应的回复。ChatGPT的目标是模拟人类对话&#xff0c;使得对话更加流畅和自然。 而爬虫是一种用于自动化地从…

linux 操作系统内核态用户态

1. 32位系统一个进程最多有多少堆内存 对 32 位操作系统而言&#xff0c;它的寻址空间是4G&#xff08;2的32次方&#xff09;&#xff0c;Linux把它分为两部分&#xff1a;最高的1G(虚拟地址从0xC0000000到0xffffffff)用做内核本身&#xff0c;成为“内核空间”&#xff0c;而…

ESP32-C2(8684) AT固件程序

ESP32C2 AT固件使用 ESP32 C2模组&#xff0c;如图1-1所示 图1-1 ESP32 C2模组 ESP32 C2开发板&#xff0c;如图1-2所示 图1-2 ESP32 C2开发 方案亮点 1、完整的 WiFi 子系统&#xff0c;符合 IEEE 802.11b/g/n 协议&#xff0c;具有 Station 模式、SoftAP 模式、SoftAP Stat…

SAP BW/HANA 数据源创建示例

操作实例 在ABAP中创建&#xff1a; 1、RSO2——创建数据源——明明规则&#xff1a;ZZZ/BZ/HY_PP/MM/SD/FI_数据表名 如果表数据中存在货币或者数量关联不是本表需要走RFC提取 根据RFC提取&#xff1a; 函数组—ZBW_GROUP_FI 创建RFC&#xff1a;ZBW_FUN_ZCOT007E 代码&a…

(css)在网页上添加Live 2D网页二次元可动小人

(css)在网页上添加Live 2D网页二次元可动小人 效果&#xff1a; 代码&#xff1a; <script src"js/L2Dwidget.min.js"></script> <script src"js/L2Dwidget.0.min.js"></script> <script>L2Dwidget.init({"model&quo…

git学习使用笔记

一、git组成结构图 工作空间&#xff1a;用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分&#xff0c;从其它计算机克隆仓库时&#xff0c;拷贝的就是这里的数据。本地索引&#xff1a;保存了下次将提交的文件列表信息&#xff0c;一般在 Git 仓库目录中。有…