Loguru是一个功能强大且易于使用的开源Python日志记录库。它建立在Python标准库中的logging模块之上，并提供了更加简洁直观、功能丰富的接口。Logging模块的使用见： Python日志记录库logging总结 。Loguru官方仓库见：loguru，loguru官方文档见： loguru-doc。Loguru的主要特点包括：简单易用：无需复杂的配置和定制即可实现基本的日志记录和输出。灵活的日志格式：支持自定义日志格式，并提供丰富的格式化选项。丰富的日志级别：支持多种日志级别，例如DEBUG、INFO、WARNING、ERROR和CRITICAL。多种日志目标：可以将日志输出到终端、文件、电子邮件、网络服务器等目标。强大的日志处理功能：支持日志过滤、格式化、压缩、旋转等功能。支持异步日志记录：能够极大地提升日志记录的性能。支持跨进程、跨线程的日志记录：可以安全地记录多进程、多线程应用程序的日志。Loguru与logging是Python中常用的两个日志记录库，但两者在功能和易用性方面存在一些差异，如下所示：特性Logurulogging易用性更简单易用相对复杂日志格式更灵活较简单日志级别更丰富较少日志目标更多种类较少日志处理功能更强大较弱异步日志记录支持不支持跨进程、跨线程支持支持支持总的来说，loguru在易用性、功能性和性能方面都优于logging。如果要一个简单、强大且易于使用的日志系统，loguru是一个很好的选择。而如果只是需要快速输出一些调试信息，print可能就足够了。不过，对于生产环境，使用loguru或其他日志系统通常会更加合适。Loguru安装命令如下：pip install loguru# 查看loguru版本 import loguru print(loguru.__version__) # 输出：0.7.21 使用说明1.1 基础用法简单使用 Loguru的核心概念是只有一个全局的日志记录器，也就是logger。这个设计使得日志记录变得非常简洁和一致。使用Loguru时，你不需要创建多个日志实例，而是直接使用这个全局的logger来记录信息。这不仅减少了配置的复杂性，也使得日志管理更加集中和高效。from loguru import logger logger.debug("这是一个调试信息")输出：2024-06-29 19:57:44.506 | DEBUG | __main__::3 - 这是一个调试信息Loguru日志输出默认格式如下：时间戳：表示日志记录的具体时间，格式通常为年-月-日 时:分:秒.毫秒。日志级别：表示这条日志的严重性级别。进程或线程标识：表示日志来自哪个模块或脚本。 main 表示日志来自主模块。如果是其他文件会显示文件名。文件名和行号：记录日志消息的函数名和行号。日志消息：实际的日志内容，此外loguru支持使用颜色来区分不同的日志级别，使得日志输出更加直观.日志等级Loguru可以通过简单的函数调用来记录不同级别的日志，并自动处理日志的格式化和输出。这一特点可以让使用者专注于记录重要的信息，而不必关心日志的具体实现细节。Loguru支持的日志级别，按照从最低到最高严重性排序：TRACE: 最详细的日志信息，用于追踪代码执行过程。Loguru默认情况下使用DEBUG级别作为最低日志记录级别，而不是TRACE级别。这是因为TRACE级别会产生大量的日志信息。DEBUG: 用于记录详细的调试信息，通常只在开发过程中使用，以帮助诊断问题。INFO: 用于记录常规信息，比如程序的正常运行状态或一些关键的操作。SUCCESS: 通常用于记录操作成功的消息，比如任务完成或数据成功保存。WARNING: 用于记录可能不是错误，但需要注意或可能在未来导致问题的事件。ERROR: 用于记录错误，这些错误可能会影响程序的某些功能，但通常不会导致程序完全停止。CRITICAL: 用于记录非常严重的错误，这些错误可能会导致程序完全停止或数据丢失。from loguru import logger logger.debug("这是一条跟踪消息") logger.debug("这是一条调试信息") logger.info("这是一条普通信息") logger.success("操作成功完成") logger.warning("这是一条警告信息") logger.error("这是一条错误信息") logger.critical("这是一条严重错误信息")输出：2024-06-29 19:58:11.535 | DEBUG | __main__:<module>:3 - 这是一条跟踪消息 2024-06-29 19:58:11.536 | DEBUG | __main__:<module>:4 - 这是一条调试信息 2024-06-29 19:58:11.536 | INFO | __main__:<module>:5 - 这是一条普通信息 2024-06-29 19:58:11.537 | SUCCESS | __main__:<module>:6 - 操作成功完成 2024-06-29 19:58:11.537 | WARNING | __main__:<module>:7 - 这是一条警告信息 2024-06-29 19:58:11.538 | ERROR | __main__:<module>:8 - 这是一条错误信息 2024-06-29 19:58:11.538 | CRITICAL | __main__:<module>:9 - 这是一条严重错误信息1.2 日志配置在loguru中，add函数用于添加日志处理器。这个函数用于指定日志消息应该被发送到何处，例如控制台、文件或其他自定义的目的地。add函数主要参数介绍如下：sink: 定义日志消息的输出位置，可以是文件路径、标准输出（stdout）、标准错误（stderr，默认）或其他自定义的输出位置。format: 指定日志消息的格式，可以是简单的字符串，也可以是格式化字符串，支持各种字段插值。level: 设置处理程序处理的日志消息的最低级别。比如设置为DEBUG，则处理程序将处理所有级别的日志消息。filter: 可选参数，用于添加过滤器，根据特定的条件过滤掉不需要的日志消息。colorize: 布尔值，指定是否对日志消息进行着色处理，使日志在控制台中更易于区分。serialize: 布尔值，指定是否对日志消息进行序列化处理，通常与enqueue=True一起使用，以确保多线程安全。enqueue: 布尔值，指定是否将日志消息放入队列中处理，用于多线程应用中避免阻塞。backtrace: 布尔值或字符串，指定是否记录回溯信息，默认为False。diagnose: 布尔值，启用后，会在处理程序内部出现错误时记录诊断信息。rotation: 日志文件轮换的配置，支持按大小或时间进行日志文件的轮换。retention: 用于设置日志文件的保留时间。compression: 布尔值，指定是否对轮换后的日志文件进行压缩处理。from loguru import logger import sys # 终端显示不受该段代码设置 # 添加一个日志处理器，输出到文件 # 设置日志最低显示级别为INFO，format将设置sink中的内容 # sink链接的本地文件，如不存在则新建。如果存在则追写 logger.add(sink="myapp.log", level="INFO", format="{time:HH:mm:ss} | {message}| {level}") # debug结果不被显示到本地文件 logger.debug("这是一条调试信息") logger.info("这是一条普通信息")输出：2024-06-29 19:58:56.159 | DEBUG | __main__:<module>:11 - 这是一条调试信息 2024-06-29 19:58:56.159 | INFO | __main__:<module>:12 - 这是一条普通信息当连续两次调用 add 函数时，loguru 会将新的日志处理器添加到处理器列表中，而不是覆盖之前的处理器。这意味着所有添加的处理器都会接收到日志消息，并且按照它们被添加的顺序来处理这些消息。from loguru import logger logger.add(sink="myapp1.log", level="INFO") logger.add(sink="myapp2.log", level="INFO") # 会同时存入所有add添加日志处理器 logger.info("这是一条普通信息，存入myapp2")如果想删除所有已添加的日志处理器，loguru运行使用 logger.remove()方法不带任何参数来移除所有日志处理器。from loguru import logger import sys # 移除所有日志处理器（包括终端输出） logger.remove() logger.add(sink="myapp3.log", level="INFO", format="{time:HH:mm:ss} | {message}| {level}") logger.debug("这是一条调试信息存入myapp3") logger.info("这是一条普通信息存入myapp3")注意调用logger.remove()之后的所有日志将不会被记录，因为没有处理器了。from loguru import logger # 移除所有日志处理器（包括终端输出） logger.remove() # 没有输出 logger.info("这是一条普通信息存入myapp3")如果希望移除某些日志处理器，而不是从所有日志器中移除，代码如下：from loguru import logger # 移除默认终端logger，如果终端存在。 # logger.remove(0) # 添加多个文件处理器，enqueu设置异步日志记录 handler1 = logger.add("myapp1.log", enqueue=True) print(handler1) # handler_id是移除的处理器的唯一标识符 handler2 = logger.add("myapp2.log") # 记录一些日志 logger.info("这些信息会被记录到两个文件中") # 移除特定的文件处理器 logger.remove(handler1) # 现在只有myapp2.log 会记录日志 logger.info("这条信息只会记录在myapp2.log 中")如果想将日志输出到日志台，代码如下：from loguru import logger import sys logger.remove() # 移除默认输出 # 添加一个日志处理器，输出到控制台，使用自定义格式 logger.add( sink=sys.stdout, level="DEBUG", # green表示颜色 format="<green>{time:HH:mm}</green> <level>{message}</level>" ) # 注意终端显示会同步显示 logger.debug("这是一条调试信息") logger.info("这是一条普通信息")时间自定义 可以使用datatime库来自定义日志时间格式。from datetime import datetime from loguru import logger # 自定义时间格式 # time_format = "%Y-%m-%d %H:%M:%S,%f" # 包括微秒 time_format = "%H:%M:%S,%f" # 包括微秒但不含年月日 # 定义日志格式，使用 datetime.now().strftime() 来格式化时间 log_format = "{time:" + time_format + "} - {level} - {message}" logger.add("myapp.log", format=log_format, level="DEBUG") # 记录一条日志 logger.debug("这是一个带有微秒的测试日志")日志轮换from loguru import logger # 当文件大小达到100MB时创建新的日志文件，旧文件保留并重命名，用于防止单个日志文件变得过大。 logger.add("file_1.log", rotation="100 MB") # 每天中午12时创建新的日志文件，旧文件保留并重命名 logger.add("file_2.log", rotation="12:00") # 当日志文件存在超过一周时创建新的日志文件，旧文件保留并重命名 logger.add("file_3.log", rotation="1 week") # 设置日志文件保留10天 logger.add("file_4.log", retention="10 days") # 当文件大小达到100MB时创建新的日志文件，旧文件保留压缩为zip文件 logger.add('file_{time}.log', rotation="100 MB", compression='zip')1.3 进阶使用异常捕获 @logger.catch装饰器可以用来装饰my_function函数，并将这些异常信息记录到日志中。from loguru import logger logger.add(sink='myapp.log') @logger.catch def my_function(x, y): return x / y res = my_function(0,0)过滤 使用loguru库进行Python日志记录时，可以通过自定义的filter函数来筛选并记录特定的日志信息。此函数接收一个记录对象作为参数，根据日志消息内容(message)、级别(level)或其他日志属性，返回布尔值以决定是否记录该条日志。如果函数返回True，则日志被记录；若返回False，则忽略该日志。from loguru import logger # 定义一个过滤器函数 def my_filter(record): # 只记录包含 "第一" 的日志 return "第一" in record["message"] # 使用过滤器 logger.add("myapp.log", filter=my_filter) # 记录一些日志 logger.info("第一个记录") logger.info("第二个记录")此外可以结合bind方法进行过滤，bind方法用于向日志记录器添加额外的上下文信息。这些信息将被包含在每条日志消息中，但不会改变日志消息本身。如下所示：from loguru import logger def filter_user(record): return record["extra"].get("user") =="A" logger.add("myapp.log", filter=filter_user) # 绑定user logger.bind(user="A").info("来自A") logger.bind(user="B").info("来自B")原文出处：https://www.cnblogs.com/luohenyueji/p/18276299
1.创建单表class Test(Base): __tablename__ = 'user' nid = Colume(Integer,primary_key=True,autoincrement=True) name = Colume(String(32))2.创建一对多class Team(Base): __tablename__ = 'team' tid = Colume(Integer,primary_key=True,autoincrement=True) caption = Colume(String(32)) class user(Base): __tablename__ = 'user' nid = Colume(Integer,primary_key=True,autoincrement=True) name = Colume(String(32)) team_id = Colume(Integer,ForeignKey('team.gid'))写完类，接下来就是把类转化为数据库表了。3.生成表、删除表def init_db(): #根据Base去找它的子类，把所有的子类生成表。 Base.metadata.create_all(engine) def drop_db(): #把Base所有的子类对应表删除。 Base.metadata.drop_all(engine)from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://python:python@192.168.0.57:3306/python_mysql", max_overflow=5) Base = declarative_base() # 单表 class Test(Base): __tablename__ = 'test' nid = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(32)) # 一对多 class Team(Base): __tablename__ = 'team' tid = Column(Integer, primary_key=True, autoincrement=True) caption = Column(String(32)) class user(Base): __tablename__ = 'user' nid = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(32)) team_id = Column(Integer, ForeignKey('team.tid')) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) init_db()执行完上面代码后，就会在对应库生成test、user、group三张表，user表的group_id以group表的gid为外键。4.生成表后开始操作表，添加team表数据from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://python:python@192.168.0.57:3306/python_mysql", max_overflow=5) Base = declarative_base() # 单表 class Test(Base): __tablename__ = 'test' nid = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(32)) # 一对多 class Team(Base): __tablename__ = 'team' tid = Column(Integer, primary_key=True, autoincrement=True) caption = Column(String(32)) class user(Base): __tablename__ = 'user' nid = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(32)) team_id = Column(Integer, ForeignKey('team.tid')) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) # init_db() # drop_db() Session = sessionmaker(bind=engine) session = Session() #往team表里插入两条数据 session.add(Team(caption='dba')) session.add(Team(caption='ddd')) session.commit()5. 添加user表数据Session = sessionmaker(bind=engine) session = Session() #批量添加数据；user表的team_id与team表的tid是有外键的，按理来说要插入的team_id的值必须在team表里有对应的tid值，比如这里插入的tead_id是1、2、3，则team表里的tid至少要有1、2、3，不然会插入失败。 #但是，我发现插入没有对应键值的team_id也不会报错。 session.add_all([ User(name='zzz',team_id=1), User(name='sss',team_id=2), User(name='ccc',team_id=3), ]) session.commit()6.查询单表如果仅仅是查询user表的name值，那不需要联合别的表，直接查询单表即可ret = session.query(User).filter(User.name=='zzz').all() obj = ret[0] print(obj.name) #上面的代码等价于这个： ret = session.query(User.name).filter(User.name=='zzz').all() print(ret)7.通过__repr__()方法改变返回值from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://python:python@192.168.0.57:3306/python_mysql", max_overflow=5) Base = declarative_base() # 创建单表 class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32)) extra = Column(String(16)) __table_args__ = ( UniqueConstraint('id', 'name', name='uix_id_name'), Index('ix_id_name', 'name', 'extra'), ) #__repr__方法是注释的，看print(ret)的输出 #def __repr__(self): # return "%s-%s" %(self.id, self.name) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) init_db() Session = sessionmaker(bind=engine) session = Session() session.add(Users(id=1,name='zsc')) session.commit() ret = session.query(Users).all() print(ret) #结果： [<__main__.Users object at 0x7f1836e80630>] 没有User类里没有__repr__方法时，session.query(Users).all()返回的是类的对象。from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://python:python@192.168.0.57:3306/python_mysql", max_overflow=5) Base = declarative_base() # 创建单表 class Users(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(32)) extra = Column(String(16)) __table_args__ = ( UniqueConstraint('id', 'name', name='uix_id_name'), Index('ix_id_name', 'name', 'extra'), ) #__repr__方法取消注释 def __repr__(self): return "%s-%s" %(self.id, self.name) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) init_db() Session = sessionmaker(bind=engine) session = Session() session.add(Users(id=1,name='zsc')) session.commit() ret = session.query(Users).all() print(ret) #结果： [1-zsc] User类里定义了__repr__方法时，session.query(Users).all()返回的是定义的返回结果。8.联合查询#创建表时指定了外键 ret = session.query(User.name).join(Team).all()等价于SELECT user.name AS FROM user INNER JOIN team ON team.tid = user.team_id #用select的话需要用on指定约束条件，用SQLAlchemy就不用指定了。 #用“isouter=True”指定left join ret = session.query(User.name).join(Team,isouter=True).all()上面的查询，随便是依赖到了别的表，但是结果只是显示了user表的数据，如果想同时显示user和team表的数据，就得用下面的方法了，ret = session.query(User.name,Team.caption).join(Team).all() print(ret) #结果： [('zzz', 'dba'), ('sss', 'ddd')]虽然上面的联合查询已经比直接用select简单了，但是还是很麻烦，所以就有了下面的方法。9.relationship9.1 利用ralationship正向查询正向查询即是使用做外链的表来查询被外链里的数据from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://python:python@192.168.0.57:3306/python_mysql", max_overflow=5) Base = declarative_base() # 一对多 class Team(Base): __tablename__ = 'team' tid = Column(Integer, primary_key=True, autoincrement=True) caption = Column(String(32)) class User(Base): __tablename__ = 'user' nid = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(32)) team_id = Column(Integer, ForeignKey('team.tid')) #加上底下这行后，不用使用.join()也可实现联表查询 #哪个表做外链，就把relationship加到哪个表 favor = relationship("Team", backref='uuu') def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) # init_db() # drop_db() Session = sessionmaker(bind=engine) session = Session() ret = session.query(User).all() for obj in ret: print(obj.nid,obj.name,obj.favor,obj.favor.tid,obj.favor.caption) #结果： 1 zzz <__main__.Team object at 0x7f5c10d02a20> 1 dba 2 sss <__main__.Team object at 0x7f5c10d026a0> 2 ddd #可见，ret仅仅是User的query结果，而使用obj.favor就相当于是使用Team表，即可直接操作team表。9.2利用ralationship实现反向查询反向查询即是使用被外链的表查询到做外链的数据class Test(Base): __tablename__ = 'test' nid = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(32)) # 一对多 class Team(Base): __tablename__ = 'team' tid = Column(Integer, primary_key=True, autoincrement=True) caption = Column(String(32)) class User(Base): __tablename__ = 'user' nid = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(32)) team_id = Column(Integer, ForeignKey('team.tid')) favor = relationship("Team", backref='uuu') def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) # init_db() # drop_db() Session = sessionmaker(bind=engine) session = Session() ret = session.query(Team).filter(Team.caption == 'dba').all() print(ret[0].tid) print(ret[0].caption) print(ret[0].uuu) #结果： 1 dba [<__main__.User object at 0x7f7d3fa5ba20>] #favor = relationship("Team", backref='uuu')里的uuu的作用就是存储着对应的做外链里的数据；比如user里有7个人是dba组的，这时候print(ret[0].uuu)就会返回7个用户的信息；user里有3个dbb组的，这时候print(ret[0].uuu)就会返回3个相关用户的信息。除了一对多还是多对多关系，多对多是专门建一个中间表来存储两张表的关联关系。SQLAlchemy看着麻烦，其实就是记语法而已，多用即可；先建表，再操作单表，再用连表，在整关系，一对多，多对多。原文转载：https://www.cnblogs.com/fuckily/p/6042743.html
{callout color="#999999"}为了运维方便，选择直接用宝塔来管理python fastapi的项目，虽然直接部署可能性能更好更灵活，但是我选择了低层本，每个人的选择可能是不一样的，各有 考虑吧。本文的大逻辑是先写一个helloworld的程序，然后再部署到服务器上{/callout}步骤一：先本地运行一个基于fastapi的helloWorld例子，方便后面在服务器上验证编写基于FastApi的Hello World文件main.pyimport uvicorn from fastapi import FastAPI app = FastAPI() @app.get("/") def sayHi(): return {"message":"Hello world!"} # 启动uvicorn服务，默认端口8000 uvicorn myapi:api --reload if __name__ == '__main__': uvicorn.run('main:app')显示本地运行跑通，本地可以使用vscode编译器，并在运行dos命令pip install fastapi[all] uvicorn main:app --reloadreload参数是为了修改代码后的热部署，运行没有报错后可以浏览器访问： http://127.0.0.1:8000如果看到打印信息则说明OK步骤二：在宝塔上部署python的环境商店安装插件python进程管理插件Python项目管理器：管理应用实例进程守护管理器：实例进程的守护进入Python项目管理器，首先安装python版本，尽量与本地的一致，避免出现本地好的，服务器上出现问题，特别怕依赖包不一致的问题。本地查看版本的命令是python --version在本地生成requirements.txt，否则宝塔创建项目会报错。创建命令如下pip freeze >requirements.txt pip install -r requirements.txt将代码上传到宝塔的/www/wwwpython/helloworld在python进程管理插件创建项目，具体参数如下，记得选择gunicon在配置修改参数，重启。默认为worker_class = 'geventwebsocket.gunicorn.workers.GeventWebSocketWorker' 修改为worker_class = 'uvicorn.workers.UvicornWorker'这里有一个坑，启动后系统会自动暂停，日志报错如下的话，是2个问题，一是启动文件名为main.py，二是启动文件是app，即在main.py中启动命令是：uvicorn.run('main:app')具体报错信息如下：Worker failed to boot.Traceback (most recent call last): File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/arbiter.py", line 586, in spawn_worker worker.init_process() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/workers/ggevent.py", line 203, in init_process super(GeventWorker, self).init_process() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/workers/base.py", line 135, in init_process self.load_wsgi() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi self.wsgi = self.app.wsgi() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/app/base.py", line 67, in wsgi self.callable = self.load() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 52, in load return self.load_wsgiapp() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 41, in load_wsgiapp return util.import_app(self.app_uri) File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/util.py", line 350, in import_app __import__(module) File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gevent/builtins.py", line 96, in __import__ result = _import(*args, **kwargs) File "/www/wwwpython/helloworld/myapi.py", line 9 SyntaxError: Non-ASCII character '\xe5' in file /www/wwwpython/helloworld/myapi.py on line 9, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details [2023-03-25 09:26:44 +0000] [7373] [INFO] Worker exiting (pid: 7373) [2023-03-25 09:26:44 +0000] [7368] [INFO] Shutting down: Master [2023-03-25 09:26:44 +0000] [7368] [INFO] Reason: Worker failed to boot. [2023-03-25 09:26:45 +0000] [7385] [INFO] Starting gunicorn 19.10.0 [2023-03-25 09:26:45 +0000] [7385] [INFO] Listening at: http://0.0.0.0:12345 (7385) [2023-03-25 09:26:45 +0000] [7385] [INFO] Using worker: geventwebsocket.gunicorn.workers.GeventWebSocketWorker [2023-03-25 09:26:45 +0000] [7394] [INFO] Booting worker with pid: 7394 [2023-03-25 09:26:45 +0000] [7394] [ERROR] Exception in worker process Traceback (most recent call last): File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/arbiter.py", line 586, in spawn_worker worker.init_process() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/workers/ggevent.py", line 203, in init_process super(GeventWorker, self).init_process() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/workers/base.py", line 135, in init_process self.load_wsgi() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi self.wsgi = self.app.wsgi() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/app/base.py", line 67, in wsgi self.callable = self.load() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 52, in load return self.load_wsgiapp() File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 41, in load_wsgiapp return util.import_app(self.app_uri) File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gunicorn/util.py", line 350, in import_app __import__(module) File "/www/wwwpython/helloworld/helloworld_venv/lib/python2.7/site-packages/gevent/builtins.py", line 96, in __import__ result = _import(*args, **kwargs) File "/www/wwwpython/helloworld/myapi.py", line 9 SyntaxError: Non-ASCII character '\xe5' in file /www/wwwpython/helloworld/myapi.py on line 9, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details [2023-03-25 09:26:45 +0000] [7394] [INFO] Worker exiting (pid: 7394) [2023-03-25 09:26:45 +0000] [7385] [INFO] Shutting down: Master [2023-03-25 09:26:45 +0000] [7385] [INFO] Reason: Worker failed to boot.设置映射域名，比如设置了 demo.xxx.com，即系统会自动创建这个域名的网站，并设置了反向代理通过 http://demo.xxx.com 返回正常即配置完成。原文出处：https://www.cnblogs.com/jpeanut/p/17254756.html
逻辑结构特点：只是描述数据结构中数据元素之间的联系规律是从具体问题中抽象出来的数学模型，是独立于计算机存储器的(与机器无关)逻辑结构分类线性结构集合中必存在唯一的一个 "第一个元素"集合中必存在唯一的一个 "最后的元素"除最后元素之外，其他数据元素均有唯一的 "后继"除第一元素之外，其他数据元素均有唯一的 "前驱"树形结构(层次结构)树形结构指的是数据元素之间存在着"一对多"的树形关系的数据结构，是一类重要的非线性数据结构。在树形结构中，树根结点没有前驱结点，其余每个结点有且只有一个前驱结点。叶子结点没有后续结点，其余每个结点的后续节点数可以是一个也可以是多个。图状结构（网状结构)图是一种比较复杂的数据结构。在图结构中任意两个元素之间都可能有关系，也就是说这是一种多对多的关系。其他结构除了以上几种常见的逻辑结构外，数据结构中还包含其他的结构，比如集合等。有时根据实际情况抽象的模型不止是简单的某一种，也可能拥有更多的特征。存储结构特点:是数据的逻辑结构在计算机存储器中的映象（或表示)存储结构是通过计算机程序来实现的，因而是依赖于具体的计算机语言的。存储结构分类顺序存储顺序存储( Sequential Storage ):将数据结构中各元素按照其逻辑顺序存放于存储器一片连续的存储空间中。链式存储链式存储(Linked Storage )∶将数据结构中各元素分布到存储器的不同点，用记录下一个结点位置的方式建立它们之间的联系，由此得到的存储结构为链式存储结构。索引存储索引存储（ lndexed Storage ):在存储数据的同时，建立一个附加的索引表，即索引存储结构=数据文件+索引表。线性表的链式存储只能操作头部节点指向下一个定义将线性表L=(a0,a1.......an-1)中各元素分布在存储器的不同存储块，称为结点，每个结点（尾节点除外）中都持有一个指向下一个节 点的引用，这样所得到的存储结构为链表结构。特点逻辑上相邻的元素ai, ai+1，其存储位置也不一定相邻;存储稀疏，不必开辟整块存储空间。对表的插入和删除等运算的效率较高。逻辑结构复杂，不利于遍历。栈先进后出 --> 水桶定义栈是限制在一端进行插入操作和删除操作的线性表（俗称堆栈），允许进行操作的一端称为"栈顶”，另一固定端称为"栈底”，当栈中没有元素时称为空栈"。特点栈只能在一端进行数据操作栈模型具有先进后出或者叫做后进先出的规律队列先进先出 --> 排队定义队列是限制在两端进行插入操作和删除操作的线性表，允许进行存入操作的一端称为队尾”，允许进行删除操作的一健称为“队就-b特点:队列只能在队头和队尾进行数据操作队列模型具有先进先出或者叫做后进后出的规律树形结构定义树( Tree )是n ( n≥0 )个节点的有限集合T，它满足两个条件∶有且仅有一个特定的称为根(Root )的节点;其余的节点可以分为m ( m≥0 )个互不相交的有限集合T1、T2、......、Tm，其中每一个集合又是一棵树，并称为其根的子树( Subtree ) 。基本概念一个节点的子树的个数称为该节点的度数，一棵树的度数是指该树中节点的最大度数。度数为零的节点称为树叶或终端节点，度数不为零的节点称为分支节点，除根节点外的分支节点称为内部节点。一个节点的子树之根节点称为该节点的子节点，该节点称为它们的父节点，同一节点的各个子节点之间称为兄弟节点。一棵树的根节点没有父节点，叶节点没有子节点。一个节点系列k1,k2，......,ki,ki+1,......,kj,并满足ki是ki+1的父节点，就称为一条从k1到kj的路径，路径的长度为j-1,即路径中的边数。路径中前面的节点是后面节点的祖先，后面节点是前面节点的子孙。节点的层数等于父节点的层数加一，根节点的层数定义为树中节点层数的最大值称为该树的高度或深度。m ( m≥0 ）棵互不相交的树的集合称为森林。树去掉根节点就成为森林，森林加上一个新的根节点就成为树。二叉树定义二叉树( Binary Tree )是n ( n≥0 )个节点的有限集合，它或者是空集( n=0 ），或者是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。二叉树与普通有序树不同，二叉树严格区分左孩子和右孩子，即使只有一个子节点也要区分左右。二叉树的特征工叉树第i ( i≥1)层上的节点最多为2i-1个。深度为k ( k≥1)的二叉树最多有2k一1个节点。在任意一棵二叉树中，树叶的数目比度数为2的节点的数目多一。满二叉树︰深度为k ( k≥1)时有2k一1个节点的二叉树。完全二叉树∶只有最下面两层有度数小于2的节点，且最下面一层的叶节点集中在最左边的若干位置上。二叉树的遍历沿某条搜索路径周游二叉树，对树中的每一个节点访问一次且仅访问一次。先序遍历︰先访问树根，再访问左子树，最后访问右子树; --> 根左右中序遍历︰先访问左子树，再访问树根，最后访问右子树; --> 左根右后序遍历︰先访问左子树，再访问右子树，最后访问树根﹔ --> 左右根 层次遍历:从根节点开始，逐层从左向右进行遍历。递归思想什么是递归?所谓递归函数是指一个函数的函数体中直接调用或间接调用了该函数自身的函数。这里的直接调用是指一个函数的函数体中含有调用自身的语句，间接调用是指一个函数在函数体里有调用了其它函数，而其它函数又反过来调用了该函数的情况。 递归函数调用的执行过程分为两个阶段递推阶段︰从原问题出发，按递归公式递推从未知到已知,最终达到递归终止条件。回归阶段:按递归终止条件求出结果，逆向逐步代入递归公式，回归到原问题求解。 优点与缺点优点︰递归可以把问题简单化，让思路更为清晰,代码更简洁缺点︰递归因系统环境影响大，当递归深度太大时，可能会得到不可预知的结果算法基础基础概念特征定义算法( Algorithm )是一个有穷规则（或语句、指令)的有序集合。它确定了解决某一问题的一个运算序列。对于问题的初始输入，通过算法有限步的运行，产生一个或多个输出。数据的逻辑结构与存储结构密切相关算法设计:取决于选定的逻辑结构算法实现:依赖于采用的存储结构算法的特性有穷性—～算法执行的步骤（或规则)是有限的;确定性——每个计算步骤无二义性;可行性——每个计算步骤能够在有限的时间内完成;输入，输出——存在数据的输入和出输出评价算法好坏的方法正确性︰运行正确是一个算法的前提。可读性︰容易理解、容易编程和调试、容易维护。健壮性:考虑情况全面，不容以出现运行错误。时间效率高︰算法消耗的时间少。储存量低︰占用较少的存储空间。排序和查找排序排序(Sort)是将无序的记录序列(或称文件）调整成有序的序列。常见排序方法∶冒泡排序冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列，一次比较两个元素，如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换，也就是说该数列已经排序完成。选择排序工作原理为，首先在未排序序列中找到最小元素，存放到排序序列的起始位置，然后，再从剩余未排序元素中继续寻找最小元素，然后放到排序序列末尾。以此类推，直到所有元素均排序完毕
《PHP中的一些常用的超全局变量总结》PHP是一种广泛使用的服务器端脚本语言,它被用于开发各种Web应用程序,在PHP中,有一些特殊的全局变量,被称为超全局变量,在本文中,我们将深入了解PHP中的一些...php是一种广泛使用的服务器端脚本语言，它被用于开发各种Web应用程序。在PHP中，有一些特殊的全局变量，被称为超全局变量。超全局变量在整个脚本中都是可用的，无需使用global关键字来访问它们。1. $_GET:$_GET是一个关联数组，用于从URL中获取参数的值。当使用GET方法发送HTTP请求时，参数会附加在URL的末尾，例如：http://example.com/?id=123。可以使用$_GET来获取这些参数的值，例如$_GET['id']将返回123。这在处理表单提交、页面导航和URL参数传递时非常有用。2. $_POST:$_POST也是一个关联数组，用于从HTTP请求的正文中获取参数值。当使用POST方法发送HTTP请求时，参数会被包含在请求正文中，而不是URL中。可以使用$_POST来获取这些参数的值，例如$_POST['name']将返回提交的名称值。与$_GET相比，$_POST更适用于处理敏感数据，因为它们不会在URL中暴露出来。3. $_SERVER:$_SERVER是一个包含了服务器和执行环境信息的关联数组。它提供了许多有用的信息，如当前页面的URL、请求方法、客户端IP地址、服务器信息等。例如，$_SERVER['REQUEST_METHOD']将返回当前请求的方法，$_SERVER['REMOTE_ADDR']将返回客户端的IP地址。$_SERVER在处理会话管理、用户认证和访问控制时非常有用。4. $_SESSION:$_SESSION是用于在不同页面之间存储和访问会话数据的关联数组。它可以用来跟踪用户会话状态，存储用户信息，以及实现用户登录和注销等功能。使用$_SESSION，可以在不同的页面之间传递数据，并且数据在用户关闭浏览器后仍然保留。5. $_COOKIE:$_COOKIE是一个包含了客户端发送的HTTP Cookie的关联数组。Cookie是一种在客户端浏览器中存储数据的机制，用于跟踪用户状态和存储用户偏好设置。通过$_COOKIE，可以读取和修改Cookie的值，例如$_COOKIE['username']将返回存储在名为"username"的Cookie中的值。6. $_REQUEST:$_REQUEST是一个关联数组，包含了通过GET、POST和COOKIE方式提交的参数的值。它可以同时获取GET和POST的参数值。然而，由于它可以获取多种来源的参数，因此在使用之前需要注意安全性和数据一致性。7. $_FILES:$_FILES是一个关联数组，用于访问通过HTTP POST方法上传的文件。它包含了上传文件的属性，如文件名、文件类型、文件大小等。通过$_FILES，可以将上传的文件保存到服务器上的指定位置。8. $_ENV:$_ENV是一个包含了环境变量的关联数组。环境变量是在操作系统中设置的一些系统级别的值，在PHP中可以通过$_ENV来访问它们。例如，$_ENV['PATH']将返回操作系统中配置的路径。9. $GLOBALS:$GLOBALS是一个包含了全局变量的关联数组。它包含了脚本中定义的所有全局变量，以变量名为键名，变量值为键值。通过$GLOBALS，可以在任何地方访问和修改全局变量的值。总结这些超全局变量提供了丰富的功能，用于访问和操作PHP脚本中的各种数据和环境。它们在不同的场景下有不同的用途，例如获取用户输入、处理文件上传、访问系统环境变量等。在使用这些超全局变量时，需要注意安全性和数据验证，以确保应用程序的安全性和可靠性。需要注意的是，尽管超全局变量可以在任何地方访问，但在编写代码时应尽量避免过度使用它们，以免造成代码的混乱和不可维护性。合理使用超全局变量，可以帮助我们简化代码、提高开发效率，并使代码更具可读性和可维护性。超全局变量在PHP中发挥着重要作用，简化了许多常见的Web开发任务。它们提供了一种方便的方式来访问和操作请求参数、服务器信息、会话数据和Cookie。了解和正确使用这些超全局变量，可以帮助我们更高效地开发PHP应用程序，并确保数据的安全性和一致性。需要注意的是，由于超全局变量是全局范围的，因此要特别小心处理它们，以防止安全漏洞和数据冲突。在使用超全局变量时，应该进行验证和过滤，确保输入的数据是安全的，并避免直接使用它们来执行敏感操作。原文出处：http://www.cppcns.com/wangluo/php/641641.html
{callout color="#d1d1d1"} 《PHP获取客户端真实IP地址》PHP获取客户端真实IP地址,只要根据实际部署情况选择相对应的代码获取访客IP地址,是可以确保程序不被假IP欺骗的...{/callout} php获取客户端真实IP地址，需要根据具体的服务器环境来确定使用哪种方法。目前搜索到的方法，大多是直接贴代码，没有针对不同情况作出说明，有可能导致系统被假IP骗过（IP欺骗）。 很多文章都提到“无法保证获取到的访客IP地址100%准确”，是否意味着PHP获取访客IP一定有漏洞可钻呢？ 只要根据实际部署情况选择相对应的代码获取访客IP地址，是可以确保程序不被假IP欺骗的。PHP的运行方式 PHP支持非常多的运行方式，例如php-cgi、php-fpm、swoole、php-cli、php-mod等。其中，php-fpm是php的fast-cgi的进程管理器。php-mod通常配合Apache使用，而php-fpm通常配合Nginx使用。从PHP5.4开始，PHP甚至可以以内置PHP服务（Buid-in web server）方式运行。逐渐的，PHP形成了很多经典搭配，例如LAMP、LNMP、LNMPA、IIS+PHP。 无论采用哪种PHP运行方式，分清是谁传递IP给PHP程序即可。这里对几种常见环境进行分析。无代理层（PHP内置服务器/swoole） 由于客户端IP数据是从TCP/IP协议层传递过来的，因此在没有中间代理（客户端和服务器直连）的情况下，可以直接通过标准方法REMOTE_ADDR获得与PHP直接通讯的IP地址。 例如$_SERVER['REMOTE_ADDR']或getenv("REMOTE_ADDR")。 而在swoole中，可以通过$request->server['remote_addr']获得客户端IP地址。 在这种情况下，REMOTE_ADDR不可以显式的伪造，获取到的是实际与服务器连接的IP地址，是可靠的。Nginx代理（Nginx + php-fpm / Nginx + swoole） 常见的LNMP方案，属于Nginx反向代理。Nginx与php的通讯，无论Unix socket方式还是tcp socket方式，都跟Nginx的Header配置有关系。proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 可以在第一层nginx代理设置 proxy_set_header X-Forwarded-For $remote_addr,其他层nginx代理设置proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for,这样就可以通过X-Forwarded-For获取客户端真实IP地址。如果设置了X-Real-IP $remote_addr，则通过X-Real-IP获取客户端IP地址。PHP代码：// php-fpm等 $_SERVER['HTTP_X_FORWARDED_FOR']); // swoole $this->http_input->header('x-real-ip'); 总结来说，当有Nginx代理的情况下，需要根据具体配置来选择获取的Header，从而正确获取客户端IP地址，此时PHP中的的REMOTE_ADDR可能是最后一级代理的IP地址。Apache代理（Apache + php_mod / Apache + php-fpm） 通常在Apache + PHP方案中，获取IP地址取决于Apache配置信息。 绝大部分情况可以使用$_SERVER["REMOTE_ADDR"]获取到真实客户端IP地址。 如果Apache上级存在Nginx，那么可以在Apache中使用mod_rpaf模块将客户端IP地址传递到X-Forwarded-For头中。PAFheader X-Forwarded-For负载均衡/云虚拟机/Serverless 在负载均衡条件下，需要查阅对应负载均衡程序的文档，来决定使用哪种方法获取真实客户端IP地址。 在大部分虚拟主机、负载均衡及无服务器中，可以通过HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR或X-REAL-IP其一得知客户端IP地址。但是不建议对多种来源进行空值判断，这样容易被伪造者利用，从而实现IP欺骗。 目前并未有标准规定将客户端IP地址放入名为CLIENT_IP的环境变量，但是有不少老虚拟主机供应商这样做。新的提供商都不再使用该环境变量。网上大量的PHP文章中都是优先获取HTTP_CLIENT_IP的值，因此导致网上存在大量的服务器可以被伪造IP欺骗。 可能有该漏洞的代码：<?php if(!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) { $ip = $_SERVER['REMOTE_ADDR']; }IIS + PHP 这种情况下直接使用$_SERVER['REMOTE_ADDR']方法即可。总结 无论是哪种情况，如果用户使用匿名代理访问服务器，只能获取到代理服务器的IP地址，其IP地址仍然具有参考意义。 获取客户端真实IP地址，此事需要查阅相关资料根据具体情况选用某一个方法，而不是 复制粘贴 。 部分方法获取到的值可能是一个数组。 不建议对获取到的IP地址进行正则过滤，有可能你获取到的是一个IPv6地址。原文出处：http://www.cppcns.com/wangluo/php/646369.html
{callout color="#d1d1d1"}《PHP定界符php和html代码混编方法实例详解》：本文主要介绍PHP定界符php和html代码混编方法实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...{/callout}正文 PHP定界符的运用，极大地方便了PHP和html混编的结构，因为在定界符中可以包括变量函数和html代码，而且可以正常解析。不用对字符串进行转义，可以是多行字符串。 PHP定界符是一种用于在字符串中定义大段代码的语法结构，它有三种形式：一、 HEREDOC（<<<）定界符 HEREDOC被称为“长字符串常量”，用于包含多行字符串。格式如下：$string = <<<EOT 这是一段多行字符串 可以包含变量以及任何其他语法 EOT; 在这个例子中，HEREDOC开始于"<<<EOT"，结束于"EOT;"，三个EOT之间的所有内容都是字符串，可以包含变量以及其他语法。 在PHP中，HEREDOC（Here Document）是一种用于创建多行字符串的语法结构。它允许您通过指定一个标识符来引用字符串。<<<是HEREDOC的定界符，用于指示开始和结束标识符之间的字符串是HEREDOC类型的。下面为大家写几个示例代码：1. 使用字符串字面量替换<<<：$name = <<<EOT Hello, world! This is a sample string. EOT; 在这个示例代码中，使用双引号包围的字符串字面量替换<<<，并在末尾添加了换行符EOT;作为结束标识符。2. 使用自定义标识符：$greeting = <<<Greetings Hello, $name! Greetings; 上述代码中，使用自定义的标识符Greetings来引用字符串。标识符可以是任何有效的PHP标识符，但通常建议使用有意义的名称。3. 嵌套使用HEREDOC：$message = <<<EOT Hello, $name! This is a sample message. Here's a list of fruits: $fruits EOT; 上述代码中，在字符串中使用另一个HEREDOC来创建嵌套的字符串。这允许您在字符串中嵌入多行文本。二、 NOWDOC（<<<'标识符'） NOWDOC与HEREDOC非常相似，但不会解析变量和特殊字符。NOWDOC是PHP中的另一种字符串语法结构，类似于HEREDOC，但不会解析其中的变量。NOWDOC使用单引号来定界字符串，并在<<<后的标识符前加上单引号。格式如下：$string = <<<'EOT' 这是一段多行字符串 不会解析包含在字符串中的任何变量或特殊字符 EOT; 注意到这里的标识符是用单引号包裹的，它与下面的纯文本是完全相同的。与HEREDOC不同，NOWDOC不会替换其中的变量或转义任何特殊字符。1. 基本示例：$message = <<<'EOT' Hello, world! This is a sample string. EOT; 该示例代码，使用NOWDOC的语法结构来创建一个多行字符串。由于使用了单引号定界符，其中的变量不会被解析。2. 使用自定义标识符：$greeting = <<<'Greetings' Hello, $name! Greetings; 在这个示例代码中，使用自定义的标识符Greetings来引用字符串。与NOWDOC一样，其中的变量不会被解析。 值得注意的是，由于NOWDOC不会解析字符串中的变量，因此您无法在其中引用或嵌入变量。如果需要在字符串中使用变量值，应该使用HEREDOC。所以NOWDOC适用于那些不需要解析变量的情况。使用单引号定界符的NOWDOC语法可以确保字符串中的文本保持原样，不会被解析或替换为变量值。三、短标签写法（<?= ?>） 短标签写法允许PHP代码直接嵌入HTML中，使用"<?="输出变量的值。例如：<?= $variable; ?> 在这个例子中，"$variable"的值将被输出到HTML页面中。请注意，您必须启用短标签才能使用它，通过在php.ini文件中将short_open_tag选项设置为"on"，或者在代码中使用"<?php"标记。四、使用<?php"标记在html代码中嵌入php代码，这种方法比较好用，但是代码看起来会比较乱！<?php $nus=rand(1,10);//赋值变量为0到10的随意数字 ?> html中嵌入php代码 随机数值是：<?php echo $nus;?> 注意，php代码不会在html文件中被解释，但是html代码可以在php文件中使用！五、把html代码赋值给php变量在php代码中，把html代码赋值给php变量也可以实现混编。示例1：<?php $html = ' ' ; $html .= ' ' ; $html .= ' 你好html ' ; $html .= ' ' ; $html .= ' ' ; $html .= ' 这就是输出php变量$html的效果! ' ;//这里的$html在单引号内，不会被解释，会被视为字符串直接输出！ $html .= ' ' ; $html .= ' ' ; echo $html; ?>PHP定界符php和html代码混编方法实例详解示例2：<?php $html = '//注意这里下一行起 你好html 这就是输出php变量$html的效果! ';//html代码到这里结束，这样的效果和使用HEREDOC（<<<）定界符的效果是一样的！ echo $html;这里的输出内容与示例1是一样的！ ?>六、总结 在使用PHP开发项目的时候，使用php与html代码混编是一件非常常见的事情，体现出php语言的灵活性。你可能会好奇，为什么我们在使用dz、zblog、织梦建站的时候，php文件里没有看到html代码。那是因为他们采用了模板分离的方法，把php文件和html分开出来编写代码，然后再通过php把html文件转换成php与html代码混编的逻辑代码，最终都是以html的形式呈现网站内容！如果你也希望php代码和html分开编写使用，你可以去找一款合适自己的模板引擎来帮助你达到该目的！原文出处：http://www.cppcns.com/wangluo/php/649944.html
《Python利用psutil库进行监控进程和资源》psutil是Python系统和进程工具库,它提供了一种跨平台的方式来获取系统信息、管理系统进程、监控系统性能、操作系统资源等,下面就跟随小编一起来... python作为一门强大的编程语言，拥有丰富的标准库和第三方库，使得开发者可以轻松完成各种任务。在系统监控和管理方面，psutil库无疑是一个强大的工具，它可以轻松地获取系统信息、管理进程、监控系统性能等。本文将深入探讨psutil库，介绍它的基本概念、用法和示例代码，帮助大家更好地理解和利用这个强大的库。什么是psutil库psutil是Python系统和进程工具库（Python System and Process Utilities）。它提供了一种跨平台的方式来获取系统信息、管理系统进程、监控系统性能、操作系统资源等。无论是在Windows、linux还是MACOS，psutil都能提供一致的API，使得开发者可以轻松地编写跨平台的代码。安装psutil库要开始使用psutil库，首先需要安装它。可以使用pip来进行安装：pip install psutil安装完成后，就可以导入并开始使用psutil库了。import psutil获取系统信息获取CPU信息import psutil # 获取CPU核心数 cpu_count = psutil.cpu_count(logical=False) print(f"Physical CPU Count: {cpu_count}") # 获取逻辑CPU核心数（包括超线程） logical_cpu_count = psutil.cpu_count(logical=True) print(f"Logical CPU Count: {logical_cpu_count}") # 获取CPU使用率 cpu_usage = psutil.cpu_percent(interval=1, percpu=True) for i, usage in enumerate(cpu_usage, 1): print(f"CPU {i} Usage: {usage}%")获取内存信息import psutil # 获取内存总量 total_memory = psutil.virtual_memory().total print(f"Total Memory: {total_memory / (1024 ** 3):.2f} GB") # 获取可用内存 available_memory = psutil.virtual_memory().available print(f"Available Memory: {available_memory / (1024 ** 3):.2f} GB") # 获取内存使用率 memory_usage = psutil.virtual_memory().percent print(f"Memory Usage: 1.15MB%")获取磁盘信息import psutil # 获取所有磁盘分区信息 disk_partitions = psutil.disk_partitions() for partition in disk_partitions: print(f"Device: {partition.device}") print(f"Mountpoint: {partition.mountpoint}") print(f"Fstype: {partition.fstype}") print("") # 获取指定分区的使用情况 disk_usage = psutil.disk_usage("/") print(f"Total: {disk_usage.total / (1024 ** 3):.2f} GB") print(f"Used: {disk_usage.used / (1024 ** 3):.2f} GB") print(f"Free: {disk_usage.free / (1024 ** 3):.2f} GB") print(f"Usage: {disk_usage.percent}%")获取网络信息import psutil # 获取网络接口列表 network_interfaces = psutil.net_if_addrs() for interface, addresses in network_interfaces.items(): print(f"Interface: {interface}") for address in addresses: print(f" Family: {address.family}") print(f" Address: {address.address}") print(f" Netmask: {address.netmask}") print(f" Broadcast: {address.broadcast}") print("")管理进程查询进程信息import psutil # 获取所有进程列表 all_processes = psutil.process_iter(attrs=['pid', 'name']) for process in all_processes: print(f"Process ID: {process.info['pid']}, Name: {process.info['name']}") # 根据进程名称查询进程 process_name = "python3" process_list = [p.info for p in all_processes if p.info['name'] == process_name] for process in process_list: print(f"Process ID: {process['pid']}, Name: {process['name']}")创建新进程import psutil import subprocess # 创建新进程 subprocess.Popen(["notepad.exe"]) # 检查进程是否存在 process_name = "notepad.exe" is_running = any(process.info['name'] == process_name for process in psutil.process_iter(attrs=['name'])) print(f"{process_name} is running: {is_running}")终止进程import psutil # 根据进程名称终止进程 process_name = "notepad.exe" for process in psutil.process_iter(attrs=['name']): if process.info['name'] == process_name: process.terminate() print(f"Terminated {process_name}")监控系统性能监控CPU利用率import psutil import time # 监控CPU利用率 for _ in range(5): cpu_percent = psutil.cpu_percent(interval=1) print(f"CPU Usage: {cpu_percent}%") time.sleep(1)监控内存使用量import psutil import time # 监控内存使用量 for _ in range(5): memory_usage = psutil.virtual_memory().percent print(f"Memory Usage: 1.15MB%") time.sleep(1)监控磁盘IOimport psutil import time # 监控磁盘IO for _ in range(5): disk_io = psutil.disk_io_counters() print(f"Read Count: {disk_io.read_count}") print(f"Write Count: {disk_io.write_count}") time.sleep(1)监控网络流量import psutil import time # 监控网络流量 for _ in range(5): network_io = psutil.net_io_counters() print(f"Bytes Sent: {network_io.bytes_sent} bytes") print(f"Bytes Received: {network_io.bytes_recv} bytes") time.sleep(1)操作系统资源获取用户信息import psutil # 获取当前用户信息 user_info = psutil.users() for user in user_info: print(f"User: {user.name}") print(f"Terminal: {user.terminal}") print(f"Host: {user.host}") print("")获取虚拟内存信息import psutil # 获取虚拟内存信息 virtual_memory = psutil.virtual_memory() print(f"Total: {virtual_memory.total / (1024 ** 3):.2f} GB") print(f"Used: {virtual_memory.used / (1024 ** 3):.2f} GB") print(f"Free: {virtual_memory.free / (1024 ** 3):.2f} GB") print(f"Usage: {virtual_memory.percent}%")安全性和权限考虑 在使用psutil库时，需要注意安全性和权限问题。某些功能可能需要管理员权限或超级用户权限才能访问，例如终止进程、查询其他用户的进程等。确保你的程序以合适的权限运行，以避免潜在的问题。总结psutil库是Python中用于系统监控和管理的强大工具，它提供了丰富的功能来获取系统信息、管理进程、监控系统性能等。本文介绍了psutil库的基本用法和示例代码，帮助大家更好地理解和利用这个库。无论是用于系统管理、性能监控、自动化任务还是系统信息收集，psutil都是一个不可或缺的Python神器。
简介PhpSpreadsheet要求PHP 7.3及以上版本，是PHPExcel库的扩展版本，可用来读写xls、xlsx、CSV、HTML等格式文件。FormatReadingWritingOpen Document Format/OASIS (.ods)✓✓Office Open XML (.xlsx) Excel 2007 and above✓✓BIFF 8 (.xls) Excel 97 and above✓✓BIFF 5 (.xls) Excel 95✓ SpreadsheetML (.xml) Excel 2003✓ Gnumeric✓ HTML✓✓SYLK✓ CSV✓✓PDF (using either the TCPDF, Dompdf or mPDF libraries, which need to be installed separately) ✓PhpSpreadsheet安装方法可参考 禅道二次开发（四）：集成PhpSpreadsheet解析Excel文件 下面来介绍PhpSpreadsheet读写excel格式文件方法写入excel文件以写入Xlsx格式文件为例，<?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; /* 写入excel */ $fileName = './test.xlsx'; $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setCellValue('A1', 'Hello World !'); $sheet->setCellValue('C3', '你好 !'); $writer = new Xlsx($spreadsheet); $writer->save($fileName); ?>工作表默认样式可以设置工作表默认样式，比如$spreadsheet->getDefaultStyle()->getFont()->setName('宋体'); // 字体 $spreadsheet->getDefaultStyle()->getFont()->setSize(8); // 字体大小 $spreadsheet->getDefaultStyle()->getAlignment()->setWrapText(true); // 自动换行 $spreadsheet->getDefaultStyle()->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); //垂直居中单元格样式use PhpOffice\PhpSpreadsheet\Style\Alignment; $spreadsheet->getActiveSheet()->getStyle('A1:D4')->getAlignment()->setWrapText(true); // 自动换行 $caseSpreadsheet->getActiveSheet()->getStyle('A1:D4')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); //垂直居中单元格对齐方式包括水平对齐和垂直对齐：// 水平对齐样式 HORIZONTAL_GENERA HORIZONTAL_LEFT HORIZONTAL_RIGHT HORIZONTAL_CENTER HORIZONTAL_CENTER_CONTINUOUS HORIZONTAL_JUSTIFY HORIZONTAL_FILL HORIZONTAL_DISTRIBUTED // Excel2007 only // 垂直对齐样式 VERTICAL_BOTTOM VERTICAL_TOP VERTICAL_CENTER VERTICAL_JUSTIFY VERTICAL_DISTRIBUTED // Excel2007 only除了设置单元格的字体，还可以设置边框，填充颜色等样式信息。比如设置单元格背景颜色：$spreadsheet->getActiveSheet()->getStyle('E2')->getFill() ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) ->getStartColor()->setARGB('FFFF0000'); // 多个单元格，如果设置多个单元格推荐此方法，性能更优。 $spreadsheet->getActiveSheet()->getStyle('B3:B7')->getFill() ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) ->getStartColor()->setARGB('FFFF0000');注意：颜色代码为ARGB，带了Alpha通道。设置单元格高度，某一行高度：// 设置列宽 $spreadsheet->getActiveSheet() ->getColumnDimension('A') ->setWidth(30); // 自动列宽 $spreadsheet->getActiveSheet() ->getColumnDimension('A') ->setAutoSize(true); // 默认列宽 $spreadsheet->getActiveSheet() ->getDefaultColumnDimension() ->setWidth(12); 单元格数据类型use PhpOffice\PhpSpreadsheet\Cell\DataType; $spreadsheet->getActiveSheet()->setCellValueExplicit("A1", "123", DataType::TYPE_STRING); /* TYPE_STRING2 TYPE_STRING TYPE_FORMULA TYPE_NUMERIC TYPE_BOO TYPE_NULL TYPE_INLINE TYPE_ERROR */数字添加引号前缀：$spreadsheet->getActiveSheet()->setCellValueExplicit("A1", "123", DataType::TYPE_STRING); $spreadsheet->getActiveSheet()->getStyle("A1")->setQuotePrefix(true); // $spreadsheet->getActiveSheet()->getStyle("A1")->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT);设置数据有效性：use PhpOffice\PhpSpreadsheet\Cell\DataValidation; $objValidation = $spreadsheet->getActiveSheet()->getCell('C1')->getDataValidation(); // 设置数据有效性的单元格 $objValidation -> setType(DataValidation::TYPE_LIST) -> setErrorStyle(DataValidation::STYLE_INFORMATION) -> setAllowBlank(false) -> setShowInputMessage(true) -> setShowErrorMessage(true) -> setShowDropDown(true) -> setErrorTitle('错误提示') -> setError('您输入的值有误') -> setPromptTitle('结果') -> setFormula1('"成功,失败"');冻结单元格$sheet = $spreadsheet->getActiveSheet(); $sheet->freezePane('A2'); // 冻结第一行 $sheet->freezePane('B1'); // 冻结第一列 $sheet->freezePane('B3'); // 冻结B3单元格单元格条件格式可以设置单元格的条件格式，可以对满足某个条件的单元格设置样式，比如设置大于80的单元格：$conditional = new \PhpOffice\PhpSpreadsheet\Style\Conditional(); $conditional->setConditionType(\PhpOffice\PhpSpreadsheet\Style\Conditional::CONDITION_CELLIS); $conditional->setOperatorType(\PhpOffice\PhpSpreadsheet\Style\Conditional::OPERATOR_GREATERTHAN); $conditional->addCondition(80); $conditional->getStyle()->getFont()->getColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_DARKGREEN); $conditional->getStyle()->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID); $conditional->getStyle()->getFill()->getStartColor()->setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_GREEN); $conditionalStyles = $spreadsheet->getActiveSheet()->getStyle('A1:A10')->getConditionalStyles(); $conditionalStyles[] = $conditional; $spreadsheet->getActiveSheet()->getStyle('A1:A10')->setConditionalStyles($conditionalStyles);可以使用的条件及操作符：// Condition types CONDITION_NONE CONDITION_CELLIS CONDITION_CONTAINSTEXT CONDITION_EXPRESSION CONDITION_CONTAINSBLANKS CONDITION_NOTCONTAINSBLANKS CONDITION_DATABAR CONDITION_NOTCONTAINSTEXT // Operator types OPERATOR_NONE OPERATOR_BEGINSWITH OPERATOR_ENDSWITH OPERATOR_EQUA OPERATOR_GREATERTHAN OPERATOR_GREATERTHANOREQUAL OPERATOR_LESSTHAN OPERATOR_LESSTHANOREQUAL OPERATOR_NOTEQUAL OPERATOR_CONTAINSTEXT OPERATOR_NOTCONTAINS OPERATOR_BETWEEN OPERATOR_NOTBETWEEN写入图片将图片写入某个单元格中：$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); $drawing->setName('Logo'); $drawing->setDescription('Logo'); $drawing->setPath($diagramPath); $drawing->setHeight(120); $drawing->setCoordinates("D2"); $drawing->setOffsetX(0); $drawing->setRotation(0); $drawing->getShadow()->setVisible(true); $drawing->getShadow()->setDirection(0); $drawing->setWorksheet($spreadsheet->getActiveSheet());设置超链接给单元格设置超链接：$spreadsheet->getActiveSheet()->getCell('B2')->getHyperlink()->setUrl("sheet://'Sheetname'!A1"); // 当前文档位置 $spreadsheet->getActiveSheet()->getCell('B2')->getHyperlink()->setUrl("https://www.baidu.com/"); // 外链地址读取excel文件下面介绍读取excel文件方法。读取文本数据<?php require 'vendor/autoload.php'; use PhpOffice\ PhpSpreadsheet\ Spreadsheet; use PhpOffice\ PhpSpreadsheet\ Writer\ Xlsx; /* 读取excel */ // $reader = new Xlsx(); // $spreadsheet = $reader->load($fileName); $reader = IOFactory::createReader('Xlsx'); $spreadsheet = $reader - > load($fileName); // $reader->setReadDataOnly(true); // 设置后无法获取excel中的图片 $worksheet = $spreadsheet - > getActiveSheet(); // $worksheet = $spreadsheet->getSheetByName('testcase'); // $rawCasedata = $worksheet->toArray(); $highestRow = $worksheet - > getHighestRow(); // 取得总行数 $highestColumn = $worksheet - > getHighestColumn(); // 取得总列数 $highestColumnIndex = Coordinate::columnIndexFromString($highestColumn); // 取得总列数 $excelData = []; for ($row = 1; $row <= $highestRow; $row++) { for ($col = 1; $col <= $highestColumnIndex; $col++) { $excelData[$row][] = (string) $worksheet - > getCellByColumnAndRow($col, $row) - > getValue(); } } echo " "; print_r($excelData); echo " "; ?>结果：Array ( [1] => Array ( [0] => Hello World ! [1] => [2] => ) [2] => Array ( [0] => [1] => [2] => ) [3] => Array ( [0] => [1] => [2] => 你好 ! ) )读取图片读取Excel文件中的图片，支持png、gif和jpg格式图片：<?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; $fileName = './test.xlsx'; $reader = IOFactory::createReader('Xlsx'); $spreadsheet = $reader->load($fileName); $worksheet = $spreadsheet->getActiveSheet(); /* 读取excel中的图片 */ $imgpath = './'; $imgArray = array(); foreach ($worksheet->getDrawingCollection() as $drawing) { list($startColumn, $startRow) = Coordinate::coordinateFromString($drawing->getCoordinates()); print_r($startColumn); print_r($startRow); switch ($drawing->getExtension()) { case 'jpeg': $source = imagecreatefromjpeg($drawing->getPath()); $imgname = $imgpath . $drawing->getCoordinates() . '.jpg'; imagejpeg($source, $imgname); break; case 'png': $source = imagecreatefrompng($drawing->getPath()); $imgname = $imgpath . $drawing->getCoordinates() . '.png'; imagepng($source, $imgname); break; default: echo "Unsupported file type: " . $drawing->getExtension() . "\n";; } }读取超链接读取超链接：$spreadsheet = $reader->load($fileName); $worksheet = $spreadsheet->getActiveSheet(); $spreadsheet->getActiveSheet()->getCell('A1')->hasHyperlink(); // 判断是否有超链接 $url = $spreadsheet->getActiveSheet()->getCell('A1')->getHyperlink()->getUrl(); // 读取超链接下载文件在服务器上创建了excel文件后，可以将它下载到客户端。// redirect output to client browser header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header("Content-Disposition: attachment;filename=$downloadFileName"); header('Cache-Control: max-age=0'); $writer = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet); $writer->save('php://output');当然也可以下载已经生成的xlsx格式文件：$FileName = basename($FilePath); header("Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); header("Content-Disposition: attachment; filename=$FileName"); header("Content-length: " . filesize($FilePath)); header("Pragma: no-cache"); header("Expires: 0"); readfile("$FilePath");xls格式文件类型为：application/vnd.ms-excel。这里就介绍这么多了，PhpSpreadsheet的更多使用方法可参考官方文档：https://phpspreadsheet.readthedocs.io/en/latest/。版权声明：本文内容由互联网用户自发贡献，该文观点仅代表作者本人。本站仅提供信息存储空间服务，不拥有所有权，不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容， 请发送邮件至 举报，一经查实，本站将立刻删除。 原文出处：https://javaforall.cn/192523.html
{mtitle title="目录信息"/}啥是CSS选择器通用选择器 - *元素选择器 - element类名选择器 - .classID 选择器 - #id多选择器 - .class.class-2逗号组合 - .class, .class-2后代选择器 - .class .class-2相邻兄弟选择器 - .class + .class-2子选择器 - .class > .class-2兄弟选择器 - .class ~ .class-2脑残猫头鹰选择器 - + 属性选择器 - []链接伪类选择器input相关第N个孩子指定类型的第N个孩子伪类选择器伪元素0. 啥是CSS选择器CSS选择器 是用于选择 HTML 元素并为其应用样式的一种机制。选择器允许我们根据元素的 类型 、 属性 、 位置 以及其他特征来选择要样式化的元素，从而实现对网页样式的精确控制。以下是一些常见的 CSS 选择器及其用途：「元素选择器（Element Selector）」 ：选择指定类型的 HTML 元素。例如， p 选择所有段落元素。「类选择器（Class Selector）」 ：选择具有指定类名的元素。以点号（ . ）开头，后跟类名。例如， .button 选择所有类名为 button 的元素。「ID 选择器（ID Selector）」 ：选择具有指定 ID 的元素。以井号（ # ）开头，后跟 ID 名称。例如， #header 选择 ID 为 header 的元素。「后代选择器（Descendant Selector）」 ：选择指定元素的后代元素。使用 空格 分隔两个选择器，其中第一个选择器表示 父元素第二个选择器表示 后代元素例如， div p 选择所有 元素内部的所有段落 元素。「相邻兄弟选择器（Adjacent Sibling Selector）」 ：选择紧接在另一个元素后面的元素。使用加号（ + ）符号连接两个选择器，其中第一个选择器表示 第一个元素第二个选择器表示其 紧邻的兄弟元素例如， h2 + p 选择所有紧接在 元素后面的 元素。「通用选择器（Universal Selector）」 ：选择页面中的 「所有元素」 。使用星号（ * ）表示。例如， * 选择页面中的所有元素。除了这些基本的选择器外，CSS 还提供了许多其他类型的选择器，如 伪类选择器 （例如 :hover 、 :active ）、 伪元素选择器 （例如 ::before 、 ::after ）、 属性选择器 （例如 [type="text"] ）等，可以根据具体的需求选择合适的选择器来对页面元素进行样式化。{dotted startColor="#ff6c6c" endColor="#1989fa"/}1. 通用选择器 - *CSS的 通用选择器 是一个星号（ * ），它匹配文档中的 「任何元素」 。使用通用选择器，我们可以选择页面上的 「所有元素」 ，并为它们应用相同的样式。例如，如果我们想为页面上的所有元素设置相同的字体样式，我们可以使用通用选择器来实现。{callout color="#2bff00"}但要注意，过度使用通用选择器可能会导致 「性能问题」 ，因为它会匹配页面上的所有元素，包括嵌套元素，这可能会增加浏览器的渲染负担。因此，建议在必要时谨慎使用通用选择器。{/callout}{dotted startColor="#ff6c6c" endColor="#1989fa"/}2. 元素选择器 - element元素选择器 是 CSS 中最简单的选择器之一，它通过指定 HTML 元素的名称来选择元素。例如，要选择所有段落元素 ，我们可以使用以下选择器：p { /* CSS 样式 */ }这将选择页面中的所有段落元素，并应用所指定的 CSS 样式。{dotted startColor="#ff6c6c" endColor="#1989fa"/}3. 类名选择器 - .class类选择器 用于选择具有特定类名的 HTML 元素。类选择器以点号（ . ）开头，后跟类名。例如，如果我们有一个类名为 button 的按钮元素，我们可以使用类选择器来选择它：.button { /* CSS 样式 */ }这将选择页面中所有具有类名 button 的元素，并应用所指定的 CSS 样式。类选择器使我们可以为页面中的 「多个元素定义相同的样式」 ，而不必重复定义。此外，一个元素可以同时具有多个类，这样就可以轻松地将多个样式应用于同一个元素。{dotted startColor="#ff6c6c" endColor="#1989fa"/}4. ID 选择器 - #idID 选择器 是 CSS 中用于选择具有特定 ID 属性的 HTML 元素的选择器。ID 选择器以井号（ # ）开头，后跟 ID 名称。例如，如果我们有一个 ID 为 my-id的标题元素，我们可以使用 ID 选择器来选择它：#my-id { /* CSS 样式 */ }这将选择页面中具有 ID 名称为 my-id 的元素，并应用所指定的 CSS 样式。 「与类选择器不同，ID 选择器应该是唯一的」 ，因为 HTML 页面中每个元素的 ID 必须是唯一的。ID 选择器通常用于 「为特定元素应用唯一的样式」 。{dotted startColor="#ff6c6c" endColor="#1989fa"/}5. 多选择器 - .class.class-2.class.class-2 表示连接两个或更多类或ID来选择具有所有指定类/ID的元素：这段CSS代码的意思是选择具有 .my-class 和 .special 两个类的元素，并设置对应的背景色。{dotted startColor="#ff6c6c" endColor="#1989fa"/}6. 逗号组合 - .class, .class-2使用 逗号分隔 多个选择器声明。这样可以轻松地 「将相同的样式应用于多个选择器声明」 ;{dotted startColor="#ff6c6c" endColor="#1989fa"/}7. 后代选择器 - .class .class-2后代选择器 用于 「选择特定元素的后代元素」 。后代选择器使用 「空格分隔两个选择器」 ，其中第一个选择器表示 父元素第二个选择器表示 后代元素例如，如果我们想选择所有 元素内部的所有段落 ，我们可以使用后代选择器：div p { /* CSS 样式 */ }这将选择所有在 元素内部的 元素，并应用所指定的 CSS 样式。后代选择器允许我们更精确地定位特定元素，而不受页面结构的限制。{dotted startColor="#ff6c6c" endColor="#1989fa"/}8. 相邻兄弟选择器 - .class + .class-2相邻兄弟选择器 它用于选择 「紧接」 在另一个元素后面的元素。相邻兄弟选择器使用加号（ + ）符号来连接两个选择器，其中第一个选择器表示 第一个元素第二个选择器表示 紧跟在其后的兄弟元素例如，如果我们想选择所有紧跟在 元素后面的段落 元素，我们可以使用相邻兄弟选择器：h2 + p { /* CSS 样式 */ }这将选择紧接在 元素后面的 元素，并应用所指定的 CSS 样式。相邻兄弟选择器允许我们 「根据元素之间的特定关系来选择元素」，从而使得样式的应用更加精确和有针对性。{dotted startColor="#ff6c6c" endColor="#1989fa"/}9. 子选择器 - .class > .class-2子选择器 用于选择特定元素的 「直接子元素」 。子选择器使用大于号（ > ）符号来连接两个选择器，其中第一个选择器表示 父元素第二个选择器表示其 直接子元素例如，如果我们想选择所有 元素下的直接子元素 ，我们可以使用子选择器：ul > li { /* CSS 样式 */ }这将选择所有直接位于 元素内部的 元素，并应用所指定的 CSS 样式。子选择器 「只会选择指定元素的直接子元素，而不会选择嵌套在更深层次的子元素」 。这使得子选择器非常有用，因为它可以帮助我们更精确地定位特定层次结构的元素，并应用相应的样式。{dotted startColor="#ff6c6c" endColor="#1989fa"/}10. 兄弟选择器 - .class ~ .class-2兄弟选择器 用于 「选择与指定元素具有相同父元素且位于其后面的所有兄弟元素」 。一般兄弟选择器使用波浪号（ ~ ）符号来连接两个选择器，其中第一个选择器表示 基准元素第二个选择器表示 它的兄弟元素例如，如果我们想选择所有与 元素具有相同父元素且位于其后面的 元素，我们可以使用一般兄弟选择器：h2 ~ p { /* CSS 样式 */ }这将选择所有与 元素具有相同父元素且位于其后面的 元素，并应用所指定的 CSS 样式。{callout color="#2bff00"} 兄弟选择器 允许我们选择与指定元素具有相同父元素且位于其后面的所有兄弟元素，而不仅仅是直接的兄弟元素。{/callout}{dotted startColor="#ff6c6c" endColor="#1989fa"/}11. 脑残猫头鹰选择器 - + 脑残猫头鹰选择器 用来描述一种特定的 CSS 模式。它涉及选择多个元素并为它们应用共享样式，从而在网页的各个组件或部分中创建一致的视觉设计。术语 脑残 指的是通用选择器（ * ）与相邻兄弟选择器（ + ）结合使用的情况。 猫头鹰 可能指的是 CSS 选择器的形态类似于猫头鹰的头部和眼睛。以下是“脑残猫头鹰”选择器模式的示例：* + * { margin-top: 1.5em; }这个 CSS 规则选择了在另一个元素之后出现的任何元素（除了容器中的第一个元素），并在顶部应用了一定的边距，有效地使元素均匀地间隔开来。这种模式特别适用于在为 「容器内的元素添加间距，但第一个元素没有前置兄弟元素」 。当然，我们也可以使用 :not(:first-child) 来达到相同的效果。{dotted startColor="#ff6c6c" endColor="#1989fa"/}12. 属性选择器属性选择器 它允许我们根据元素的属性及其属性值来选择元素，并为其应用样式。属性选择器使用方括号（ []）表示，内部包含属性名和可选的属性值。存在性属性选择器选择具有指定属性的元素，不论其属性值是什么。[data-text] { /* CSS 样式 */ }选择所有具有 data-text 属性的元素，并为其应用样式。这个选择器 「不关心属性的值是什么，只要有这个属性就会被选中」值属性选择器选择具有指定属性和特定值的元素。[data-text="hello"] { /* CSS 样式 */ }选择所有 data-text 属性的值为 hello 的元素，并为其应用样式。{callout color="#2bff00"}只有属性值为 hello 的元素才会被选中。{/callout}部分值属性选择器选择具有指定属性，并且属性值包含特定值的元素。[title*="saur"] { /* CSS 样式 */ }选择所有 title 属性值中包含 saur子字符串 的元素，并为其应用样式。{callout color="#2bff00"}只要属性值包含 saur ，无论是在开头、中间还是结尾，都会被选中。{/callout}起始值属性选择器选择具有指定属性，且属性值以特定值开头的元素。a[href^="https://"] { /* CSS 样式 */ }选择所有 href 属性值以 https:// 开头的 元素，并为其应用样式。{callout color="#2bff00"}只有链接以 https:// 开头的 元素才会被选中。{/callout}结束值属性选择器选择具有指定属性，且属性值以特定值结尾的元素。[src$=".png"] { /* CSS 样式 */ }选择所有 src 属性值以 .png 结尾的元素，并为其应用样式。{callout color="#2bff00"}只有图片路径以 .png 结尾的元素才会被选中。{/callout}包含值属性选择器选择具有指定属性，且属性值包含特定值的元素，且值之间以空格分隔。[class~="active"] { /* CSS 样式 */ }选择所有具有包含 active 的类名的元素，并为其应用样式。{callout color="#2bff00"}这个选择器将选中所有具有 active 类的元素，无论它们的类名是 active 还是 some-active-class 等。{/callout}{dotted startColor="#ff6c6c" endColor="#1989fa"/}13. link伪类选择器以下四个伪类用于选择各种状态下的 link 等元素。这四个伪类通常与链接一起使用，但是 :active 对于 button 也很有用，而 :hover 可以用于所有类型的元素：:link 选择未访问的 link 。它允许我们为用户尚未点击的超链接设置样式。:visited 选择用户已经访问过的 link 。这个伪类允许我们为以前点击过的超链接应用样式。:hover 当用户的指针（例如鼠标光标）悬停在元素（通常是链接）上时，选择这些元素。:active 选择在它们被激活时的元素（通常是链接或按钮），例如当用户点击它们时。{dotted startColor="#ff6c6c" endColor="#1989fa"/}14. input相关在前端项目中， input 有很大的用处。我们来看 MDN 关于 input 的介绍。所以，和 input 相关的选择器也很多。{callout color="#2bff00"}元素聚焦 - :focus{/callout} :focus 伪类选择器表示获得焦点的元素（如表单输入）。当用户点击或轻触一个元素或使用键盘的 Tab 键选择它时，它会被触发。元素选中 - :checked{/callout}:checked 伪类选择器用于选择当前被选中/勾选的 单选按钮 、 复选框 或选择元素中的选项。input[type='checkbox'] { all: unset; -webkit-appearance: none; appearance: none; margin: 0; } input[type='checkbox']:checked + label { background: mediumseagreen; } input[type='checkbox']:focus + label { box-shadow: 0 0 0 2px yellow; }在上面的示例中，我使用 appearance: none 来移除复选框输入框的默认样式{callout color="#2bff00"}元素禁用 - :disabled{/callout}:disabled 伪类匹配 「禁用」 的表单元素，例如 button 或 input {callout color="#2bff00"}元素可用 - :enabled{/callout}:enabled 伪类匹配可以交互并接收输入的表单元素{callout color="#2bff00"}元素验证 - :valid{/callout} :valid 伪类用于定位具有内容符合其属性规定要求（如 pattern 、 type 等）的输入元素：{callout color="#2bff00"}元素验证- :invalid{/callout} :invalid 伪类用于定位内容不符合要求的输入元素：{callout color="#2bff00"}元素必填 - :required{/callout} :required 伪类用于定位具有 required 属性的输入元素，表明在提交表单之前必须填写它们：{callout color="#2bff00"}元素可选 - :optional{/callout} :optional 伪类用于定位没有 required 属性的输入元素，表示它们不是必填项：{dotted startColor="#ff6c6c" endColor="#1989fa"/}15. 第N个孩子{callout color="#2bff00"}第一个孩子 - :first-child{/callout}:first-child 伪类用于定位其 「父元素内的第一个子元素」 ：{callout color="#2bff00"}最后一个孩子 - last-child{/callout}:last-child 伪类用于定位其父元素内的最后一个子元素：{callout color="#2bff00"}第N个孩子 - :nth-child{/callout}:nth-child 伪类根据元素在其父元素中的位置进行选择，允许进行各种各样的选择。:nth-child 还可以让我们按模式选择元素：:nth-child(odd) 或 :nth-child(2n+1) 选择 「每个奇数位置」 的子元素:nth-child(even) 或 :nth-child(2n) 选择 「每个偶数位置」 的子元素公式中的 n 就像一个计数器，让我们可以在重复循环中选择元素。{callout color="#2bff00"}第N个孩子(倒数) - :nth-last-child{/callout}:nth-last-child 伪类类似于 :nth-child ，但它是从 「最后一个子元素开始向后计数」 的{callout color="#2bff00"}元素的唯一子元素 - :only-child{/callout}如果某个元素是其父元素的 「唯一子元素」 ，则 :only-child 伪类将其作为目标元素：{dotted startColor="#ff6c6c" endColor="#1989fa"/}16. 指定类型的第N个孩子{callout color="#2bff00"}类型的第一个元素 - :first-of-type{/callout}:first-of-type 伪类的目标是其父级中 「该类型的第一个元素」 ：{callout color="#2bff00"}类型的最后一个元素 - :last-of-type{/callout}:last-of-type 伪类的目标是其父级中 「该类型的最后一个元素」 {callout color="#2bff00"}类型的第N个元素 - :nth-of-type{/callout}:nth-of-type 伪类根据元素的类型和兄弟元素中的位置来匹配元素{callout color="#2bff00"}类型的第N个元素(向后计数) - :nth-last-of-type{/callout}:nth-last-of-type 伪类根据元素的类型和兄弟元素中的位置来匹配元素，但从末尾开始计数{callout color="#2bff00"}其类型的唯一元素 - :only-of-type{/callout}:only-of-type 伪类的目标元素是其兄弟元素中该类型的唯一元素：{dotted startColor="#ff6c6c" endColor="#1989fa"/}17. 伪类选择器{callout color="#2bff00"}目标元素选择器- .target{/callout}:target 伪类选择具有与 URL 片段匹配的 ID 属性的元素（例如： https://example.com/#fragment ）。 :target 经常用于为直接链接到的页面部分设置样式，通常 「与页面内链接一起使用」 。{callout color="#2bff00"}否定伪类 - .not(){/callout}:not()函数伪类 允许我们定位与指定选择器或条件不匹配的元素。它本质上是一个 「过滤器」 ：{callout color="#2bff00"}父选择器 - :has(){/callout}:has()函数伪类 允许在元素包含某个元素或另一个选择器时对其进行样式设置：{callout color="#2bff00"}其他伪类{/callout}:root ：选择文档中最高级别的父元素，通常是 HTML 文档中的 元素。用于定义 CSS 变量，这些变量将对页面内所有元素可用。:is() ：匹配可以是多个选择器之一的元素，使得长选择器列表更简洁易读。例如， :is(h1, h2, h3) 可以匹配这三个标题元素中的任何一个。:where() ：类似于 :is() ，但允许根据条件选择元素，而不影响选择器的特异性。:default ：匹配设置为其默认选择状态的 UI 元素（例如单选按钮或复选框）。:empty ：选择没有子元素的元素（包括文本节点）。:fullscreen ：选择当前以全屏模式显示的元素。:in-range ：匹配值在指定范围内的表单元素（使用 min 和 max 属性）。 :out-of-range ：匹配值在指定范围之外的表单元素。:indeterminate ：选择状态不确定的表单元素，例如既不选中也不取消选中的复选框（通常在树形视图结构中看到）。:read-only ：匹配由于 readonly 属性而无法由用户编辑的表单元素。:read-write ：选择用户可以编辑的表单元素，意味着它们不是只读的。:lang() ：根据其语言属性匹配元素。例如， :lang(en) 选择用英语定义的元素。{dotted startColor="#ff6c6c" endColor="#1989fa"/}18. 伪元素{callout color="#2bff00"}第一个子伪元素 - ::before{/callout}::before 伪元素用于在元素内容之前插入内容。它可用于添加装饰内容、图标或其他不需要在实际 DOM 中的元素：{callout color="#2bff00"}最后一个子伪元素 - ::after{/callout}::after 伪元素与 ::before 类似，用于在元素内容之后插入内容：{callout color="#2bff00"}首字母伪元素- ::first-letter{/callout}::first-letter 伪元素用于设置块级元素的 「第一个字母的样式」 ，允许使用首字下沉等设计元素：{callout color="#2bff00"}首行伪元素 - ::first-line{/callout}::first-line 伪元素用于设置块级元素第一行的样式。这允许印刷效果可以根据包含元素的大小和字体大小动态调整：{callout color="#2bff00"}文本输入占位符 - ::placeholder{/callout}::placeholder 伪元素用于设置表单字段（如 和 ）的占位符文本的样式：{callout color="#2bff00"}样式突出显示框 - ::selection{/callout}::selection 伪元素用于设置元素中已突出显示或由用户选择的部分的样式。例如，当用户单击并拖动以选择文本时， ::selection 伪元素可用于修改该选择的背景颜色、文本颜色和其他样式：{callout color="#2bff00"}列表标记伪元素 - ::marker{/callout}::marker 伪元素用于设置列表项中标记框的样式，列表项通常包含项目符号（对于无序列表）或数字/字母（对于有序列表）。在引入 ::marker 伪元素之前，自定义这些标记通常需要解决方法，但这个伪元素给了我们更多的控制权：{dotted startColor="#ff6c6c" endColor="#1989fa"/}原文出处:https://mp.weixin.qq.com/s/8Zhw-obLYrsPPXqkaqW5wA