如何使用Scrapy和Python 3爬取网页

简介

网络爬虫,通常称为网络爬行或网络蜘蛛,是以编程方式浏览一系列网页并提取数据的行为,是处理网络数据的强大工具。

通过使用网络爬虫,您可以挖掘有关一组产品的数据,获取大量文本或定量数据以进行分析,从没有官方 API 的网站检索数据,或者只是满足您自己的个人好奇心。

在本教程中,您将学习有关爬取和蜘蛛过程的基础知识,同时探索一个有趣的数据集。我们将使用Quotes to Scrape,这是一个托管在专门用于测试网络蜘蛛的网站上的引用数据库。通过本教程结束时,您将拥有一个完全功能的 Python 网络爬虫,它可以浏览包含引用的一系列页面,并在屏幕上显示它们。

该爬虫将很容易扩展,因此您可以对其进行调整,并将其用作从网络上爬取数据的自己项目的基础。

先决条件

要完成本教程,您需要一个用于 Python 3 的本地开发环境。您可以按照《如何安装和设置 Python 3 的本地编程环境》中的说明配置所需的一切。

步骤 1 —— 创建基本爬虫

爬取是一个两步过程:

  1. 系统地查找并下载网页。
  2. 从下载的页面中提取信息。

这两个步骤可以用许多语言的许多方式来实现。

您可以使用编程语言提供的模块或库从头开始构建一个爬虫,但随着爬虫变得更加复杂,您可能会遇到一些潜在的问题。例如,您需要处理并发性,以便可以同时爬取多个页面。您可能希望找出如何将爬取的数据转换为不同的格式,如 CSV、XML 或 JSON。有时您还必须处理需要特定设置和访问模式的网站。

如果您构建的爬虫基于一个已存在的库,该库可以为您处理这些问题,那么您将会更加顺利。在本教程中,我们将使用 Python 和 Scrapy 来构建我们的爬虫。

Scrapy 是最流行和强大的 Python 爬取库之一;它采用“电池包含”方法来进行爬取,这意味着它处理了所有爬虫都需要的常见功能,因此开发人员不必每次都重新发明轮子。

Scrapy,像大多数 Python 包一样,位于 PyPI(也称为 pip)上。PyPI 是所有已发布的 Python 软件的社区拥有的存储库。

如果您的 Python 安装与本教程的先决条件中概述的一样,那么您的机器上已经安装了 pip,因此您可以使用以下命令安装 Scrapy:

pip install scrapy

如果您在安装过程中遇到任何问题,或者您想要在不使用 pip 的情况下安装 Scrapy,请查看官方安装文档。

安装了 Scrapy 后,为我们的项目创建一个新文件夹。您可以在终端中运行以下命令来执行此操作:

mkdir quote-scraper

现在,进入您刚刚创建的新目录:

cd quote-scraper

然后创建一个名为 scraper.py 的新 Python 文件,用于我们的爬虫。在本教程中,我们将把所有代码放在这个文件中。您可以使用您选择的编辑软件创建此文件。

通过创建一个以 Scrapy 为基础的非常基本的爬虫来开始项目。为此,您需要创建一个 Python 类,它是 scrapy.Spider 的子类,这是 Scrapy 提供的一个基本蜘蛛类。该类将具有两个必需的属性:

  • name —— 蜘蛛的名称。
  • start_urls —— 从中开始爬取的 URL 列表。我们将从一个 URL 开始。

在您的文本编辑器中打开 scrapy.py 文件,并添加以下代码以创建基本蜘蛛:

import scrapyclass QuoteSpider(scrapy.Spider):name = 'quote-spdier'start_urls = ['https://quotes.toscrape.com']

让我们逐行分解这段代码:

首先,我们导入 scrapy,以便可以使用该软件包提供的类。

接下来,我们使用 Scrapy 提供的 Spider 类,并将其作为 BrickSetSpider子类。将子类视为其父类的更专业化形式。Spider 类具有定义如何跟踪 URL 并从找到的页面中提取数据的方法和行为,但它不知道要查找的位置或要查找的数据。通过对其进行子类化,我们可以提供这些信息。

最后,我们将类命名为 quote-spider,并为我们的爬虫指定一个起始 URL:https://quotes.toscrape.com。如果您在浏览器中打开该 URL,它将带您到一个搜索结果页面,显示许多著名引语的第一页。

现在,测试一下爬虫。通常,Python 文件是通过类似 python path/to/file.py 的命令运行的。但是,Scrapy 配备了自己的命令行界面,以简化启动爬虫的过程。使用以下命令启动您的爬虫:

scrapy runspider scraper.py

该命令将输出类似以下内容:

2022-12-02 10:30:08 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.epollreactor.EPollReactor
2022-12-02 10:30:08 [scrapy.extensions.telnet] INFO: Telnet Password: b4d94e3a8d22ede1
2022-12-02 10:30:08 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',...'scrapy.extensions.logstats.LogStats']
2022-12-02 10:30:08 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',...'scrapy.downloadermiddlewares.stats.DownloaderStats']
2022-12-02 10:30:08 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',...'scrapy.spidermiddlewares.depth.DepthMiddleware']
2022-12-02 10:30:08 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2022-12-02 10:30:08 [scrapy.core.engine] INFO: Spider opened
2022-12-02 10:30:08 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2022-12-02 10:30:08 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2022-12-02 10:49:32 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://quotes.toscrape.com> (referer: None)
2022-12-02 10:30:08 [scrapy.core.engine] INFO: Closing spider (finished)
2022-12-02 10:30:08 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 226,...'start_time': datetime.datetime(2022, 12, 2, 18, 30, 8, 492403)}
2022-12-02 10:30:08 [scrapy.core.engine] INFO: Spider closed (finished)

这是大量的输出,让我们逐一解释一下。

  • 爬虫初始化并加载了所需的其他组件和扩展,以处理从 URL 读取数据。
  • 它使用我们在 start_urls 列表中提供的 URL,并获取了 HTML,就像您的网络浏览器一样。
  • 它将该 HTML 传递给 parse 方法,该方法默认情况下不执行任何操作。由于我们从未编写过自己的 parse 方法,因此蜘蛛在不执行任何工作的情况下就完成了。

现在让我们从页面中提取一些数据。

第二步 — 从页面中提取数据

我们已经创建了一个非常基本的程序,用于下载页面,但它还没有进行任何爬取或蜘蛛行为。让我们给它一些要提取的数据。

如果你查看我们要爬取的页面,你会发现它具有以下结构:

  • 每个页面都有一个标题。
  • 有一个登录链接。
  • 然后是引语本身,显示在一个类似表格或有序列表的结构中。每个引语都有类似的格式。

在编写爬虫时,你需要查看 HTML 文件的源代码,并熟悉其结构。以下是源代码,为了可读性,已删除了与我们目标无关的标签:

[secondary_label quotes.toscrape.com]
<body>
...<div class="quote" itemscope itemtype="http://schema.org/CreativeWork"><span class="text" itemprop="text">“I have not failed. I&#39;ve just found 10,000 ways that won&#39;t work.”</span><span>by <small class="author" itemprop="author">Thomas A. Edison</small><a href="/author/Thomas-A-Edison">(about)</a></span><div class="tags">Tags:<meta class="keywords" itemprop="keywords" content="edison,failure,inspirational,paraphrased" /    > <a class="tag" href="/tag/edison/page/1/">edison</a><a class="tag" href="/tag/failure/page/1/">failure</a><a class="tag" href="/tag/inspirational/page/1/">inspirational</a><a class="tag" href="/tag/paraphrased/page/1/">paraphrased</a></div></div>
...    
</body>

爬取这个页面是一个两步过程:

  1. 首先,通过查找页面上具有我们想要的数据的部分来获取每个引语。
  2. 然后,对于每个引语,通过从 HTML 标签中提取数据来获取我们想要的数据。

scrapy 根据你提供的 选择器 来获取数据。选择器是我们可以使用的模式,以便找到页面上的一个或多个元素,以便我们可以处理元素内的数据。scrapy 支持 CSS 选择器或 XPath 选择器。

我们现在将使用 CSS 选择器,因为 CSS 完美适用于查找页面上的所有集合。如果你查看 HTML,你会发现每个引语都是用类 quote 指定的。由于我们正在寻找一个类,我们将使用 .quote 作为我们的 CSS 选择器。选择器的 . 部分搜索元素上的 class 属性。我们只需在我们的类中创建一个名为 parse 的新方法,并将该选择器传递到 response 对象中,如下所示:

class QuoteSpider(scrapy.Spider):name = 'quote-spdier'start_urls = ['https://quotes.toscrape.com']def parse(self, response):QUOTE_SELECTOR = '.quote'TEXT_SELECTOR = '.text::text'AUTHOR_SELECTOR = '.author::text'for quote in response.css(QUOTE_SELECTOR):pass

这段代码获取页面上的所有集合,并循环遍历它们以提取数据。现在让我们提取这些引语的数据,以便我们可以显示它。

再次查看我们要解析的页面的源代码,告诉我们每个引语的文本存储在具有 text 类的 span 中,引语的作者存储在具有 author 类的 <small> 标签中:

[secondary_label quotes.toscrape.com]...<span class="text" itemprop="text">“I have not failed. I&#39;ve just found 10,000 ways that won&#39;t work.”</span><span>by <small class="author" itemprop="author">Thomas A. Edison</small>...

我们正在循环遍历的 quote 对象有其自己的 css 方法,因此我们可以传入一个选择器来定位子元素。修改你的代码如下,以查找集合的名称并显示它:

class QuoteSpider(scrapy.Spider):name = 'quote-spdier'start_urls = ['https://quotes.toscrape.com']def parse(self, response):QUOTE_SELECTOR = '.quote'TEXT_SELECTOR = '.text::text'AUTHOR_SELECTOR = '.author::text'for quote in response.css(QUOTE_SELECTOR):yield {'text': quote.css(TEXT_SELECTOR).extract_first(),'author': quote.css(AUTHOR_SELECTOR).extract_first(),}

在这段代码中,你会注意到两件事情:

  • 我们在引语和作者的选择器后附加了 ::text。这是一个 CSS 伪选择器,它获取标签内的文本,而不是标签本身。
  • 我们对 quote.css(TEXT_SELECTOR) 返回的对象调用了 extract_first(),因为我们只想要匹配选择器的第一个元素。这给我们一个字符串,而不是元素的列表。

保存文件并再次运行爬虫:

scrapy runspider scraper.py

这次输出将包含引语及其作者:

...
2022-12-02 11:00:53 [scrapy.core.scraper] DEBUG: Scraped from <200 https://quotes.toscrape.com>
{'text': '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”', 'author': 'Albert Einstein'}
2022-12-02 11:00:53 [scrapy.core.scraper] DEBUG: Scraped from <200 https://quotes.toscrape.com>
{'text': '“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”', 'author': 'Jane Austen'}
2022-12-02 11:00:53 [scrapy.core.scraper] DEBUG: Scraped from <200 https://quotes.toscrape.com>
{'text': "“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”", 'author': 'Marilyn Monroe'}
...

让我们继续扩展这个功能,通过添加新的选择器来获取关于作者的页面链接和引语标签的链接。通过调查每个引语的 HTML,我们发现:

  • 存储作者 about 页面的链接是在其名称后面紧跟的一个链接中。
  • 标签存储为一组 a 标签,每个都有 tag 类,存储在具有 tags 类的 div 元素内。

因此,让我们修改爬虫以获取这些新信息:

class QuoteSpider(scrapy.Spider):name = 'quote-spdier'start_urls = ['https://quotes.toscrape.com']def parse(self, response):QUOTE_SELECTOR = '.quote'TEXT_SELECTOR = '.text::text'AUTHOR_SELECTOR = '.author::text'ABOUT_SELECTOR = '.author + a::attr("href")'TAGS_SELECTOR = '.tags > .tag::text'for quote in response.css(QUOTE_SELECTOR):yield {'text': quote.css(TEXT_SELECTOR).extract_first(),'author': quote.css(AUTHOR_SELECTOR).extract_first(),'about': 'https://quotes.toscrape.com' + quote.css(ABOUT_SELECTOR).extract_first(),'tags': quote.css(TAGS_SELECTOR).extract(),}

保存你的更改并再次运行爬虫:

scrapy runspider scraper.py

现在输出将包含新的数据:

2022-12-02 11:14:28 [scrapy.core.scraper] DEBUG: Scraped from <200 https://quotes.toscrape.com>
{'text': '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”', 'author': 'Albert Einstein', 'about': 'https://quotes.toscrape.com/author/Albert-Einstein', 'tags': ['inspirational', 'life', 'live', 'miracle', 'miracles']}
2022-12-02 11:14:28 [scrapy.core.scraper] DEBUG: Scraped from <200 https://quotes.toscrape.com>
{'text': '“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”', 'author': 'Jane Austen', 'about': 'https://quotes.toscrape.com/author/Jane-Austen', 'tags': ['aliteracy', 'books', 'classic', 'humor']}
2022-12-02 11:14:28 [scrapy.core.scraper] DEBUG: Scraped from <200 https://quotes.toscrape.com>
{'text': "“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”", 'author': 'Marilyn Monroe', 'about': 'https://quotes.toscrape.com/author/Marilyn-Monroe', 'tags': ['be-yourself', 'inspirational']}

现在让我们将这个爬虫转变为一个可以跟踪链接的蜘蛛。

步骤 3 —— 爬取多个页面

你已经成功地从初始页面提取了数据,但我们并没有继续查看其余的结果。爬虫的整个目的是检测和遍历到其他页面的链接,并从这些页面中获取数据。

你会注意到每个页面的顶部和底部都有一个小右尖括号(>),它链接到下一页的结果。以下是该部分的 HTML 代码:

[secondary_label quotes.toscrape.com]
...<nav><ul class="pager"><li class="next"><a href="/page/2/">Next <span aria-hidden="true">&rarr;</span></a></li></ul></nav>
...

在源代码中,你会找到一个带有 next 类的 li 标签,以及在该标签内部的一个指向下一页的 a 标签。我们所要做的就是告诉爬虫,如果存在下一页,就跟随该链接。

按照以下方式修改你的代码:

class QuoteSpider(scrapy.Spider):name = 'quote-spdier'start_urls = ['https://quotes.toscrape.com']def parse(self, response):QUOTE_SELECTOR = '.quote'TEXT_SELECTOR = '.text::text'AUTHOR_SELECTOR = '.author::text'ABOUT_SELECTOR = '.author + a::attr("href")'TAGS_SELECTOR = '.tags > .tag::text'NEXT_SELECTOR = '.next a::attr("href")'for quote in response.css(QUOTE_SELECTOR):yield {'text': quote.css(TEXT_SELECTOR).extract_first(),'author': quote.css(AUTHOR_SELECTOR).extract_first(),'about': 'https://quotes.toscrape.com' + quote.css(ABOUT_SELECTOR).extract_first(),'tags': quote.css(TAGS_SELECTOR).extract(),}next_page = response.css(NEXT_SELECTOR).extract_first()if next_page:yield scrapy.Request(response.urljoin(next_page))

首先,我们定义了一个用于“下一页”链接的选择器,提取第一个匹配项,并检查其是否存在。scrapy.Request 是一个新的请求对象,Scrapy 知道这意味着它应该获取并解析下一页。

这意味着一旦我们转到下一页,我们将在那一页上寻找下一页的链接,在该页面上我们将寻找下一页的链接,依此类推,直到我们找不到下一页的链接为止。这是网页抓取的关键部分:查找和跟随链接。在这个例子中,它非常线性;一个页面有一个链接到下一页,直到我们到达最后一页。但你也可以跟随标签、或其他搜索结果的链接,或者任何你想要的 URL。

现在,如果你保存你的代码并再次运行爬虫,你会发现它不仅仅在迭代完第一页的结果后停止。它会继续遍历所有 10 页上的 100 条引语。在整个过程中,这并不是一个巨大的数据块,但现在你知道了自动查找新页面进行抓取的过程。

以下是本教程的完整代码:

import scrapyclass QuoteSpider(scrapy.Spider):name = 'quote-spdier'start_urls = ['https://quotes.toscrape.com']def parse(self, response):QUOTE_SELECTOR = '.quote'TEXT_SELECTOR = '.text::text'AUTHOR_SELECTOR = '.author::text'ABOUT_SELECTOR = '.author + a::attr("href")'TAGS_SELECTOR = '.tags > .tag::text'NEXT_SELECTOR = '.next a::attr("href")'for quote in response.css(QUOTE_SELECTOR):yield {'text': quote.css(TEXT_SELECTOR).extract_first(),'author': quote.css(AUTHOR_SELECTOR).extract_first(),'about': 'https://quotes.toscrape.com' + quote.css(ABOUT_SELECTOR).extract_first(),'tags': quote.css(TAGS_SELECTOR).extract(),}next_page = response.css(NEXT_SELECTOR).extract_first()if next_page:yield scrapy.Request(response.urljoin(next_page),)

结论

在本教程中,你构建了一个完全功能的爬虫,可以在不到三十行的代码中从网页中提取数据。这是一个很好的开始,但你可以用这个爬虫做很多有趣的事情。这应该足够让你思考和实验。如果你需要更多关于 Scrapy 的信息,请查看 Scrapy 的官方文档。关于从网页中提取数据的更多信息,请参阅我们的“如何使用 Beautiful Soup 和 Python 3 抓取网页”的教程。

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

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

相关文章

1、FreeCAD概述与架构

FreeCAD介绍 FreeCAD的诞生&#xff1a;2002年10月29日&#xff0c;由Jrgen Riegel上传了版本0.0.1的初始上传。FreeCAD的维基百科页面显示&#xff0c;FreeCAD基本上是由不同强大的库组成的集合&#xff0c;其中最重要的是openCascade&#xff0c;用于管理和构建几何体&#x…

【Vue 2.x】学习vue之一基础部分

文章目录 Vue 一基础部分第一章1、git两个分支主分支子分支 使用方法方式1&#xff1a;采用命令的方式操作分支方式2&#xff1a;在idea中使用git的分支 向git远程仓库提交时忽略文件使用git时的一些冲突注意事项 2、Vue问题1&#xff1a;什么是Vue&#xff1f;问题2&#xff1…

泰勒创造力达到顶峰?(下)

上文说了一半&#xff0c;回顾看文&#xff1a; https://blog.csdn.net/weixin_41953346/article/details/138336524 继续看下文 “Like I lost my twin /Fuck it if I cant have him,"she sings in “Down Bad". 在《Down Bad》这首歌中&#xff0c;她唱道&#xff…

TiDB系列之:使用TiUP部署TiDB集群最新版本,同时部署TiCDC的详细步骤

TiDB系列之:使用TiUP部署TiDB集群最新版本,同时部署TiCDC的详细步骤 一、部署TiDB集群二、准备环境三、安装 TiUP四、安装TiUP cluster组件五、初始化包含TiCDC的TiDB集群拓扑文件六、检查和修复集群存在的潜在风险七、查看可以安装的tidb版本八、部署 TiDB 集群:九、查看集…

Activiti7 开发快速入门【2024版】

记录开发最核心的部分&#xff0c;理论结合业务实操减少废话&#xff0c;从未接触工作流快速带入开发。假设你是后端的同学学过JAVA和流程图&#xff0c;则可以继续向后看&#xff0c;否则先把基础课程书准备好先翻翻。 为什么要工作流 比起直接使用状态字段&#xff0c;工作…

工业互联网常用开源库

libopen62541 opc-ua开源库 libmodbus modbus开源库 libsocketcan can 开源库 canutils&#xff1a;ubuntu 中socket can 与can通道绑定命令ifconfig -a 查看当前can设备名如can0ip link set down can0ip link set can0 type can bitrate 5000ip link set up can0cansend ca…

【C++之多态的知识】

C学习笔记---018 C之多态的知识1、C多态的简单介绍1.1、多态的分类1.2、多态的构成条件 2、虚函数2.1、虚函数的重写(覆盖) 3、虚函数重写的两个例外3.1、协变&#xff1a;(基类与派生类虚函数返回值类型不同)3.2、析构函数的重写(基类与派生类析构函数的名字不同) 4、两个关键…

redis运维篇下篇

最近在学redis&#xff0c;由于笔者是学运维的&#xff0c;所以推荐学习运维的小伙伴参考&#xff0c;希望对大家有帮助&#xff01; redis运维篇上篇:http://t.csdnimg.cn/MfPud 附加redis多用户管理:http://t.csdnimg.cn/DY3yx 目录 十.redis慢日志 十一.redis的key的有效…

dvwa kali SQL注入

high: 1.txt的来源 1.txt的内容 手动添加&#xff1a; id1&SubmitSubmit 执行&#xff1a; sqlmap -r /root/1.txt -p id --second-url "http://192.168.159.128:20000/vulnerabilities/sqli_blind/" --batch medium&#xff1a; 换链接&#xff0c;换cook…

HDFS存取策略联系

书上关于这部分分了三个点&#xff1a; 1.数据存放 2.数据读取 3.数据复制 但数据存放和数据复制都是数据写操作过程中的&#xff0c;“存放”体现一种思想&#xff0c;“复制”体现过程&#xff0c;整个数据写操作过程如下&#xff1a; 1.分块&#xff1a;当客户端写入一个…

【JS篇之】异常

前言&#xff1a;在代码编写过程中&#xff0c;最常遇到的就是程序异常。其实异常并非坏事&#xff0c;它可以让开发人员及时发现、定位到错误&#xff0c;提醒我们做正确的事情&#xff0c;甚至在某些时候&#xff0c;我们还会手动抛出异常。 1.异常的分类 在JS中&#xff0…

2021 OWASP Top 10-零基础案例学习

文章目录 A01:2021 – 权限控制失效情境 #1: SQL 注入攻击风险风险与后果解决方案情境 #2: 未经授权的访问控制漏洞风险与后果解决方案 A02:2021 – 加密机制失效情境 #1: 自动解密的信用卡卡号与SQL注入情境 #2: 弱SSL/TLS使用与会话劫持情境 #3: 不安全的密码存储与彩虹表攻击…

http实现post请求时本地没问题,线上报413错误、nginx配置免费https、nginx反向代理

MENU 错误原因解决其他方式关于nginx的文章 错误原因 前端发送请求以后后端没有收到请求 而客户端却报了413错误 是请求实体过大的异常 如果请求夹带着文件就可能造成请求实体过大 那这里是什么原因造成的呢 在基础的后端开发中 都会用到nginx反向代理 默认大小为1M 超过1M都会…

LinkedList与链表

文章目录 ArrayList的缺陷链表链表的概念及结构链表的实现 LinkedList的使用什么是LinkedListLinkedList具体使用 ArrayList和LinkedList的区别 ArrayList的缺陷 通过源码知道&#xff0c;ArrayList底层使用数组来存储元素 由于其底层是一段连续空间&#xff0c;当在ArrayList任…

Windows 11 系统安装时如何跳过联网和逃避微软账号登录

问题描述 Windows 11 是从 22H2 版本之后开始强制联网何登录微软账号的。 这就带来两个问题&#xff1a; 1、如果我的电脑没有网络或者网卡驱动有问题&#xff0c;那就无法继续安装系统了。 2、如果我有强怕症&#xff0c;就是不想登录微软账号&#xff0c;害怕个人信息泄露…

SpringEL表达式编译模式SpelCompilerMode详解

https://docs.spring.io/spring-framework/reference/core/expressions.html 在构建SpringEL表达式解析器时候&#xff0c;发现可以传递个SpelCompilerMode参数&#xff0c;这个值不传的话默认是OFF // SpelParserConfiguration config new SpelParserConfiguration(); Spel…

uniApp+Vue3+vite+Element UI或者Element Plus开发学习,使用vite构建管理项目,HBuilderX做为开发者工具

我们通常给小程序或者app开发后台时&#xff0c;不可避免的要用到可视化的数据管理后台&#xff0c;而vue和Element是我们目前比较主流的开发管理后台的主流搭配。所以今天石头哥就带大家来一起学习下vue3和Element plus的开发。 准备工作 1&#xff0c;下载HBuilderX 开发者…

Portworx安装和使用

Portworx安装和使用 Portworx介绍 Portworx是一家美国存储初创公司&#xff0c;它研发了业界第一个容器定义存储系统Portworx。Portworx提供了全新的、统一的Scale out存储栈&#xff0c;其核心架构是共享的、松耦合的、分布式、基于元数据的块存储层(卷、块设备、全局共享卷…

【webrtc】MessageHandler 8: 基于线程的消息处理:处理音频输入输出断开

m98代码,看起来m114 去掉了MessageHandler :音频的录制和播放 都使用了on message,但只是用来通知并处理流的断开的。AAudioRecorder AAudioRecorder 处理流断开 OnErrorCallback :有可能 错误回调是别处来的,是其他线程, 但是这个错误的处理要再自己的线程执行: 音频播…

Go中为什么不建议用锁?

Go语言中是不建议用锁&#xff0c;而是用通道Channel来代替(不要通过共享内存来通信&#xff0c;而通过通信来共享内存)&#xff0c;当然锁也是可以用&#xff0c;锁是防止同一时刻多个goroutine操作同一个资源&#xff1b; GO语言中&#xff0c;要传递某个数据给另一个gorout…