Playwright 的使用

news2024/11/18 3:34:01

Playwright 的特点

支持当前所有主流浏览器,包括 Chrome 和 Edge (基于 Chromiuns), Firefox , Safari

支持移动端页面测试,使用设备模拟技术,可以让我们在移动Web 浏览器中测试响应式的 Web 应用程序

支持所有浏览器的无头模式和非无头模式的测试

安装和配置过程简单,安装过程中会自动安装对应的浏览器和驱动,不需要额外配置 WebDriber

提供和自动等待相关的API , 在页面加载时会自动等待对应的节点加载,大大减小了API 编写的复杂的

安装

1. 首先确保 python 版本大于 3.7

pip install playwright

这时,playwright 会安装 Chromium , Firefox 和 WebKit 浏览器并配置一些驱动

基本使用

Playwright 支持两种模式,同步 和 异步 根据需要选择不同的模式

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    for browser_type in [p.chromium, p.firefox, p.webkit]:
        browser = browser_type.launch(headless=False)
        page = browser.new_page()
        page.goto('https://www.baidu.com')
        page.screenshot(path=f'screenshot-{browser_type.name}.png')
        print(page.title())
        browser.close()

代码执行后会依次使用三种浏览器打开百度首页

这里我们首先导入并直接调用了 sync_playwright 方法, 该方法的返回值是一个 PlaywrightContextManger 对象,可以理解为一个浏览器上下文管理器,我们将其配置为 p 变量。然后依次调用 p 的 chromium, firefox 和 webkit 属性创建了 Chromium, Firefox 以及 webkit 浏览器实例。接着用一个 for 循环依次执行了这 3 个浏览器实例的 launch 方法, 同时设置了 headless 参数为 False

如果不把 headless 设置为 False ,就会默认以无头模式打开浏览器,我们将看不到任何窗口

在 for 循环中, launch 方法返回一个 Browser 对象, 我们将其赋值为 browser 变量。 然后调用 browseer 的 new_page 方法新建了一个选项卡, 返回值是一个 Page  对象, 将其赋值为 page , ,之后调用 page 的一系列API 完成了各种自动化操作,调用 goto 方法加载某个页面,这里我们访问的是百度首页, 调用 screenshot 方法获取页面截图, 往其里面传入的文件名称是截图自动保存后的图片名称, 这里的名称中我们加入了 browser_type 的 name 属性, 代表浏览器的类型,于是 3 次循环中 screenshot 方法的结果分别是 chromium , firefox , 和 webkit 另外,还调用了 title 方法,该方法会返回页面的标题, 即 HTML 源代码中 title 节点的文字, 也就是选项卡上的文字,并将返回的页面标题打印到控制台, 最后,调用 browser 的 close 方法关闭整个浏览器,代码结束

上面演示的是同步模式,Playwright 还支持异步模式

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        for browser_type in [p.chromium, p.firefox, p.webkit]:
            browser = await browser_type.launch()
            page = await browser.new_page()
            await page.goto('https://www.baidu.com')
            await page.screenshot(path=f'screenshot-{browser_type.name}.png')
            print(await page.title())
            await browser.close()

asyncio.run(main())

可以看到,写法和同步模式基本一样,只不过这里导入的是 async_playwright 方法, 不再是 sync_playwright 方法, 以及写法上添加了 async / await 关键字, 最后运行效果和同步模式一样

另外可以注意到, 这两个例子中使用了 with as 语句,with 用于管理上下文对象,可以返回一个上下文管理器,寄一个 PlaywrightContextManger 对象,无论代码运行期间是否抛出异常,该对象都能帮助我们自动分配并且释放 Playwright 的资源

这里并没有传入 headless 为 True 所以没有显示浏览器页面,也就是用的是无头模式

代码生成

Playwright 还有一个强大的功能,是可以录制我们在浏览器中的操作并自动生成代码,有了这个功能,我们甚至一行代码都不用写。这个功能可以通过 playwright 命令行调用 codegen 实现,先来看看 codegen 有什么参数

在终端输入命令如下;

playwright codegen --help

结果类似如果下:

Usage: playwright codegen [options] [url]

open page and generate code for user actions

Options:
  -o, --output <file name>             saves the generated script to a file
  --target <language>                  language to generate, one of javascript, playwright-test, python, python-async,
                                       python-pytest, csharp, csharp-mstest, csharp-nunit, java, java-junit (default:
                                       "python")
  --save-trace <filename>              record a trace for the session and save it to a file
  --test-id-attribute <attributeName>  use the specified attribute to generate data test ID selectors
  -b, --browser <browserType>          browser to use, one of cr, chromium, ff, firefox, wk, webkit (default:
                                       "chromium")
  --block-service-workers              block service workers
  --channel <channel>                  Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc
  --color-scheme <scheme>              emulate preferred color scheme, "light" or "dark"
  --device <deviceName>                emulate device, for example  "iPhone 11"
  --geolocation <coordinates>          specify geolocation coordinates, for example "37.819722,-122.478611"
  --ignore-https-errors                ignore https errors
  --load-storage <filename>            load context storage state from the file, previously saved with --save-storage
  --lang <language>                    specify language / locale, for example "en-GB"
  --proxy-server <proxy>               specify proxy server, for example "http://myproxy:3128" or
                                       "socks5://myproxy:8080"
  --proxy-bypass <bypass>              comma-separated domains to bypass proxy, for example
                                       ".com,chromium.org,.domain.com"
  --save-har <filename>                save HAR file with all network activity at the end
  --save-har-glob <glob pattern>       filter entries in the HAR by matching url against this glob pattern
  --save-storage <filename>            save context storage state at the end, for later use with --load-storage
  --timezone <time zone>               time zone to emulate, for example "Europe/Rome"
  --timeout <timeout>                  timeout for Playwright actions in milliseconds, no timeout by default
  --user-agent <ua string>             specify user agent string
  --viewport-size <size>               specify browser viewport size in pixels, for example "1280, 720"
  -h, --help                           display help for command

Examples:

  $ codegen
  $ codegen --target=python
  $ codegen -b webkit https://example.com
 

说明:

1. 这里是命令行,所以要在终端 (cmd) 输入

2. 这里输入命令是配置了环境变量的, 如果没有配置环境变量则需要加上全路径

例如我的在: 

C:\Users\86151\AppData\Roaming\Python\Python311\Scripts

里面有

主要是找到自己的 python 环境, 然后找到 Scripts 文件夹

可以看到结果中有几个选项, -o 代表输出的代码文件名称, -target 代表使用的语言, 默认是 python , 代表会生成同步模式的代码, 如果传入 pythhon-async 则会生成异步模式的代码, -b 代表使用的浏览器, 默认是 chromium  还有很多其他设置, 例如 -device 可以模拟使用手机浏览器(如 iPthon 11), -lang 代表设置浏览器的语言, -timeout 可以设置页面加载超时时间

playwright codegen -o script.py -b firefox

运行代码后会弹出一个 Firefox 浏览器, 同时右侧输出一个脚本框,实时显示当前操作对应的代码,我们可以在浏览器随意操作,例如打开百度,点击搜索框输入 nba, 在点击搜索按钮

这里看到随着我们的操作,右侧也生成了对应的代码,操作完成之后,关闭浏览器,playwright 会生成一 script.py 的文件, 前面我们自定义的,这个文件的位置,取决于你执行命令时的位置

这个文件我们也是可以执行的,执行之后,它会重复我们刚刚的操作。这里哦们可以发现,自动生成的代码和我们写的是有些区别的,例如:这里的 new_page 方法并不是直接通过 browser 调用的,而是通过 context ,这个 context 又是由 browser 调用 new_context 方法生成的,这个context 变量其实是一个 BrowserContext 对象, 这是一个类似隐身模式的独立上下文环境, 其运行资源是单独隔离的, 在一些自动化测试过程中, 我们可以为每个测试用例单独创建一个 BrowserContext 对象, 这样能够保证各个测试用例互不干扰,就提API参考:

https://playwright.dev/python/docs/api/class-browsercontext

支持移动端浏览器

模拟移动端浏览器

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    iphone_12_pro_max = p.devices['iPhone 12 Pro Max']
    browser = p.webkit.launch(headless=False)
    context = browser.new_context(**iphone_12_pro_max, locale='zh-CN')
    page = context.new_page()
    page.goto('https://www.whatismybrowser.com/')
    page.wait_for_load_state(state='networkidle')
    page.screenshot(path='browser-iphone.png')
    browser.close()

这里我们先用 PlaywrightContextManger 对象的 devices 属性指定了一台移动设备,传入的参数是移动设备的型号, 例如 iPhone 12 Pro Max ,当然也可以传入其他内容,例如 iPhone 8 ....

前面我们已经了解了 BrowserContext 对象, 它可以用来模拟移动端浏览器, 初始化一些移动设备的信息,语言,权限,位置等内容, 这里我们就创建了一个移动端 BrowserContext 对象, 最后把返回的 BrowserContext 对象赋值给了 context 变量

接着我们调用 context 的 new_page 方法创建了一个新的选项卡, 然后跳转到一个用于获取浏览器信息的网站, 调用 wait_for_load_state 方法等待页面的某个状态完成, 这里我们传入的 state 是 networkidle , 也就是网络空闲状态, 因为在页面初始化和数据加载过程中,肯定有网络请求伴随产生,所以加载过程肯定不算 networkle 状态,意味着这里传入 networkidle 可以标识当前页面初始化和数据加载完成的状态, 加载完成之后, 我们调用 screenshot 方法获取了当前页面的截图,最后关闭了浏览器

选择器

文本选择器

文本选择器支持用 text= 这样的语法进行筛选

page.click('text-log in ')

这里代表选择并点击文本的内容是 Log in  的节点

CSS 选择器

例如根据 id 或 class 筛选

page.click("button")

page.click("#nav-bar  .contact-us-item")

根据节点属性筛选

page.click("[data-test=login=button]")

page.click("[aria-label=Sign in ]")

CSS 选择器 + 文本值

可以结合CSS 选择器结合文本值的方式进行筛选,比较常见的方法是 has-text 和 text ,前者代表节点中包含指定的字符串, 后者代表节点中的文本值和指定的字符串完全匹配

page.click("article:has-text('Playwright')")

page.click("#nav-bar : text(' Contact us')")

第一行代码就是选择选择文本值中包含 Playwrght 字符串的 article 节点, 第二行代码是选择 id 为 nav-bar 的节点中文本值为 Contact us 的节点

CSS 选择器 +节点关系

CSS 选择器还可以结合节点关系来筛选节点,例如使用 has 指定另外一个选择器,

page.click(".item-description:has(.item-promo-banner)")

这里选择的就是class 为 item-description 的节点, 且该节点还要包含 class 为 item-promo-banner 的子节点

另外还可以结合一些相对位置关系,例如使用 right-of 指定位于某个节点右侧的节点

page.click("input:right:right-of(:text('Username'))")

这里选择的就是一个 imput 节点, 并且该节点要求位于文本值为 Username 的节点的右侧

XPath

当然 XPath 也是支持的, 不过 xpath 这个关键字需要我们自行指定

page.click("xpath=//button")

这里开头指定 xpath= 字符串 , 代表这个字符串是一个 XPath 表达式

更多关于选择器的用法和实践可以关注官网: 

https://playwright.dev/python/docs/api/class-page

常用操作方法

例如: click(点击), fill(输入)等

文档地址:

https://playwright.dev/python/docs/api/class-page

事件监听

page 对象提供一个 on 方法, 用来监听页面中发生的各个事件, 例如 close, console, load, request, response 等

这里我们监听 response 事件 , 在每次网络请求得到响应的时候会出发这个事件,我们可以设置回调方法来获取响应中的全部信息

from playwright.sync_api import sync_playwright

def on_response(response):
    print(f"Statue {response.status}: {response.url}")

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.on('response', on_response)
    page.goto('https://spa6.scrape.center/')
    page.wait_for_load_state('networkidle')
    browser.close()

Statue 200: https://spa6.scrape.center/
Statue 200: https://spa6.scrape.center/js/chunk-19c920f8.c3a1129d.js
Statue 200: https://spa6.scrape.center/css/chunk-19c920f8.2a6496e0.css
Statue 200: https://spa6.scrape.center/css/chunk-2f73b8f3.5b462e16.css
Statue 200: https://spa6.scrape.center/js/chunk-vendors.77daf991.js
Statue 200: https://spa6.scrape.center/css/app.ea9d802a.css
Statue 200: https://spa6.scrape.center/js/chunk-4dec7ef0.e4c2b130.js
Statue 200: https://spa6.scrape.center/js/app.5ef0d454.js
Statue 200: https://spa6.scrape.center/js/chunk-2f73b8f3.8f2fc3cd.js

这里省略了一部分输出内容

我们在创建 page 对象后, 就开始监听 response 事件, 同时将回调函数方法设置为 on_response

on_response 接收一个参数,然后输出响应中的状态码和链接

可以发现输出结果其实正好对应浏览器 Network 面板中的所有请求和响应

这个网站真实的数据都是 Ajax 加载的, 同时 Ajax 请求中还带有加密参数, 不好轻易获取, 但有了 on_response 方法, 如果我们想截获 Ajax 请求,岂不是很容易

改写一下这里的判定条件, 输出对应的 JSON 结果

from playwright.sync_api import sync_playwright

# 通过事件监听来获取 Ajax 加密的JSON 内容
def on_response(response):
    if '/api/movie/' in response.url and response.status ==200:
        print(response.json())


with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.on('response', on_response)
    page.goto('https://spa6.scrape.center/')
    page.wait_for_load_state('networkidle')

{'count': 103, 'results': [{'id': 1, 'name': '霸王别姬', 'alias': 'Farewell My Concubine', 'cover': 'https://p0.meituan.net/movie/ce4da3e03e655b5b88ed31b5cd7896cf62472.jpg@464w_644h_1e_1c', 'categories': ['剧情', '爱情'], 'published_at': '1993-07-26', 'minute': 171, 'score': 9.5, 'regions': ['中国内地', '中国香港']}, {'id': 2, 'name': '这个杀手不太冷', 'alias': 'Léon', 'cover':  'https://p0.meituan.net/movie/27b76fe6cf3903f3d74963f70786001e1438406.jpg@464w_644h_1e_1c', 'categories': ['动画', '歌舞', '冒险'], 'published_at': '1995-07-15', 'minute': 89, 'score': 9.0, 'regions': ['美国']}]}
 

这里省略了部分内容

可以看出来,相对来说简单了很多

获取页面源代码

获取页面源代码其实很简单,直接调用 Page 对象的 content 方法即可

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto('https://spa6.scrape.center/')
    page.wait_for_load_state('networkidle')
    html = page.content()
    print(html)
    browser.close()

运行之后就是网页源代码了, 借助工具就可以分析提取想要的信息了

页面点击

页面点击前面用过了, 就是 click 方法

page.click(selector, **kwargs)

可以看到必须传入的参数是 selector , 其他参数都是可选的。 selector 代表选择器,用来匹配想要点击的节点,如果有多个节点和传入的选择器匹配, 那么只使用第一个节点

其他一些比较重要的参数如下

click_count :点击次数,默认为1

timeout: 等待找到要点击得劲节点的超时时间(单位为秒),默认是 30

psition: 需要传入一个字典,带有 x 属性和 y 属性, 代表点击位置相对节点左上角的偏移量

force : 即使按钮设置了不可点击, 也要强制点击, 默认为 False 

click 内部执行逻辑如下

找到与 selector 匹配到的节点, 如果没有找到,就一直等待直到超时,超时时间由 timeout 参数设置

检查匹配到的节点是否存在可操作性,等待检查结果,如果某个按钮设置了不可点击,就等该按钮变成可点击的时候再去点击,除非通过 force 参数设置跳过了可操作性检查步骤,才会强制点击

如果需要,就滚动一下页面,使需要点击的节点呈现出来

调用 Page 对选哪个的 mouse 方法, 点击节点的中心位置,如果指定了 positon 参数, 就点击参数指定的位置

具体参数设置可以参考官方文档:

Page | Playwright

文本输入

文本输入对应的方法是 fill 其API 定义如下

page.fill(selector, value, **kwargs)

这个方法传入两个必要参数,第一个也是 selector ,依然代表选择器,第二个是 value,代表输入的文本内容,还可以通过 timeout 查找对应节点的最长等待时间

获取节点属性

除了操作本身,我们还可以获取节点的属性,方法是 get_attribute 其 API 定义如下

page.get_attribute(selector, name , **kwargs)

这个方法接收两个参数,第一个是 selector , 代表选择器。第二个是 name ,代表要获取的属性名称,还可以通过 timeout 查找对应节点的最长等待时间

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto('https://spa6.scrape.center/')
    page.wait_for_load_state('networkidle')
    href = page.get_attribute('a.name', 'href')
    print(href)
    browser.close()

/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx

可以看到获取的对应节点的 href 属性,但只有一条结果, 这是因为如果传入的选择器匹配到了多条件结果,就只会用第一个

获取多个节点

使用 query_selector_all 方法可以获取所有节点,它会返回节点列表,通过遍历得到其中的单个节点后,可以接着调用上面介绍的针对单个节点的方法完成一些操作和获取属性

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto('https://spa6.scrape.center/')
    page.wait_for_load_state('networkidle')
    elements = page.query_selector_all('a.name')
    for element in elements:
        print(element.get_attribute('href'))
        print(element.text_content())
    browser.close()

/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
霸王别姬 - Farewell My Concubine
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIy
这个杀手不太冷 - Léon

省略部分输出内容

这里我们通过 query_selector_all 方法获取了所有匹配到的节点, 每个节点各对应一个 ElementHandle 对象, 可以调用 ElementHandle 对象的 get_attribute 方法获取节点属性, 也可以通过 text_content 方法获取节点文本

获取单个节点

获取单个节点的特定方法 query_selector, 如果匹配到了多个节点,那么它只返回第一个

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto('https://spa6.scrape.center/')
    page.wait_for_load_state('networkidle')
    element = page.query_selector('a.name')
    print(element.get_attribute('href'))
    print(element.text_content())
    browser.close()

/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
霸王别姬 - Farewell My Concubine

可以看到只输出了第一个节点的信息

网络劫持

在介绍一个方法 route 利用这个方法可以实现网络劫持和修改操作。如修改 request 的属性,修改响应结果等

from playwright.sync_api import sync_playwright
import re
import time


# 设置图片不加载,劫持请求数据
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    def cancel_request(route, request):
        route.abort()

    page.route(re.compile(r"(\.png)|(\.jpg)"), cancel_request)
    page.goto("https://spa6.scrape.center/")
    page.wait_for_load_state('networkidle')
    page.screenshot(path='no_picture.png')
    time.sleep(3)
    browser.close()

这里我们调用了 route 方法, 第一个参数通过正则表达式传入了 URL 路径,这里的 (\.png) |(\.jpg)代表所有包含 .png 或 .jpg 的链接,遇到这样的请求,会回调 cancel_request 方法做处理。 cancel_request  方法接收两个参数,一个是 route 代表一个 CallableRoute 对象, 另一个是 request , 代表 Request 对象, 这里我们直接调用 CallableRoute 对象的 abort 方法,取消了这次请求,最终导致所有的图片都取消加载

这里解释这么做的一个作用: 图片资源都是二进制文件, 我们在爬取的过程中可能并不想关心具体的二进制内容,而只关心图片的 URL 是什么,所以浏览器是否把图片加载出来就不重要了,如此设置可以提高整个页面的加载速度,提高爬取效率

另外利用这个功能还可以对一些响应内容进行修改, 例如直接将响应结果修改为自定的文本

这里首先顶一个 HTML 文本文件, 命名为 custom__response.html

<!DOCTYPE html>
<html>
  <head>
    <title>Hack Response</title>
  </head>
  <body>
    <h1>Hack Response</h1>
  </body>
</html>

代码如下

from playwright.sync_api import sync_playwright
import time


# 改变响应数据
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    def modify_response(route, request):
        route.fulfill(path='./custom_response.html')

    page.route('/', modify_response)
    page.goto('https://spa6.scrape.center/')
    time.sleep(3)
    browser.close()

这里按照教程所说应该是要展示出 上面自己编写的 HTML 内容,但是实际效果并没有。

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

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

相关文章

做一个能和你互动玩耍的智能机器人之四--固件

在openbot的firmware目录下我们能够找到arduino的固件源码和相关的文档。 openbot的controller目录下&#xff0c;是控制器的代码目录&#xff0c;用来控制机器人做一些动作。未来的目标是加入大模型&#xff0c;使其能够理解人的语言和动作来控制。 固件代码&#xff0c;支持…

数据结构 -- 算法的时间复杂度和空间复杂度

数据结构 -- 算法的时间复杂度和空间复杂度 1.算法效率1.1 如何衡量一个算法的好坏1.2 算法的复杂度 2.时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例 3.空间复杂度4. 常见复杂度对比 1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法…

数据库实验:SQL Server基本表单表查询

一、实验目的&#xff1a; 1、掌握使用SQL语法实现单表查询 二、实验内容&#xff1a; 1. 查询订购日期为2001年5月22日的订单情况。&#xff08;Orders&#xff09;&#xff08;时间日期的表达方式为 dOrderDate ‘2001-5-22’&#xff0c;类似字符串&#xff0c;使用单引号…

Linux---git工具

目录 初步了解 基本原理 基本用法 安装git 拉取远端仓库 提交三板斧 1、添加到缓存区 2、提交到本地仓库 3、提交到远端 其他指令补充 多人协作管理 windows用户提交文件 Linux用户提交文件 初步了解 在Linux中&#xff0c;git是一个指令&#xff0c;可以帮助我们做…

Python爬虫-中国汽车市场月销量数据

前言 本文是该专栏的第34篇,后面会持续分享python爬虫干货知识,记得关注。 在本文中,笔者将通过某汽车平台,来采集“中国汽车市场”的月销量数据。 具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。废话不多说,下面跟着笔者直接往下看正文详细内容。(附…

【原创】使用keepalived虚拟IP(VIP)实现MySQL的高可用故障转移

1. 背景 A、B服务器均部署有MySQL数据库&#xff0c;且互为主主。此处为A、B服务器部署MySQL数据库实现高可用的部署&#xff0c;当其中一台MySQL宕机后&#xff0c;VIP可自动切换至另一台MySQL提供服务&#xff0c;实现故障的自动迁移&#xff0c;实现高可用的目的。具体流程…

微服务-MybatisPlus下

微服务-MybatisPlus下 文章目录 微服务-MybatisPlus下1 MybatisPlus扩展功能1.1 代码生成1.2 静态工具1.3 逻辑删除1.4 枚举处理器1.5 JSON处理器**1.5.1.定义实体****1.5.2.使用类型处理器** **1.6 配置加密&#xff08;选学&#xff09;**1.6.1.生成秘钥**1.6.2.修改配置****…

哪里可以查找短视频素材?6个素材查找下载渠道分享!

在短视频的风靡浪潮中&#xff0c;不少创作者纷纷投身于这一领域&#xff0c;无论是分享生活点滴还是进行商业宣传&#xff0c;高质量的短视频内容总能吸引众多观众的目光。然而&#xff0c;精良的短视频制作离不开优质的素材支持。本文将为大家介绍6个优秀的高质量短视频素材下…

ProxmoxPVE虚拟化平台--U盘挂载、硬盘直通

界面说明 ### 网络设置 ISO镜像文件 虚拟机中使用到的磁盘 挂载USB设备 这个操作比较简单&#xff0c;不涉及命令 选中需要到的虚拟机&#xff0c;然后选择&#xff1a; 添加->USB设置选择使用USB端口&#xff1a;选择对应的U盘即可 硬盘直通 通常情况下我们需要将原有…

前端Long类型精度丢失:后端处理策略

文章目录 精度丢失的具体原因解决方法1. 使用 JsonSerialize 和 ToStringSerializer2. 使用 JsonFormat 注解3. 全局配置解决方案 结论 开发商城管理系统的品牌管理界面时&#xff0c;发现一个问题&#xff0c;接口返回品牌Id和页面展示的品牌Id不一致&#xff0c;如接口返回的…

C/C++大雪纷飞代码

目录 写在前面 C语言简介 EasyX简介 大雪纷飞 运行结果 写在后面 写在前面 本期博主给大家带来了C/C实现的大雪纷飞代码&#xff0c;一起来看看吧&#xff01; 系列推荐 序号目录直达链接1爱心代码https://want595.blog.csdn.net/article/details/1363606842李峋同款跳…

Prime Land(牛客)

计算出n-1 对n-1进行质因数分解 // Problem: Prime Land // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/21094/D // Memory Limit: 524288 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org)#include<iostream> #in…

PAT1060它们是否相等

感谢松鼠爱葡萄 大佬代码太简洁了ilil #include <iostream> #include <cstring>using namespace std;string change(string a, int n) { // 找到小数点的位置&#xff0c;从0开始计数int k a.find("."); // 如果字符串中没有 "."&…

Linux 理解文件系统

查看文件信息 ls -l 每行包含7列&#xff1a; 模式硬链接数文件所有者组大小最后修改时间文件名 stat查看更多信息 硬盘抽象理解 注意&#xff1a; 一个block的大小是由格式化的时候确定的&#xff0c;并且不可以更改mke2fs的-b选项可以设定block大小为1024、2048或4096字节…

光盘文件系统 (iso9660) 格式解析

越简单的系统, 越可靠, 越不容易出问题. 光盘文件系统 (iso9660) 十分简单, 只需不到 200 行代码, 即可实现定位读取其中的文件. 参考资料: https://wiki.osdev.org/ISO_9660 相关文章: 《光盘防水嘛 ? DVDR 刻录光盘泡水实验》 https://blog.csdn.net/secext2022/article/d…

GD 32 UNIX时间戳

前言 ... UINX时间戳定义 UNIX时间戳是一种表示时间的方法&#xff0c;广泛用于计算机系统和网络协议中。它定义的时间起点是1970年1月1日午夜&#xff08;协调世界时UTC&#xff09;&#xff0c;也就是所谓的“UNIX纪元”开始的时刻。 Unix 时间戳(Unix Timestamp)定义为从U…

DjangoRF实战-2-apps-users

1、用户模块 创建一个用户模块子应用&#xff0c;用来管理用户&#xff0c;和认证和授权。 1.1根目录创建apps&#xff0c; 为了使用方便&#xff0c;还需要再pycharm中设置一下资源路径&#xff0c;就可以自动提示 1.2注册子应用 1.3添加应用根目录到环境变量path python导…

搭建cool-admin-java(前端vue)项目

为什么选择 Cool Admin&#xff1f;​ 随着技术不断地发展&#xff0c;特别是最近 Ai 相关的技术发展&#xff0c;以往的框架已经越来越不能满足现代化的开发需求。 Cool Admin 做为后来者有后发优势&#xff0c;主要特点&#xff1a; Ai 编码&#xff0c;从页面到后端代码&…

成为git砖家(5): 理解 HEAD

文章目录 1. git rev-parse 命令2. 什么是 HEAD2.1 创建分支当并未切换&#xff0c; HEAD 不变2.2 切换分支&#xff0c;HEAD 改变2.3 再次切换分支&#xff0c; HEAD 再次改变 3. detached HEAD4. HEAD 表示分支、表示 detached HEAD 有什么区别&#xff1f;区别相同点 5. HEA…