基于XxlCrawler的Java执行JS渲染方式实战-以获取商飞C919飞行照片为例

news2024/11/24 12:30:51

目录

前言

一、抓取目标解析

1、原始网站介绍

2、列表页面结构解析 

二、XxlCrawler的常规配置

1、PageVo对象的定义

2、定义XxlCrawler并启动

三、使用HtmlUnit来执行动态渲染

1、在pom.xml中加入htmlunit的引用

2、设置PageLoader加载器

3、执行抓取

四、总结


前言

        关于XxlCrawler的相关实例,无论是技术博客圈还是本文的历史博客,均有过多篇实践系列的讲解。但是在之前的博客当中,我们有的是直接采集后台返回的json的接口,或者是采集已经渲染好的html网页数据。这些采集的方式和方法是比较简单的。通过在XxlCrawler的官方示例和教程,我们都可以开发出满足业务需要的应用程序。

        不管您是一个后端开发人员,或者是一个前端开发人员。对于Javascript动态渲染一定有所耳闻,界面在加载时,并不是直接就对数据进行了渲染,而是部分渲染。比如加载页面中的静态部分,如网页的主体的架构,要展示的信息要素的布局等等。而真正的数据并不是直接渲染的,而是通过ajax的方式或者其他axios等方式来进行请求接口。不论哪种请求,原理都是执行了某些接口,而这些接口还不一定是直接请求了后台的接口。而且这些数据的展示,必须要等上述的Javascript执行完成之后才能加载到页面中。因此就有了Java动态渲染的说法。与常规的接口抓取和静态页面抓取的方式不同,Javascript的动态渲染页面的抓取就稍微有点麻烦。

        本文即在这样的场景中诞生。本文以获取商飞C919的飞机照片为例,重点讲解Javascript动态渲染的案例场景,怎么抓取这种Javascript动态渲染的网页,最后给出实际的程序代码。让你掌握如何正确的抓取这种Javascript的动态渲染页面,拿到我们需要的数据。本文修正了官网提供的例子无法运行的问题,告诉你正确的开发方式。如果正在阅读博客的你,当前也有这种需求,不妨来看看本文,或许有一定帮助。

一、抓取目标解析

        在进行如何进行信息抓取之前,首先我们对要抓取的目标界面进行深度解析。看看通过Javascript动态渲染的界面跟静态界面有什么不同,如果通过常规的动态渲染的模式,能否正常的抓取数据。这都是本小节需要讲清楚的。

1、原始网站介绍

        对于很多的飞行爱好者来说,飞行器也是一种非常值得观赏的事务。很多的飞行器设计得非常漂亮。在起飞或者降落的时候,是一种别样的美。本人曾将供职于某航空公司,记得当时的一大爱好就是站在办公楼旁边,看着停机坪外的飞机来来往往。曾经也保障过飞机的起飞和降落。对冲上云霄的美有一种特殊的感情。闲言少叙,这里以航班追踪网站为例,可以在它的官方网站上看到很多飞机的图片。这些都是图片都是一些爱好者或者喜欢飞行的朋友们在全世界各地搜集的飞机的飞行照片,很多飞机甚至是大家平时看不到的机型。

        可以在浏览器中输入以下的连接:

https://www.flightaware.com/photos/aircrafttype/C919/sort/votes/page/1

        如下图所示:

        在上图的飞机飞行照片中,就是我们当前选择的目标飞机C919的一些照片。 是不是很酷,点击具体的照片还能看到这些飞机的朋友圈信息,比如全尺寸照片、飞行机型、飞机拍照时的位置信息等等,点赞的数量,浏览的数量等等信息。如下图所示:

2、列表页面结构解析 

        在前面的网页连接中,我们打开飞机的列表界面,按照常规的思路。我们首先来分析一下列表的网页结构,如果采用常规的网页,我们应该怎么做呢?在谷歌浏览器中打开列表界面后,我们可以看到以下的网页结构,通过页面元素检查,可以找到其页面展示结构信息。

        其大致的网页结构源码如下,飞机的照片都是存放到一个id为gridderContainerFancy的一个div下面的,因此我们只要抓取这个div下面的页面元素即可:

<div id="gridderContainerFancy" style="max-width: none;">
    <div style="width: 1214px; overflow: hidden; white-space: nowrap;">
        xxx
    </div>
</div>

        在掌握了以上的相关信息之后,下面我们按照常规的方式,按照我们之前的经验,按照XxlCrawler的相关配置进行相关程序的配置开发。

二、XxlCrawler的常规配置

        这里我们按照XxlCrawler的常规配置模式来介绍如何进行抓取程序的配置。就算后面要执行Javascript的动态渲染,也还是要进行相应的配置的。因此首先来讲讲基础的配置。

1、PageVo对象的定义

        在XxlCrawler抓取信息是,pageVo是一个非常重要的对象。要使用它来进行页面的信息抓取,在下面的网页结构中,我们大概了解了如何抓取页面的请求地址。

        直接抓取它的img标签的src信息,其实就是的访问地址。这个思路是没有问题的。 因此按照这个思路,我们来定义抓取页面的pageVo对象,关键代码如下:

@PageSelect(cssQuery = "#gridderContainerFancy")
@Data
public static class PageVo {

@PageFieldSelect(cssQuery = "img", selectType = SelectType.ATTR, selectVal = "abs:src")
     private List<String> images;
     public List<String> getImages() {
         return images;
     }
     public void setImages(List<String> images) {
         this.images = images;
     }
     @Override
     public String toString() {
         return "PageVo{" + "images=" + images +
                    '}';
     }
 }

2、定义XxlCrawler并启动

        在常规的静态页面抓取时,我们在定义了pageVo之后就可以设置XxlCrawler,然后启动怕抓取器即可。这里直接将关键的代码分享给大家:

@Test
public void testFetch() {
    // 构造爬虫
    XxlCrawler crawler = new XxlCrawler.Builder().setUrls("https://www.flightaware.com/photos/aircrafttype/C919/sort/votes/page/1")
           .setAllowSpread(false)// 允许扩散爬取,将会以现有URL为起点扩散爬取整站,这里只爬一个页面,设置为不允许扩散
			.setThreadCount(3)
            .setTimeoutMillis(120 * 1000)//超时时间定位120秒钟
            .setFailRetryCount(3)// 重试三次
            .setPageParser(new PageParser<PageVo>() {
                @Override
                public void parse(Document html, Element pageVoElement, PageVo pageVo) {
                    System.out.println(pageVo.images.size());
                    if (pageVo.getImages()!=null && pageVo.getImages().size() > 0) {
                         Set<String> imagesSet = new HashSet<>(pageVo.getImages());
                         for (String img: imagesSet) {
                            System.out.println(img);
                            // 下载图片文件
                            String fileName = FileUtil.getFileNameByUrl(img, null);
                            System.out.println(fileName);
                          }
                     }
                }
          }).build();
     crawler.start(true); // 启动
}

        正常情况下,在程序启动后,在控制台就可以看到采集的图片地址信息被输出。感兴趣的朋友可以自己配置一下环境试跑一下,不仅没有正确的获取信息,同时还可能会报错。如下图所示:

22:24:20.161 [main] INFO com.xuxueli.crawler.XxlCrawler - >>>>>>>>>>> xxl crawler still running ...
22:24:22.327 [pool-1-thread-2] INFO com.xuxueli.crawler.XxlCrawler - >>>>>>>>>>> xxl crawler is finished.
22:24:22.327 [pool-1-thread-2] INFO com.xuxueli.crawler.XxlCrawler - >>>>>>>>>>> xxl crawler stop.

        这到底是怎么回事呢? 是不是不符合我们的期望值。首先我们打开页面,查看一下源码,在它的源码中我们可以发现一些蛛丝马迹,如下图所示:

        按图索骥,我们先来看看它的源码: 

$(function () {
	genHTMLFancy();

	$('.pageContainer').infinitescroll({
		navSelector: "div.photosNav",
		nextSelector: "div.photosNav a:eq(1)",
		itemSelector: "div.photoMarker",
		loading: {msgText: 'Loading more photos...', selector: "div.photosNav"},
		behavior: 'flightaware'
	}, function (photoMarkers) {
		var photos = retrievePhotos(photoMarkers);
		++numPagesLoaded;
		var html = genHTMLFancyFromList(photos);
		$('#gridderContainerFancy').append('<div class="photoDivider"><hr class="photoDividerLeft"/>Page ' + parseInt(1 + (numPagesLoaded - 1)) + '<hr class="photoDividerRight" />');
		$('#gridderContainerFancy').append(html);
	});
});

        是否在上面的代码中发现一些线索呢?没有错,眼尖的您一定也发现了,它的gridderContainerFancy的主体内容是通过Javascript动态渲染后得到的。因此我们就大概知道了原因。我们正常配置的抓取器,在页面还没有完全执行完的前提下就执行了页面抓取,所以信息就获取不到。那么如何执行Javascript的动态渲染呢?下一节将重点讲解。

三、使用HtmlUnit来执行动态渲染

        当我们明白了问题的症结之所在,也就知道如何去解决这个问题。本节主要讲解如何使用HtmlUnit来执行Javascript的动态渲染,帮助我们正确的去采集信息。最后将采集的照片地址进行在线打开进行验证。

1、在pom.xml中加入htmlunit的引用

        这里我在编写相关程序时遇到一个问题,就是参考管网的原始实例时,它采用的htmlunit的包和我本文中的有一些不一样,而且它的包引入还有一些问题。导致我们的应用程序不能正确的运行。为了能保证程序的正确运行,我重新修改了htmlunit包的引用,改成下面的引用:

<!-- add by yelangking on 2024/08/04 -->
<!-- https://mvnrepository.com/artifact/org.htmlunit/htmlunit -->
<dependency>
	<groupId>org.htmlunit</groupId>
	<artifactId>htmlunit</artifactId>
	<version>4.4.0</version>
</dependency>

2、设置PageLoader加载器

        这里我们创建一个pageLoader加载器,我们参考官网的代码来进行扩展,避免了官网给出的实例运行不了的问题。

.setFailRetryCount(3)// 重试三次
.setPageLoader(new AircraftTypePageLoader())// HtmlUnit 版本 PageLoader:支持 JS 渲染

        下面来看一下具体的页面加载器的具体实现:

package com.yelang.project.transportation.flight.domain;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.Map;
import org.htmlunit.HttpMethod;
import org.htmlunit.ProxyConfig;
import org.htmlunit.WebClient;
import org.htmlunit.WebRequest;
import org.htmlunit.html.HtmlPage;
import org.htmlunit.util.Cookie;
import org.htmlunit.util.NameValuePair;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xuxueli.crawler.loader.PageLoader;
import com.xuxueli.crawler.model.PageRequest;
import com.xuxueli.crawler.util.UrlUtil;
public class AircraftTypePageLoader extends PageLoader{
	private static Logger logger = LoggerFactory.getLogger(AircraftTypePageLoader.class);
	@Override
	public Document load(PageRequest pageRequest) {
		if (!UrlUtil.isUrl(pageRequest.getUrl())) {
            return null;
        }
        WebClient webClient = new WebClient();
        try {
            WebRequest webRequest = new WebRequest(new URL(pageRequest.getUrl()));
            // 请求设置
            webClient.getOptions().setUseInsecureSSL(true);
            webClient.getOptions().setJavaScriptEnabled(true);
            webClient.getOptions().setCssEnabled(false);
            webClient.getOptions().setThrowExceptionOnScriptError(false);
            webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
            webClient.getOptions().setDoNotTrackEnabled(false);
       webClient.getOptions().setUseInsecureSSL(!pageRequest.isValidateTLSCertificates());
            if (pageRequest.getParamMap() != null && !pageRequest.getParamMap().isEmpty()) {
                for (Map.Entry<String, String> paramItem : pageRequest.getParamMap().entrySet()) {
                    webRequest.getRequestParameters().add(new NameValuePair(paramItem.getKey(), paramItem.getValue()));
                }
            }
            if (pageRequest.getCookieMap() != null && !pageRequest.getCookieMap().isEmpty()) {
                webClient.getCookieManager().setCookiesEnabled(true);
                for (Map.Entry<String, String> cookieItem : pageRequest.getCookieMap().entrySet()) {
                    webClient.getCookieManager().addCookie(new Cookie("", cookieItem.getKey(), cookieItem.getValue()));
                }
            }
            if (pageRequest.getHeaderMap() != null && !pageRequest.getHeaderMap().isEmpty()) {
                webRequest.setAdditionalHeaders(pageRequest.getHeaderMap());
            }
            if (pageRequest.getUserAgent() != null) {
                webRequest.setAdditionalHeader("User-Agent", pageRequest.getUserAgent());
            }
            if (pageRequest.getReferrer() != null) {
                webRequest.setAdditionalHeader("Referer", pageRequest.getReferrer());
            }
            webClient.getOptions().setTimeout(pageRequest.getTimeoutMillis());
            webClient.setJavaScriptTimeout(pageRequest.getTimeoutMillis());
            webClient.waitForBackgroundJavaScript(pageRequest.getTimeoutMillis());
            // 代理
            if (pageRequest.getProxy() != null) {
                InetSocketAddress address = (InetSocketAddress) pageRequest.getProxy().address();
                boolean isSocks = pageRequest.getProxy().type() == Proxy.Type.SOCKS;
                String proxyScheme = null;
                webClient.getOptions().setProxyConfig(new ProxyConfig(address.getHostName(), address.getPort(), proxyScheme, isSocks));
            }
            // 发出请求
            if (pageRequest.isIfPost()) {
                webRequest.setHttpMethod(HttpMethod.POST);
            } else {
                webRequest.setHttpMethod(HttpMethod.GET);
            }
            HtmlPage page = webClient.getPage(webRequest);
            String pageAsXml = page.asXml();
            if (pageAsXml != null) {
                Document html = Jsoup.parse(pageAsXml);
                return html;
            }
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (webClient != null) {
                webClient.close();
            }
        }
        return null;
	}
}

3、执行抓取

        在完成上述的配置之后,再来运行抓取程序,我们来观察控制台的信息输出,可以看到下面的程序输出,说明信息抓取成功。

22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl -       job id: 5
22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl -   6)  Job target execution time: 1722956284105
22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl -       job to string: JavaScript Execution Job 6: window.setTimeout(function(){a.hide()}, 200)
22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl -       job id: 6
22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl -   7)  Job target execution time: 1722956286635
22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl -       job to string: JavaScript Execution Job 7: window.setTimeout(function(){reelIn(bait,1);}, 5)
22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl -       job id: 7
22:58:06.630 [pool-1-thread-2] DEBUG org.htmlunit.javascript.background.JavaScriptJobManagerImpl - ------------------------------------------
22:58:06.656 [pool-1-thread-2] DEBUG org.htmlunit.html.ScriptElementSupport - Loading external JavaScript: https://a.pub.network/flightaware-com/pubfig.min.js
22:58:06.657 [pool-1-thread-2] DEBUG org.htmlunit.WebClient - Load response for GET https://a.pub.network/flightaware-com/pubfig.min.js
22:58:06.658 [pool-1-thread-2] DEBUG org.apache.http.client.protocol.RequestAddCookies - CookieSpec selected: mine

        从页面中抓取的图片信息如下:

21
https://photos.flightaware.com/photos/retriever/3ccbbb7b86b670f616b811871be13ccc5a849c38
https___photos.flightaware.com_photos_retriever_3ccbbb7b86b670f616b811871be13ccc5a849c38
https://photos.flightaware.com/photos/retriever/ec7adec83e353fd44cb25401bcf6c40ff91c4075
https___photos.flightaware.com_photos_retriever_ec7adec83e353fd44cb25401bcf6c40ff91c4075
https://photos.flightaware.com/photos/retriever/752c011d314b366377471ae3d0e7c89da3ffe228
https___photos.flightaware.com_photos_retriever_752c011d314b366377471ae3d0e7c89da3ffe228
https://photos.flightaware.com/photos/retriever/2281714df14ca36daa5db6ebbaa7d745cecb25d0
https___photos.flightaware.com_photos_retriever_2281714df14ca36daa5db6ebbaa7d745cecb25d0
https://photos.flightaware.com/photos/retriever/aa601aa1b763e17f64640bb7b92f8937cd4d3771

         我们将图片地址复制到地址栏中,可以看到他们的实际图片信息,如下图所示:

 

        通过以上步骤,我们基本上就将C919的照片通过动态渲染的方式进行了抓取,后续可以将这些抓取下来的图片进行入库,比如放到统一存储服务器中,后面可以进行统一分析或者知识图谱的建设使用。 

四、总结

        以上就是本文的主要内容。本文以获取商飞C919的飞机照片为例,重点讲解Javascript动态渲染的案例场景,怎么抓取这种Javascript动态渲染的网页,最后给出实际的程序代码。让你掌握如何正确的抓取这种Javascript的动态渲染页面,拿到我们需要的数据。本文修正了官网提供的例子无法运行的问题,告诉你正确的开发方式。如果正在阅读博客的你,当前也有这种需求,不妨来看看本文,或许有一定帮助。行文仓促,难免有不足之处,如有错误或者不足之处,欢迎各位专家朋友在评论区留言批评指正,不胜感激。

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

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

相关文章

看,esp8266就是这么简单

材料准备 1.esp 8266 2.一条可以传输数据的数据线 3一台电脑 前言 如今是物联网的时代&#xff0c;例如“智能家居&#xff0c;无人驾驶……”&#xff0c;多方面的进行物联网改革与创新。那其中&#xff0c;物联网主要的是联网。那通常都是以“esp 8266”和“esp 32”占据了…

在Kylin服务器安装PostgreSQL16数据库

1、下载PostgreSQL16安装包 下载地址https://www.postgresql.org/ftp/source/v16.3/ 2、安装依赖和ICU库 查看服务器版本 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel python-devel gcc-c opens…

在抖音做电商推广,货架场非做不可

前一段时间跟几个做电商的朋友聊天&#xff0c;我发现大家的干劲儿还挺满的&#xff0c;讨论的话题也出奇地一致&#xff1a;要找新增量。 其中有个朋友是做服装品类的&#xff0c;做得还不错。我请教他秘诀&#xff0c;他说&#xff1a;做电商&#xff0c;推广拿量非常关键。…

MySQL笔记(七):索引

一、索引优化速度 创建对应字段的索引&#xff0c;只对该列有效&#xff0c;只能提高该列的查询速度 创建索引后&#xff0c;查询速度变快&#xff0c;但是表占用空间变大 create index 索引名 on 表名(需要创建索引的列)二、索引的原理 普通索引允许该字段重复 全文索引&#…

Resize Observer监测DOM元素尺寸改变的神器

前言 大家在遇到需要监测DOM元素尺寸大小的需求时&#xff0c;可能第一时间想到的都是使用window.addEventListener来监听resize 事件&#xff0c; 但是reize事件会在一秒内触发将近60次&#xff0c;所以很容易在改变窗口大小时导致性能问题。因为它会监听我们页面每个元素的…

MySQL总体功能

基于Innodb存储引擎的讨论 MySQL 核心功能 功能解决的问题ACID模型数据并发访问&#xff0c;和奔溃恢复安全问题,一致性&奔溃恢复索引数据查询效率问题备份容错设计,解决硬件错误带来的问题复制数据迁移监控执行数据库操作的异常记录

《嵌入式 - 嵌入式大杂烩》ARM Cortex-M寄存器详解

1 ARM Cortex-M寄存器概述 ARM Cortex-M提供了 16 个 32 位的通用寄存器(R0 - R15),如下图所示。前15个(R0 - R14)可以用作通用的数据存储,R15 是程序计数器 PC,用来保存将要执行的指令。除了通用寄存器,还有一些特殊功能寄存器。特殊功能寄存器有预定义的功能,而且必须通…

Java编码算法

编码 1.编码算法2.URL编码**URLEncoder类&#xff0c;主要进行编码****URLDecoder类&#xff0c;主要进行解码** 3.Base64编码Base64编码Base64的补充字符Base64的占位符Base64的应用 结论&#xff1a; 1.编码算法 什么是编码? ASCII码就是一种编码&#xff0c;字母A的编码是…

C语言典型例题28

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 习题2.5 输入一个华氏温度&#xff0c;要求输出摄氏温度。公式为C5/9(F-32)&#xff0c;要求输出要有文字说明&#xff0c;取两位小数 数学知识&#xff1a; &#xff08;1&#xff09;华氏温度与摄氏温度&#x…

MySQL(六):mysql 约束

基本介绍&#xff1a;约束用于确保数据库的数据满足特定的商业规则&#xff0c;约束包括&#xff1a;not null、unique、primary key、foreign key 、check五种。 一、主键的使用&#xff08;primary key) 字段名 字段类型 primary key用于唯一的表示表行的数据&#xff0c;当…

SpringBoot基础 第二天

SpringBoot对静态资源的映射: (1) 要在src/main/resources文件夹下创建static和templates两个文件夹staitc存储静态资源,templates存储模板引擎 (2)要在pom.xml依赖下导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>…

VS项目打包成lib库并使用

一、新建一个静态库项目 一般要把项目设为Release模式 二、添加文件 将所需要打包的头文件、源文件添加到该静态库项目中 三、生成项目 生成成功后即可在Release文件夹出现找到相应的.lib文件 四、使用静态库 将静态库文件复制到项目文件夹中&#xff0c;然后在项目属性设…

LSV实验——部署DR模式集群

目录 一、实验环境 二、配置 1、LVS 2、router 3、client 4、RS 三、配置策略 1.Director服务器采用双IP桥接网络&#xff0c;一个是VPP&#xff0c;一个DIP 2.Web服务器采用和DIP相同的网段和Director连接 3.每个Web服务器配置VIP 4.每个web服务器可以出外网 一、实验环…

CENTOS9+RSYSLOG+LOGROTATE收集日志

系统版版本为CentOS Stream release 9 rsyslog和lograte通常系统已经安装了。 #rpm -qa | grep rsyslog rsyslog-logrotate-8.2310.0-4.el9.x86_64 rsyslog-8.2310.0-4.el9.x86_64 rsyslog-gnutls-8.2310.0-4.el9.x86_64 rsyslog-gssapi-8.2310.0-4.el9.x86_64 rsyslog-relp-…

常见中间件漏洞(三、Jboss合集)

目录 三、Jboss Jboss介绍 3.1 CVE-2015-7501 漏洞介绍 影响范围 环境搭建 漏洞复现 3.2 CVE-2017-7504 漏洞介绍 影响范围 环境搭建 漏洞复现 3.3 CVE-2017-12149 漏洞简述 漏洞范围 漏洞复现 3.4 Administration Console弱囗令 漏洞描述 影响版本 环境搭建…

Netty技术全解析:ByteToMessageCodec类深度解析

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

Qt/C++最新地图组件发布/历时半年重构/同时支持各种地图内核/包括百度高德腾讯天地图

一、前言说明 最近花了半年时间&#xff0c;专门重构了整个地图组件&#xff0c;之前写的比较粗糙&#xff0c;有点为了完成功能而做的&#xff0c;没有考虑太多拓展性和易用性。这套地图自检这几年大量的实际项目和用户使用下来&#xff0c;反馈了不少很好的建议和意见&#…

【技巧】IDEA 个性化配置

【技巧】IDEA 个性化配置 自动补全 关闭大小写区分 自动导包 插件 Rainbow Brackets 彩色括号 更容易区分是哪个括号

【LVS】负载均衡之NAT模式

一、LVS概念 LVS&#xff08;Linux Virtual Server&#xff09;是一个基于Linux操作系统的虚拟服务器技术&#xff0c;用于实现负载均衡和高可用性。LVS通过将客户端的请求分发到多台后端服务器上&#xff0c;从而提高整体服务的处理能力和可靠性。 二、LVS优势 高性能&…

Centos安装OpenJDK

使用yum包管理器搜索可用的OpenJDK包 sudo yum search openjdk根据你的需求选择合适的OpenJDK版本进行安装 sudo yum install java-1.8.0-openjdk.x86_64安装完成后&#xff0c;验证安装是否成功 java -version