【python自动化】playwright长截图切换标签页JS注入实战

前言

当前教程使用的playwright版本为1.37.0,selenium版本为3.141.0

官方文档:https://playwright.dev/python/docs/screenshots

本教程目录如下

文章目录

    • 前言
    • playwright各类截图源码阅读
      • ElementHandle类下的截图
      • Page类下的截图
      • Locator类下的截图
    • Playwright快速使用截图功能
    • 两大框架标签页切换对比
      • selenium切换标签页
      • playwright切换标签页
      • playwright切换标签页代码封装
    • JS注入
      • selenium使用js注入
      • playwright使用js注入
    • 综合实战
      • **实战需求**
      • **难点**
      • **解决方案**
      • 图像拼接封装
    • 实战完整代码

playwright各类截图源码阅读

通过阅读源码:playwright -> sync_api -> _generated.py可知,可以在以下三种方式下进行screenshots截图。

返回值均为字节bytes

class ElementHandle(JSHandle):(官方不建议或弃用)def screenshotclass Page(SyncContextManager):(官方推荐整个页面截图)def screenshotclass Locator(SyncBase):(官方推荐元素定位截图)def screenshot

ElementHandle类下的截图

官方建议不要用此方式,请使用Locator对象进行操作,后续可能会弃用此方式。

该方法会截取页面的屏幕截图,并根据该特定元素的大小和位置进行裁剪。如果该元素被其他元素覆盖,则在截图上实际上不可见。如果该元素是可滚动容器,则截图上只会显示当前滚动的内容。该方法在进行屏幕截图之前会等待 可操作性 检查,然后将元素滚动到视图中。如果该元素从 DOM 中分离,该方法将抛出错误。

用法

element_handle.screenshot()
element_handle.screenshot(**kwargs)

参数

参数类型含义
timeoutUnion[float, None]最大等待时间,以毫秒为单位。默认为30000(30秒)。传入0以禁用超时。browser_context.set_default_timeout()page.set_default_timeout()方法更改默认值。
typeUnion[“jpeg”, “png”, None]指定截图的类型,默认为png。
pathUnion[pathlib.Path, str, None]图像保存的文件路径。截图类型将根据文件扩展名进行推断。如果path是相对路径,则相对于当前工作目录解析。如果不提供路径,则图像将不会保存到磁盘。
qualityUnion[int, None]图像的质量,介于0到100之间。不适用于png图像。
omit_backgroundUnion[bool, None]隐藏默认的白色背景,允许使用透明度进行截图。不适用于jpeg图像。默认为false
animationsUnion[“allow”, “disabled”, None]设置为"disabled"时,停止CSS动画、CSS过渡和Web动画。动画的处理方式取决于其持续时间:有限动画将快进到完成状态,因此它们会触发transitionend事件。 无限动画将取消到初始状态,然后在截图后重新播放。默认为"allow",即保持动画不变。
caretUnion[“hide”, “initial”, None]设置为"hide"时,截图将隐藏文本插入符。设置为"initial"时,文本插入符的行为不会改变。默认为"hide"
scaleUnion[“css”, “device”, None]设置为"css"时,截图上每个CSS像素将具有一个实际像素。对于高DPI设备,这将使截图保持较小的大小。使用"device"选项将使每个设备像素有一个实际像素,因此高DPI设备的截图将是两倍或更大。默认为"device"
maskUnion[List[Locator], None]指定在截图时应隐藏的定位符。被隐藏的元素将被叠加一个粉色框#FF00FF(由maskColor自定义),完全覆盖其边界框。
mask_colorUnion[str, None]指定被隐藏元素的覆盖框的颜色,以CSS颜色格式表示。默认颜色为粉色#FF00FF

Page类下的截图

用法

page.screenshot()
page.screenshot(**kwargs)

参数

参数类型含义
timeoutUnion[float, None]最大等待时间,以毫秒为单位。默认为30000(30秒)。传入0以禁用超时。browser_context.set_default_timeout()page.set_default_timeout()方法更改默认值。
typeUnion[“jpeg”, “png”, None]指定截图的类型,默认为png。
pathUnion[pathlib.Path, str, None]图像保存的文件路径。截图类型将根据文件扩展名进行推断。如果path是相对路径,则相对于当前工作目录解析。如果不提供路径,则图像将不会保存到磁盘。
qualityUnion[int, None]图像的质量,介于0到100之间。不适用于png图像。
omit_backgroundUnion[bool, None]隐藏默认的白色背景,允许使用透明度进行截图。不适用于jpeg图像。默认为false
full_pageUnion[bool, None]true时,截取完整可滚动页面的屏幕截图,而不是当前可见的视口。默认为false
clipUnion[{x: float, y: float, width: float, height: float}, None]指定结果图像的裁剪区域的对象。
animationsUnion[“allow”, “disabled”, None]设置为"disabled"时,停止CSS动画、CSS过渡和Web动画。动画的处理方式取决于其持续时间:有限动画将快进到完成状态,因此它们会触发transitionend事件。 无限动画将取消到初始状态,然后在截图后重新播放。默认为"allow",即保持动画不变。
caretUnion[“hide”, “initial”, None]设置为"hide"时,截图将隐藏文本插入符。设置为"initial"时,文本插入符的行为不会改变。默认为"hide"
scaleUnion[“css”, “device”, None]设置为"css"时,截图上每个CSS像素将具有一个实际像素。对于高DPI设备,这将使截图保持较小的大小。使用"device"选项将使每个设备像素有一个实际像素,因此高DPI设备的截图将是两倍或更大。默认为"device"
maskUnion[List[Locator], None]指定在截图时应隐藏的定位符。被隐藏的元素将被叠加一个粉色框#FF00FF(由maskColor自定义),完全覆盖其边界框。
mask_colorUnion[str, None]指定被隐藏元素的覆盖框的颜色,以CSS颜色格式表示。默认颜色为粉色#FF00FF

Locator类下的截图

该方法将截取页面的屏幕截图,并根据定位符匹配的特定元素的大小和位置进行裁剪。如果该元素被其他元素覆盖,则在截图上实际上不可见。如果该元素是可滚动容器,则截图上只会显示当前滚动的内容。

该方法会等待可操作性检查,然后将元素滚动到视图中,然后再进行截图。如果该元素已从 DOM 中移除,则该方法会抛出一个错误。

用法

page.get_by_role("link").screenshot()
page.locator("#main-content").screenshot()

参数

参数类型含义
timeoutUnion[float, None]最大等待时间,以毫秒为单位。默认为30000(30秒)。传入0以禁用超时。browser_context.set_default_timeout()page.set_default_timeout()方法更改默认值。
typeUnion[“jpeg”, “png”, None]指定截图的类型,默认为png。
pathUnion[pathlib.Path, str, None]图像保存的文件路径。截图类型将根据文件扩展名进行推断。如果path是相对路径,则相对于当前工作目录解析。如果不提供路径,则图像将不会保存到磁盘。
qualityUnion[int, None]图像的质量,介于0到100之间。不适用于png图像。
omit_backgroundUnion[bool, None]隐藏默认的白色背景,允许使用透明度进行截图。不适用于jpeg图像。默认为false
animationsUnion[“allow”, “disabled”, None]设置为"disabled"时,停止CSS动画、CSS过渡和Web动画。动画的处理方式取决于其持续时间:有限动画将快进到完成状态,因此它们会触发transitionend事件。 无限动画将取消到初始状态,然后在截图后重新播放。默认为"allow",即保持动画不变。
caretUnion[“hide”, “initial”, None]设置为"hide"时,截图将隐藏文本插入符。设置为"initial"时,文本插入符的行为不会改变。默认为"hide"
scaleUnion[“css”, “device”, None]设置为"css"时,截图上每个CSS像素将具有一个实际像素。对于高DPI设备,这将使截图保持较小的大小。使用"device"选项将使每个设备像素有一个实际像素,因此高DPI设备的截图将是两倍或更大。默认为"device"
maskUnion[List[Locator], None]指定在截图时应隐藏的定位符。被隐藏的元素将被叠加一个粉色框#FF00FF(由maskColor自定义),完全覆盖其边界框。
mask_colorUnion[str, None]指定被隐藏元素的覆盖框的颜色,以CSS颜色格式表示。默认颜色为粉色#FF00FF

注意点

locator类下的截图方法,是比page类下的截图方法少了两个可选参数。

full_page:对于元素截图不支持全页面滚动长截图。

clip:对于元素截图不支持裁剪。

Playwright快速使用截图功能

当前页面截图

page.screenshot(path="screenshot.png")

当前页面长截图

page.screenshot(path="screenshot.png", full_page=True)

将截图转为字节存储在缓存中

screenshot_bytes = page.screenshot()
print(base64.b64encode(screenshot_bytes).decode())

根据元素截图

page.locator(".header").screenshot(path="screenshot.png")

两大框架标签页切换对比

在Web UI测试中,我们点击某个带有超链接的元素,可能会在新的标签页打开。

实际上有时候浏览器还是停留在当前页面,并没有自己切到新页面,这时候就需要切换到新的标签页进行元素定位等相关操作。

selenium切换标签页

在selenium是通过handles句柄的方式进行切换。每个页面都有唯一的句柄,最新的页面可通过下标[-1]获取。

driver.switch_to.window(driver.window_handles[-1])

playwright切换标签页

在playwright的page类下有个将页面置于最前面(激活选项卡)方法,可以将目标标签页激活,并且在目标标签页进行元素定位等相关操作。

官方文档:https://playwright.dev/python/docs/api/class-page#page-bring-to-front

# 用法如下
Page.bring_to_front()

如何激活我们所需要激活的页面?

1、通过url

page.url

2、通过title

page.title

playwright切换标签页代码封装

个人感觉切换标签页selenium更方便一点,playwright需要我们自己封装一下。我封装的代码如下:

def switch_to_page(context: BrowserContext, url: str = None, title: str = None) -> Page:"""切换到指定url:param context:传入一个浏览器上下文:param title: 当前标签页的标题:param url: 当前标签页的url:return: label_page:Page对象 返回对应的标签页,如果没找到则返回最新的标签页"""for label_page in context.pages:if url:if url in label_page.url:label_page.bring_to_front()return label_pageelif title:if title in label_page.title():label_page.bring_to_front()return label_pageelse:if title:print(f"没有找到【{title}】标题的标签页")if url:print(f"没有找到【{url}】网址的标签页")return context.pages[-1]

代码注释都写的很清楚了,这里就不单独做解析了。

JS注入

在一些特殊的情况下,我们需要执行原生js,从而达到我们一些框架无法完成的操作。

selenium使用js注入

使用execute_script方法

def execute_script(self, script, *args):"""在当前窗口/框架中同步执行JavaScript。:参数:- script: 可执行的JavaScript脚本.- \*args: 任何适用的JavaScript参数.:使用方法:driver.execute_script('return document.title;')"""

实战示列

我要通过js在浏览器创建一个新标签并打开我博客首页。

from selenium import webdriver
driver = webdriver.Chrome()
driver.execute_script(f'window.open("https://blog.csdn.net/qq_46158060", "_blank");')

playwright使用js注入

官方文档:https://playwright.dev/python/docs/api/class-page#page-evaluate

使用evaluate方法或evaluate_handle方法。

page.evaluate() 和 page.evaluate_handle() 之间的唯一区别是 page.evaluate_handle() 返回 JSHandle。

def evaluate(self, expression: str, arg: typing.Optional[typing.Any] = None) -> typing.Any:

传入的是一个表达式,返回一个序列化值。

官方有挺多个示列。

```py
result = await page.evaluate(\"([x, y]) => Promise.resolve(x * y)\", [7, 8])
print(result) # prints \"56\"
``````py
result = page.evaluate(\"([x, y]) => Promise.resolve(x * y)\", [7, 8])
print(result) # prints \"56\"
```A string can also be passed in instead of a function:```py
print(await page.evaluate(\"1 + 2\")) # prints \"3\"
x = 10
print(await page.evaluate(f\"1 + {x}\")) # prints \"11\"
``````py
print(page.evaluate(\"1 + 2\")) # prints \"3\"
x = 10
print(page.evaluate(f\"1 + {x}\")) # prints \"11\"
```

实战示列(1)

我要通过js在浏览器创建一个新标签并打开我博客首页。

page.evaluate(f'window.open("https://blog.csdn.net/qq_46158060", "_blank");')

实战示列(2)

通过js定位一个id为main-content的元素,并且滚动该元素。

js = 'document.getElementById("main-content").scrollTo(600,800)'
page.evaluate(js)
# 或
# page.evaluate_handle(js)

综合实战

需要操作的页面如下

在这里插入图片描述

实战需求

1、使用playwright连接本地指定端口浏览器

2、通过浏览器在新标签页打开指定项目ID下的相关页面

3、页面分为左右两栏,可以分别滚动,需要滚动右侧栏,并且进行长截图

4、要求一个页面只能有一张图

难点

1、playwright如何连接本地指定端口浏览器进行操作

2、前文中提到,页面截图,默认是第一个滚动条(暂未找到切换滚动条方案),这里需要定位右侧栏,也就是第二个滚动条

3、执行playwright按键操作进行滚动,默认是第一个滚动条,需要结合多种定位和键位,操作复杂

4、前文中提到,如果通过定位右侧栏大框元素进行截图,只能固定截图,无法长截图

解决方案

1、playwright连接本地浏览器详细教程参考我之前写过的文章:playwright连接已有浏览器操作

2、使用js定位右侧框的元素

3、使用js定位右侧框的元素进而执行滚动操作

4、通过js滚动进行多次截图。每滚动一次截图一次,至于滚动的范围需要自己先进行调试,最后将多张图进行拼接成一张图片。

图片拼接技术参考之前文章:web自动化之selenium的特殊用法汇总篇 , 这篇文章的特殊网页无法长截图,使用多图拼接技术章节。

图像拼接封装

下载三方库

pip install pillow==8.4.0

封装代码

from PIL import Image
# 拼接图片的代码
def stitch_images(imgPath_1: str, imgPath_2: str, newImgPath: str) -> str:""":param imgPath_1: 第一张图片的路径,:param imgPath_2: 第二张图片的路径,:param newImgPath: 拼接图片的路径,:return: newImgPath"""# 获取JPG、png图像的路径im_list_1 = [imgPath_1, imgPath_2]im_list = [Image.open(fn) for fn in im_list_1]# 图片转化为相同的尺寸ims = []for i in im_list:new_img = i.resize((1920, 961), Image.BILINEAR)ims.append(new_img)# 单幅图像尺寸width, height = ims[0].size# 创建空白长图result = Image.new(ims[0].mode, (width, height * len(ims)))# 拼接图片for i, im in enumerate(ims):result.paste(im, box=(0, i * height))# 保存图片result.save(newImgPath)return newImgPath

实战完整代码

步骤:

1、使用playwright连接本地浏览器(含用户数据,免登陆,懒加载)

2、使用js在新标签页打开相关网址

3、切换至指定标签页

4、定位右侧栏,结合js滚动进行多图截取

5、使用PIL库进行多图拼接

注:本教程为示列代码,业务代码为方便阅读未进行封装,相关代码都进行了注释。

# -*- coding: utf-8 -*-
"""
@Time : 2023/6/25 14:50
@Email : Lvan826199@163.com
@公众号 : 梦无矶的测试开发之路
@File : PW_001_截图.py
"""
__author__ = "梦无矶小仔"import timefrom playwright.sync_api import sync_playwright, BrowserContext, Page
from PIL import Imagedef switch_to_page(context: BrowserContext, url: str = None, title: str = None) -> Page:"""切换到指定url:param context:传入一个浏览器上下文:param title: 当前标签页的标题:param url: 当前标签页的url:return: label_page:Page对象 返回对应的标签页,如果没找到则返回最新的标签页"""for label_page in context.pages:if url:if url in label_page.url:label_page.bring_to_front()return label_pageelif title:if title in label_page.title():label_page.bring_to_front()return label_pageelse:if title:print(f"没有找到【{title}】标题的标签页")if url:print(f"没有找到【{url}】网址的标签页")return context.pages[-1]# 拼接图片的代码
def images_stitch(imgPath_1: str, imgPath_2: str, newImgPath: str) -> str:""":param imgPath_1: 第一张图片的路径,:param imgPath_2: 第二张图片的路径,:param newImgPath: 拼接图片的路径,:return: newImgPath"""# 获取JPG、png图像的路径im_list_1 = [imgPath_1, imgPath_2]im_list = [Image.open(fn) for fn in im_list_1]# 图片转化为相同的尺寸ims = []for i in im_list:new_img = i.resize((1920, 961), Image.BILINEAR)ims.append(new_img)# 单幅图像尺寸width, height = ims[0].size# 创建空白长图result = Image.new(ims[0].mode, (width, height * len(ims)))# 拼接图片for i, im in enumerate(ims):result.paste(im, box=(0, i * height))# 保存图片result.save(newImgPath)return newImgPath# 主代码playwright = sync_playwright().start()
# 连接已经打开的浏览器,找好端口
browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9223")
default_context = browser.contexts[0]
print(type(default_context))
page = default_context.pages[0]projectID = 6691166666666666666
gameID = 497566666666666666
timeRange = 7# 官方教程 https://playwright.dev/python/docs/api/class-page#page-evaluateurl123 = f'https://play.google.com/console/u/0/developers/{projectID}/app/{gameID}/vitals/metrics/overview?days={timeRange}'page.evaluate(f'window.open("https://play.google.com/console/u/0/developers/{projectID}/app/{gameID}/vitals/metrics/overview?days={timeRange}", "_blank");'
)  # 执行js代码page.wait_for_timeout(5000)page = switch_to_page(default_context, url123)### 进行截图
# 官方教程 https://playwright.dev/python/docs/screenshots#full-page-screenshots
now_time = time.time_ns()
page.wait_for_timeout(1000)
page.locator("#main-content").click()
page.wait_for_timeout(1000)image_path_1 = f'./img-{now_time}.png'
page.locator("#main-content").screenshot(path=f'img-{now_time}.png')
print("截图成功001")page.wait_for_timeout(1000)
js = 'document.getElementById("main-content").scrollTo(600,800)'
# page.evaluate_handle(js)
page.evaluate(js)
print("执行了js滚动")
page.wait_for_timeout(1000)
now_time = time.time_ns()
image_path_2 = f'./img-{now_time}.png'
page.locator("#main-content").screenshot(path=f'img-{now_time}.png')
print("截图成功002")# 截图拼接
new_image_path = './image_result.png'
result_img_path = images_stitch(image_path_1,image_path_2,new_image_path)
print(f"完整长截图路径:{result_img_path}")

最终长截图效果展示

在这里插入图片描述

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

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

相关文章

官方项目《内容示例》中Common UI部分笔记:Common UI 分场景使用教程

文章目录 前言0. 通用设置0.1 开启插件0.2 设置Viewport 1. 分场景教程1. 1 在仅使用鼠标控制的场景下Common Activatable StackCommon Activatable Widget 1.2 当焦点落到一个按钮时显示默认确认(Click/Accept)按键图标Common Input Action DataBaseInp…

【Mysql】数据库第二讲(数据库中数据类型的介绍)

数据类型 1.数据类型分类2.数值类型介绍2.1tinyint类型2.2bit类型介绍2.3小数类型介绍2.3.1 float2.3.2decimal 3.字符串类型介绍3.1char3.2varchar面试:char和varchar的区别 4.日期和时间类型5.enum和set 1.数据类型分类 2.数值类型介绍 2.1tinyint类型 数值越界测…

项目(智慧教室)第三部分,人机交互在stm32上的实现

一。使用软件 1.stm32cubemx中针对汉字提供的软件 2.对数据进行处理 2.上面点击ok--》这里选择确定 3.这里选择保存即可由字符库,但是需要占用内存太大,需35M,但是stm32只有几百k,所以需要自己删减。 生成中文字符(用…

QTday3(QT实现文件对话框保存操作、实现键盘触发事件【WASD控制小球的移动】)

1.实现文件对话框保存操作 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }void Widget::on_fontBtn_clicked() {//调用QFo…

dll文件反编译源代码 C#反编译 dotpeek反编译dll文件后export

目录 背景下载安装dotpeek导入dll文件export导出文件参考 背景 项目合作的时候,使用前人的或者其他部门dll文件直接在机台运行,会出现很多问题,逻辑,效率等等,此时我们可以选择对他们的代码进行反编译和重构&#xff…

redisson分布式锁

RLock官网解释 基于Redis的Java分布式可重入锁对象,实现了锁接口。 如果获得锁的Redisson实例崩溃,那么这种锁可能永远挂起在获得状态。为了避免这种情况,Redisson维护了锁看门狗,它在锁持有者Redisson实例活着的时候延长锁过期时…

算法笔记:哈夫曼树、哈夫曼编码

1 字符的机内表示 2 前缀编码 字符只放在叶结点中字符编码可以有不同的长度由于字符只放在叶结点中,所以每个字符的编码都不可能是其他字符编码的前缀前缀编码可被惟一解码 3 哈夫曼树 哈夫曼树是一棵最小代价的二叉树,在这棵树上,所有的字…

SpotBugs(是FindBugs的继任者)安装、使用

SpotBugs介绍 SpotBugs和FindBugs的关系 SpotBugs是FindBugs的继任者,从SpotBugs停止的地方继续。 备注:FindBugs项目已经停止了,从2015年发布3.0.1版本以后再没有新的版本。 SpotBugs通过静态分析寻找java代码中的bug,通过发现…

lv3 嵌入式开发-9 linux TFTP服务器搭建及使用

目录 1 TFTP服务器的介绍 2 TFTP文件传输的特点 3 TFTP服务器的适用场景 4 配置介绍 4.1 配置步骤 4.2 使用 5 常见错误 1 TFTP服务器的介绍 TFTP(Trivial File Transfer Protocol)即简单文件传输协议 是TCP/IP协议族中的一个用来在客户机与服务器…

手机也可以搭建个人博客?安卓Termux+Hexo搭建属于你自己的博客网站【cpolar实现公网访问】

文章目录 前言 1.安装 Hexo2.安装cpolar3.远程访问4.固定公网地址 前言 Hexo 是一个用 Nodejs 编写的快速、简洁且高效的博客框架。Hexo 使用 Markdown 解析文章,在几秒内,即可利用靓丽的主题生成静态网页。 下面介绍在Termux中安装个人hexo博客并结合…

mysql 安全加固

PS:之前在做安全测试的时候,报告mysql有安全漏洞,于是研究了下如何修复,于是记录下来分享给大家 1.1修改mysql 存放位置 修复 1.停服务 service mysqld stop2.迁位置 2.1 新建迁移目录 mkdir /home/database2.2 迁移数据文件…

【MySQL】MySQL8.0安装教程

下载 MySQL官网下载安装包 安装 1、双击安装程序开始安装 2、选择安装类型 选Server only(只安装mysql),然后点击“next”。 3、检测需要的安装, 直接点击Execute开始安装 4、点击next 5、点击next 6、next 7、密码验证方式&a…

macos13 arm芯片(m2) 搭建hbase docker容器 并用flink通过自定义richSinkFunction写入数据到hbase

搭建hbase docker容器 下载镜像 https://hub.docker.com/r/satoshiyamamoto/hbase/tags 点击run 使用镜像新建容器 填写容器名和 容器与宿主机的端口映射 测试 通过宿主机访问容器内的hbase webUI http://localhost:60010/master-status

I2C与I3C的对比

I2C与I3C的对比 电气特性 I2C 1.半双工 2.串行数据线(SDA)和串行时钟线(SCL) 3.数据线漏极开路,即I2C接口接上拉电阻 4.I2C总线运行速度:**标准模式100kbit/s,快速模式400kbit/s,快速模式plus 1Mbit/s,**高速模式…

深入探讨梯度下降:优化机器学习的关键步骤(三)

文章目录 🍀引言🍀随机、批量梯度下降的差异🍀随机梯度下降的实现🍀随机梯度下降的调试 🍀引言 随机梯度下降是一种优化方法,主要作用是提高迭代速度,避免陷入庞大计算量的泥沼。在每次更新时&a…

[uniapp]踩坑日记 unexpected character > 1或‘=’>1 报错

在红色报错文档里下滑&#xff0c;找到Show more 根据提示看是缺少标签&#xff0c;如果不是缺少标签&#xff0c;看看view标签内容是否含有<、>、>、<号,把以上符合都进行以<号为例做{{“<”}}处理

Ubuntu编译运行socket.io

本篇文章记录一下自己在ubuntu上编译运行socket.io的过程&#xff0c;客户端选用的是socket.io的c的库&#xff0c;编译起来倒不难&#xff0c;但是说到运行的话&#xff0c;对我来说确实是花了点功夫。毕竟程序要能运行起来才能更方便地去熟悉代码&#xff0c;因此今天我就记录…

MySQL——索引

索引在 MySQL 数据库中分三类&#xff1a; B 树索引Hash 索引全文索引 目的&#xff1a;在查询的时候提升效率 b树 参考&#xff1a;https://blog.csdn.net/qq_40649503/article/details/115799935 数据库索引&#xff0c;是数据库管理系统中一个排序的数据结构&#xf…

在VScode中使用sftp传输本地文件到服务器端

安装SFTP 在VScode的扩展中安装sftp 注意这里需要在你没连接服务器的状态下安装&#xff0c;即本机需要有sftp 配置传输端口 安装成功后&#xff0c;使用快捷键"ctrlshiftp",输入sftp&#xff0c;选择Config 根据自己的实际情况修改配置文件&#xff0c;主要改h…

Golang-GJSON 快速而简单的方法来从 json 文档获取值

GJSON 是一个 Go 包&#xff0c;它提供了一种快速而简单的方法来从 json 文档获取值。它具有单行搜索、点符号路径、迭代和解析 json 行等功能。 GJSON 也可用于Python和Rust 入门 安装中 要开始使用GJSON 请安装 Go 并运行 go get &#xff1a; $ go get -u github.com/ti…