18爬虫:关于playwright相关内容的学习

news2025/3/14 0:10:43

1.如何在python中安装playwright

打开pycharm,进入终端,输入如下的2个命令行代码即可自动完成playwright的安装

pip install playwright  ——》在python中安装playwright第三方模块

playwright install ——》安装playwright所需的工具插件和所支持的浏览器

看到这里,是否想要动手进行安装。先不要着急,playwright对安装环境也是有一定要求的。

python版本要求Python 3.8 或更高版本
windows系统要求Windows 10+、Windows Server 2016+
MacOS系统版本要求MacOS 12 Monterey 或 MacOS 13 Ventura
linux系统版本要求Debian 11、Debian 12、Ubuntu 20.04 或 Ubuntu 22.04

2.playwright的录屏功能

playwright具有录屏功能,并自动生成相应的同步操作代码或者异步操作代码,这个功能是非常强大的。

打开pycharm进入终端,输入如下的命令

打开浏览器并启动录屏操作playwright codegen
打开浏览器并进入指定网页同时启动录屏操作playwright codegen https://www.baidu.com/
查看对应的参数playwright codegen --help

当我们在pycharm终端中输入:playwright codegen 后,会自动弹出浏览器和代码录屏窗口

我们在使用playwright弹出的浏览器时,需要讲该浏览器的搜索引擎变更问百度或者其他在国内可用的搜索引擎,原浏览器搜索引擎默认是google。

接下来我们进入百度页面,输入python,可以发现右侧的代码记录工具将我们在浏览器上的操作逐一映射成python代码,默认是生成同步代码,这个代码是可以直接在python编译环境中运行的

import re
from playwright.sync_api import Playwright, sync_playwright, expect


def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://www.baidu.com/s?ie=UTF-8&wd=%E7%99%BE%E5%BA%A6")
    with page.expect_popup() as page1_info:
        page.locator("#content_left [id=\"\\31 \"]").get_by_role("link", name="百度一下,你就知道").click()
    page1 = page1_info.value
    page1.locator("#kw").click()
    page1.locator("#kw").fill("pytho")
    page1.locator("#kw").press("Enter")
    page1.locator("#kw").press("Enter")
    page1.get_by_role("button", name="百度一下").click()

    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)

3.playwright驱动浏览器的两种方式

使用with驱动浏览器,代码如下:

from playwright.sync_api import sync_playwright

with sync_playwright() as p: # 实例化一个playwright对象
    # headless设置有头模式启动浏览器
    browser = p.chromium.launch(headless=False) # 创建一盒浏览器对象,指定驱动的浏览器,用launch设置启动的模式
    context = browser.new_context() # 创建上下文,上下文可以理解为上下文的交互,同时会打开一个浏览器
    page = context.new_page() # 打开一个浏览器的标签页
    page.goto('url') # 访问的网址
    page.wait_for_timeout(10000) # 默认的单位是毫秒,作用类似于timeout,该种方法是playsright自带的

    # 正常关闭playwright的步骤
    page.close() # 关闭访问的页面
    context.close() # 关闭上下文管理工具
    browser.close() # 关闭浏览器

不使用with,一般使用录屏工具生成的代码都是不使用with,代码如下:

from playwright.sync_api import sync_playwright

p = sync_playwright().start() # 实例化一个playwright对象
browser = p.chromium.launch(headless=False,slow_mo=2000) # 创建一个浏览器对象,有头的模式,slow_mo表示每执行一个步骤暂停2秒
context = browser.new_context() # 创建上下文管理器
page = context.new_page() # 创建一个网页对象
page.goto("https://www.baidu.com/") # 使用网页对象访问对应url网站
page.wait_for_timeout(10000) # 等待10秒。10000毫秒

page.close() # 关闭网页对象
context.close() # 关闭上下文管理器
browser.close() # 关闭浏览器对象

p.stop() # 停止playwright对象

使用with驱动浏览器可以自动帮我们打开浏览器(sync_playwright().start()),也可以帮我们自动停止浏览器进程(sync_playwright().stop())。不使用with则需要我们手动开启和停止,除此以外代码逻辑没有区别。

4.playwright同时驱动两个浏览器

在同一个浏览器中使用一个context管理器管理所有的标签页,如果想要同时驱动两个浏览器或者多个浏览器,需要创建两个不同的context管理器,具体的代码如下:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context1 = browser.new_context()
    context2 = browser.new_context()

    page1 = context1.new_page()
    page2 = context2.new_page()

    page1.goto('https://www.baidu.com/')
    page2.goto('https://www.mi.com/shop')

    page1.wait_for_timeout(10000)
    page2.wait_for_timeout(10000)

    print(page1.title)
    print(page2.title)

    page1.close()
    page2.close()
    context1.close()
    context2.close()
    browser.close()

在上述的代码中,我们分别创建两个不同context管理器对象,并创建两个不同的浏览器页面分别访问百度和小米。

5.在同一个浏览器中访问两个不同的网页

有了驱动两个不同浏览器的基础,在同一个浏览器中访问两个不同的网页,也就是打开两个标签是非常简单的。在代码中,我们使用同一个context管理器创建两个不同的page对象分别访问不同的url,具体代码如下:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    # 只需要一个context对象

    page1 = context.new_page()
    page2 = context.new_page()

    page1.goto('https://www.baidu.com/')
    page2.goto('https://www.mi.com/shop')

    page1.wait_for_timeout(10000)
    page2.wait_for_timeout(10000)

    print(page1.title)
    print(page2.title)

    page1.close()
    page2.close()
    context.close()
    browser.close()

6.网页之间的切换操作

学习过前端的相关知识后,我们发现在网页上点击某一超链接,有的会在原标签页上打开超链接对应的网页,有的会打开一个新的标签页,有的会弹出一个新的浏览器窗口。那么如何才能让代码访问到新打开的网页,如何才能返回原网上上呢。

这里就需要使用context管理器中的pages属性,该属性是一个列表,记录我们当前代码打开的所有网页,索引0表示原网页,索引1表示第二次打开的网页,以此类推。

《写文章-CSDN创作中心》对应的索引为0——〉原网页

《qq_37587269-CSDN博客》对应的索引为1——〉第二次打开的网页

《路飞学城-帮助有志向的年轻人》对应的索引为2——〉第三次打开的网页

'''
网页之间的切换
    有些网页的超链接打开之后会新开一个标签页,也有可能是弹出一个新的窗口
    如果要定位新页面上面的元素的话,需要进行网页切换

    pages = context.pages——》上下文管理器中的pages能够记录当前浏览器打开恶所有网页
    pages[i].bring_to_front()——》激活当前页面的句柄

    # 切换网页的另外两种方式
    context.go_to_page(pages[i])
    context.go_to_url(pages[i].url)
'''

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False,slow_mo=2000)
    context = browser.new_context()
    page = context.new_page()

    page.goto("https://blog.csdn.net/weixin_40744274/article/details/140598126") # CSDN网页
    page.locator('xpath=//*[@id="markdownContent"]/p[3]/a').click() # 网页上的某一个超链接,点击之后打开一个新的标签页
    page.locator('xpath=//*[@id="markdownContent"]/p[4]/a').click() # 网页上的某一个超链接,点击之后打开一个新的标签页
    page.wait_for_timeout(5000)

    pages = context.pages # 获取所有的打开网页
    '''
    pages的形式是所有打开的网页的url组成的列表,url的顺序是我们打开网页的顺序,整体称为网页句柄
    pages = [
        <Page url='https://blog.csdn.net/weixin_40744274/article/details/140598126'>, 
        <Page url='https://link.csdn.net/?from_id=140598126&target=https%3A%2F%2Fedu.51cto.com%2Fvideo%2F4645.html%3Futm_platform%3Dpc%26utm_medium%3D51cto%26utm_source%3Dzhuzhan%26utm_content%3Dwzy_tl'>, 
        <Page url='https://link.csdn.net/?from_id=140598126&target=https%3A%2F%2Fedu.51cto.com%2Fvideo%2F3832.html%3Futm_platform%3Dpc%26utm_medium%3D51cto%26utm_source%3Dzhuzhan%26utm_content%3Dwzy_tl'>
    ]
    '''
    print(pages)

    # 切换到网页2上
    pages[1].bring_to_front() # 调整当前playwright的视角至第二个打开的网页上
    pages[1].locator('xpath=//*[@id="linkPage"]/div[1]/div[2]/div[2]/a').click()
    pages[1].wait_for_timeout(5000)

    # 切换到网页3上
    pages[2].bring_to_front() # 调整当前playwright的视角至第三个打开的网页上
    pages[2].locator('xpath=//*[@id="linkPage"]/div[1]/div[2]/div[2]/a').click()
    pages[2].wait_for_timeout(5000)

    # 返回初始网页,也就是网页1
    page.bring_to_front() # pages[0],bring_to_front()
    t = page.locator('xpath=/html/head/title').inner_text() # python playwright模拟鼠标右键点击-CSDN博客
    print(t)

    page.close()
    context.close()
    browser.close()

不同网页之间的切换代码如上所示:利用context.pages中记录的不同网页对象,通过context.pages[i].bring_to_front()激活当前网页,也就是将playwright的视角切换至当前网页。综上所述,playwright切换网页依赖于context的pages属性与bring_to_front()方法。

context.pages记录当前context管理器下所有打开的网页
context.pages[i].bring_to_front()激活指定索引对应的网页

7.模拟网页中的前进与后退

当我们打开某个超链接后,有的会在原标签页上打开超链接对应的网页,此时我们不需要切换playwright的视角就可以访问新网页,这一点是非常人性化的。

当我们访问完新网页后,如何才能退回至原来的网页中呢。这时如果再使用网页切换是不可行的,在context.pages中仅仅保存了最新的url,此时需要使用page中的go_forward()与go_back()方法模拟网页中的前进与后退。具体代码如下:

'''
模拟浏览器中的前进与后退
page.go_forward()和page.go_back()方法,实现网页的前进与后退,同一网页标签上的前进与后退
'''
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://www.mi.com/?g_utm=Thirdparty.Baidu.ProductUnion.BrandZone-Baidu-PC.Brand-A-2")
    page.wait_for_timeout(5000)

    # 点击网页登录,相当于网页前进异步
    page.locator('xpath=//div[contains(text(),"登录")]').click()
    loc = page.locator('xpath=//input[@name="account"]')
    page.wait_for_timeout(5000)
    # 模拟浏览器中的前进后退
    page.go_back() # 网页后退
    page.wait_for_timeout(5000)

    page.go_forward() # 网页前进
    page.wait_for_timeout(5000)

    # 测试前进后退之后是否需要重新定位元素
    # 跳转到登录的页面
    loc.fill('123456')
    page.locator('xpath=//input[@name="password"]').fill('1234567')
    page.wait_for_timeout(5000)
    page.locator('xpath=//button[@type="submit"]').click()
    page.wait_for_timeout(5000)

    page.close()
    context.close()
    browser.close()

8.playwright中的元素定位

playwrigh中元素定位多种,这里我们仅以代码的形式演示如何使用xpath进行元素定位,代码如下:

'''
playwright中的元素定位方法
    playwright中有内置的get_xx_xx方法,系统默认
    同样也有locator()方法进行定位
    contains关键字的使用,对应的语法结构为contains(属性,值)
    and关键字的使用  例如://div[@class="uesr_name" and @id="account"]
    在xpath中通过文本进行定位:注意开始标签和结束标签之间的文本才是元素上的文字,元素上的文字才可以使用该种定位方法
        <div>文本</div>
        对应的语法结构为: //div[text()="输入"]
        也可以结合contains进行文本定位: contains(text(),'登录')

    xpath定位中一些不常用的定位方法

    playwright中的文本定位,精确匹配page.locator('text="登录"'),模糊匹配page.locator('text=登录')
        该定位比xpath中的定位强大很多,不仅可以定位HTML标签之间的文字,也可以定位HTML标签属性内的文字元素,也就是说只要页面上存在的文字都可以拿来使用

只掌握xpath定位即可
'''

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()

    page.goto("https://www.mi.com/?g_utm=Thirdparty.Baidu.ProductUnion.BrandZone-Baidu-PC.Brand-A-2")
    # page.locator('xpath=//div[@class="user-name"]').click() # 通过xpath定位登录按钮然后进行点击登录

    # xpath中关键字contains使用
    # page.locator('//div[contains(@class,"name")]').click() # 定位div标签,其class属性值包含name

    # 使用xpath进行文本定位
    # page.locator('xpath=//div[text()="登录"]').click()

    # contains结合文本进行定位
    page.locator('xpath=//div[contains(text(),"登录")]').click()
    page.wait_for_timeout(5000)

    # 通过xpath中的文本进行定位


    # 跳转到登录的页面
    page.locator('xpath=//input[@name="account"]').fill('123456')
    page.locator('xpath=//input[@name="password"]').fill('1234567')
    page.wait_for_timeout(5000)
    page.locator('xpath=//button[@type="submit"]').click()
    page.wait_for_timeout(5000)

    page.close()
    context.close()
    browser.close()

9.playwright中保存cookie信息以及如何使用cookie自动登录网页

如果想要保存cookie信息,需要使用 context.storage_state(path='login_status.json')

如果想要使用本地的cookie信息,需要在创建context对象时加入本地保留的cookie信息,context = browser.new_context(storage_state='login_status.json')

playwright保留登录的cookie信息,代码如下:

'''
context.storage_state(path='login_status.json') 将cookie保存至一个json文件中
'''
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()

    page.goto("https://www.mi.com/?g_utm=Thirdparty.Baidu.ProductUnion.BrandZone-Baidu-PC.Brand-A-2")
    page.get_by_text("登录").click()
    page.locator('xpath=//*[@id="rc-tabs-0-panel-login"]/form/div[1]/div[1]/div[2]/div/div/div/div/input').fill('XXXXXX')
    page.locator('xpath=//*[@id="rc-tabs-0-panel-login"]/form/div[1]/div[2]/div/div[1]/div/input').fill('XXXXXX')
    page.locator('xpath=//*[@id="rc-tabs-0-panel-login"]/form/div[1]/div[3]/label/span[1]/input').click()
    page.locator('xpath=//*[@id="rc-tabs-0-panel-login"]/form/div[1]/button').press('Enter')
    page.wait_for_timeout(5000)


    # 暴露cookie信息,将对应的信息保存至json文件
    context.storage_state(path='login_status.json')
    page.close()
    context.close()
    browser.close()

使用本地保留的cookie信息登录代码如下:

'''
context.storage_state(path='login_status.json') 将cookie保存至一个json文件中
context = browser.new_context(storage_state='login_status.json')使用保留的json文件
'''
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context(storage_state='login_status.json') # 在实例化上下文的时候使用cookie信息直接登录
    page = context.new_page()

    page.goto("https://www.mi.com/?g_utm=Thirdparty.Baidu.ProductUnion.BrandZone-Baidu-PC.Brand-A-2")
    page.wait_for_timeout(10000)

    page.close()
    context.close()
    browser.close()

10:实战:bili中指定关键字搜索页面中视频的标题和作者名称

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()

    page.goto("https://bilibili.com")
    page.locator('xpath=//input[@class="nav-search-input"]').fill('python')
    page.locator('xpath=//*[@id="nav-searchform"]/div[2]').click()
    # 此时虽然打开一个新的网页,但是playwright的视角页进行了同步切换,检查context.pages只有一个url记录
    t3 = page.locator('xpath=//h3').all()
    for t in t3:
        print(t.get_attribute('title'))

    page.wait_for_timeout(10000)

    page.close()
    context.close()
    browser.close()

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

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

相关文章

docker Error response from daemon: Get “https://registry-1.docker.io/v2/ 的问题处理

docker Error response from daemon: Get "https://registry-1.docker.io/v2/ 的问题处理 最近pull 数据 发现 docker 有如下错误 文章目录 docker Error response from daemon: Get "https://registry-1.docker.io/v2/ 的问题处理报错问题检查网络连接解决方案&…

【Linux系统】线程:线程的优点 / 缺点 / 超线程技术 / 异常 / 用途

1、线程的优点 创建和删除线程代价较小 创建一个新线程的代价要比创建一个新进程小得多&#xff0c;删除代价也小。这种说法主要基于以下几个方面&#xff1a; &#xff08;1&#xff09;资源共享 内存空间&#xff1a;每个进程都有自己独立的内存空间&#xff0c;包括代码段…

123,【7】 buuctf web [极客大挑战 2019]Secret File

进入靶场 太熟悉了&#xff0c;有种回家的感觉 查看源代码&#xff0c;发现一个紫色文件 点下看看 点secret 信息被隐藏了 要么源代码&#xff0c;要么抓包 源代码没有&#xff0c;抓包 自己点击时只能看到1和3处的文件&#xff0c;点击1后直接跳转3&#xff0c;根本不出…

微服务知识——微服务拆分规范

文章目录 一、微服务拆分规范1、高内聚、低耦合2、服务拆分正交性原则3、服务拆分层级最多三层4、服务粒度适中、演进式拆分5、避免环形依赖、双向依赖6、通用化接口设计&#xff0c;减少定制化设计7、接口设计需要严格保证兼容性8、将串行调用改为并行调用&#xff0c;或者异步…

双目标定与生成深度图

基于C#联合Halcon实现双目标定整体效果 一&#xff0c;标定 1&#xff0c;标定前准备工作 &#xff08;获取描述文件与获取相机参数&#xff09; 针对标准标定板可以直接调用官方提供描述文件&#xff0c;也可以自己生成描述文件后用PS文件打印 2&#xff0c;相机标定 &…

在 Navicat 17 中扩展 PostgreSQL 数据类型 | 创建自定义域

定义域 以适当的格式存储数据可以确保数据完整性&#xff0c;防止错误&#xff0c;优化性能&#xff0c;并通过实施验证规则和支持高效数据管理来维护系统间的一致性。基于这些原因&#xff0c;顶级关系数据库&#xff08;如PostgreSQL&#xff09;提供了多种数据类型。此外&a…

Linux+Docer 容器化部署之 Shell 语法入门篇 【Shell 替代】

&#x1f380;&#x1f380;Shell语法入门篇 系列篇 &#x1f380;&#x1f380; LinuxDocer 容器化部署之 Shell 语法入门篇 【准备阶段】LinuxDocer 容器化部署之 Shell 语法入门篇 【Shell变量】LinuxDocer 容器化部署之 Shell 语法入门篇 【Shell数组与函数】LinuxDocer 容…

IDEA+DeepSeek让Java开发起飞

1.获取DeepSeek秘钥 登录DeepSeek官网 : https://www.deepseek.com/ 进入API开放平台&#xff0c;第一次需要注册一个账号 进去之后需要创建一个API KEY&#xff0c;然后把APIkey记录保存下来 接着我们获取DeepSeek的API对话接口地址&#xff0c;点击左边的&#xff1a;接口…

mysql的原理及经验

1. 存储引擎 存储引擎是MySQL的核心组件之一&#xff0c;它负责数据的存储和检索。MySQL支持多种存储引擎&#xff0c;每种引擎都有其独特的特点和适用场景。 InnoDB&#xff1a;这是MySQL的默认存储引擎&#xff0c;支持事务处理&#xff08;ACID特性&#xff09;、行级锁定和…

【漫话机器学习系列】083.安斯库姆四重奏(Anscombe‘s Quartet)

安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09; 1. 什么是安斯库姆四重奏&#xff1f; 安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09;是一组由统计学家弗朗西斯安斯库姆&#xff08;Francis Anscombe&#xff09; 在 1973 年 提出的 四组数据集。它们…

e2studio开发RA2E1(9)----定时器GPT配置输入捕获

e2studio开发RA2E1.9--定时器GPT配置输入捕获 概述视频教学样品申请硬件准备参考程序源码下载选择计时器时钟源UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user_uart_callback ()printf输出重定向到串口定时器输入捕获配…

开源安全一站式构建!开启企业开源治理新篇章

在如今信息技术日新月异、飞速发展的数字化时代&#xff0c;开源技术如同一股强劲的东风&#xff0c;为企业创新注入了源源不断的活力&#xff0c;然而&#xff0c;正如一枚硬币有正反两面&#xff0c;开源技术的广泛应用亦伴随着不容忽视的挑战。安全风险如影随形&#xff0c;…

Node.js 与 npm 版本兼容性问题详解:如何避免版本冲突

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

鸿蒙开发中 SaveButton 按钮 保存按钮点击后权限授权失败。

原因分析 查看官方文档的解释 在 控制台中 过滤这个字段 过滤关键字"SecurityComponentCheckFail"可以获取具体原因。 得到 产生的原因 是 因为层叠的原因 savebutton 组件必须的 在屏幕的最高层 不能有任何的覆盖和遮挡 通过这样书写就解决了 // 下面是安…

胜任力冰山模型:深入探索职业能力的多维结构

目录 1、序言 2、什么是胜任力&#xff1f; 3、任职资格和胜任力的区别 4、胜任力冰山模型&#xff1a;职场能力的多维展现 4.1、冰山水面上的部分 4.2、冰山水面下的部分 4.3、深层的个人特质与价值观 5、如何平衡任职资格与胜任能力 6、结语 1、序言 在快速发展的I…

C#面试常考随笔12:游戏开发中常用的设计模式【C#面试题(中级篇)补充】

C#面试题&#xff08;中级篇&#xff09;&#xff0c;详细讲解&#xff0c;帮助你深刻理解&#xff0c;拒绝背话术&#xff01;-CSDN博客 简单工厂模式 优点&#xff1a; 根据条件有工厂类直接创建具体的产品 客户端无需知道具体的对象名字&#xff0c;可以通过配置文件创建…

将Deepseek接入pycharm 进行AI编程

目录 专栏导读1、进入Deepseek开放平台创建 API key 2、调用 API代码 3、成功4、补充说明多轮对话 总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——…

《论文阅读》GPT-3是否会产生移情对话?一种新的情境示例选择方法和用于生成同理心对话的自动评估度量 ICCL 2022

《论文阅读》GPT-3是否会产生移情对话?一种新的情境示例选择方法和用于生成同理心对话的自动评估度量 ICCL 2022 前言贡献PromptIn-context learningSITSMEMOSITSM新的自动指标实验前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手工敲…

javaEE初阶————多线程初阶(3)

大家新年快乐呀&#xff0c;今天是第三期啦&#xff0c;大家前几期的内容掌握的怎么样啦&#xff1f; 1&#xff0c;线程死锁 1.1 构成死锁的场景 a&#xff09;一个线程一把锁 这个在java中是不会发生的&#xff0c;因为我们之前讲的可重入机制&#xff0c;在其他语言中可…

【Flink快速入门-1.Flink 简介与环境配置】

Flink 简介与环境配置 实验介绍 在学习一门新的技术之前&#xff0c;我们首先要了解它的历史渊源&#xff0c;也就是说它为什么会出现&#xff0c;它能够解决什么业务痛点。所以本节我们的学习目的是了解 Flink 的背景&#xff0c;并运行第一个 Flink 程序&#xff0c;对它有…