当前位置: 首页 > news >正文

Jsoup爬虫——自学习梳理

       ——项目已完结(源码在文末)

        一个较大的项目,通过后台进行网站爬虫,选择的是一个招聘类型的网站,爬取数据后会选择一部分放入到我们的数据库中,前台通过后台返回的Json数据进行展示;大概就是这样的一个项目。

首先来说说Json爬虫,然后一步步带大家进入项目。

1.先展示一下如何使用Jsoup爬取数据,展示在IDEA控制台

导入依赖 放入pom.xml

<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.14.3</version>
</dependency>

        使用Jsoup进行网页爬取时,你可以根据需要选择和提取特定的HTML元素、属性或文本内容。Jsoup提供了丰富的选择器和方法来操作和处理HTML。

import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;public class JsoupExample {public static void main(String[] args) {String url = "https://sou.zhaopin.com/?jl=530&kw=java";try {Document doc0 = Jsoup.connect(url).get();   //请求方式获取到页面   Document元素  HTML文档String d = doc0.toString();Document doc = Jsoup.parse(d, "UTF-8"); //将页面转换为Document对象Elements content = doc.getElementsByClass("joblist-box__item");for(Element element:content){String price = element.getElementsByClass("jobinfo__salary").text();String company = element.getElementsByClass("companyinfo__name").text();System.out.println(price+" " + company);}} catch (IOException e) {e.printStackTrace();}}
}

但是扒下来的数据是假数据,网站知道我们在爬虫,所以为组织我们,给我们假数据蒙混,怎么扒取真的?:模拟浏览器访问

JSOUP设置请求头

public static final String url ="https://sou.zhaopin.com/?jl=530&kw=java";
public static void main(String[] args) throws IOException {Document scriptHtml = Jsoup.connect(url).header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7").header("Accept-Encoding", "gzip, deflate, br, zstd").header("Accept-Language", "zh-CN,zh;q=0.9").header("Cache-Control","max-age=0").header("Connection","keep-alive").header("Upgrade-Insecure-Requests","1").header("User-Agent","Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36")// "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0.header("Cookie", "x-zp-client-id=ef9626f5-a52b-4a15-8a12-b0a85e7c218d; sts_deviceid=19035834b09cac-08a587ad82e264-26001f51-2073600-19035834b0a15e9; LastCity=%E5%8C%97%E4%BA%AC; LastCity%5Fid=530; FSSBBIl1UgzbN7NO=5dPSkp4s02hfTOHNsDqa0XyPFtDtb6nPFiDZH.or1HT9mqeNJTekRD2P9yNyU_R6irRJn347wEzUdpkKbtsY84a; _uab_collina=171888479751435972092526; selectCity_search=530; campusOperateJobUserInfo=c32dd24c-0bda-4124-9245-7bd215dc412c; sensorsdata2015jssdkchannel=%7B%22prop%22%3A%7B%22_sa_channel_landing_url%22%3A%22https%3A%2F%2Flanding.zhaopin.com%2Fregister%3Fidentity%3Dc%26channel_name%3Dbaidu_sem_track%26callback_id%3DIQJ0tYqm%26_data_version%3D0.6.1%26channel_utm_content%3Dpp%26project%3Dzlclient%26channel_utm_medium%3Dcpt%26tid%3DxTB%26channel_link_type%3Dweb%26channel_utm_source%3Dbaidupcpz_x%26hash_key%3DuaKIYNjV6PvgIWUUAJ9n%26sat_cf%3D2%26channel_utm_campaign%3Dzbt%26channel_utm_term%3D01%26_channel_track_key%3DM9kzlBZB%26link_version%3D1%26channel_keyword_id%3D%7Bkeywordid%7D%26channel_ad_id%3D%7Bcreative%7D%26channel_account_id%3D%7Buserid%7D%26channel_keyword%3D%7Bkw_enc_utf8%7D%26channel_adgroup_id%3D%7Bunitid%7D%26channel_campaign_id%3D%7Bplanid%7D%22%7D%7D; locationInfo_search={%22code%22:%22570%22%2C%22name%22:%22%E4%BF%9D%E5%AE%9A%22%2C%22message%22:%22%E5%8C%B9%E9%85%8D%E5%88%B0%E5%B8%82%E7%BA%A7%E7%BC%96%E7%A0%81%22}; Hm_lvt_7fa4effa4233f03d11c7e2c710749600=1719221305,1719382515,1719456967,1719570373; at=47d39fc439d34b6281b7bc14e69b6679; rt=c31cb4d7b5fa4ed59efea7b21489e342; acw_tc=276077d617196447797961224e0bea96b10da8c367bedcc0f99eb890b6caeb; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221148276223%22%2C%22first_id%22%3A%22190356c8cbd25a-0e9855f7268eda8-26001f51-2073600-190356c8cbec48%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTkwMzU2YzhjYmQyNWEtMGU5ODU1ZjcyNjhlZGE4LTI2MDAxZjUxLTIwNzM2MDAtMTkwMzU2YzhjYmVjNDgiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIxMTQ4Mjc2MjIzIn0%3D%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%221148276223%22%7D%2C%22%24device_id%22%3A%22190356c8cbd25a-0e9855f7268eda8-26001f51-2073600-190356c8cbec48%22%7D; Hm_lvt_21a348fada873bdc2f7f75015beeefeb=1719456972,1719464039,1719570379,1719644781; Hm_lpvt_21a348fada873bdc2f7f75015beeefeb=1719644781; FSSBBIl1UgzbN7NP=5Rv5nMDRd7baqqqDA3zDd9a283B0wU93MkS9KJPnFe6CBe_CJZEvMnOO47V8d3kebx.uYMb4zQoh0ciC6Wtf3jx.6HCyx3mShkYcMrJ5A8R6gsymjegmlAsFWm3b1zqR5X_18Ha46KDRQonyHOxMNQ0AomzJ3uHrBPyDM9F_gP4S7ft.OCaZZ8txRjYlslaSbvyLEv0.Kk4c2e5GUNyHQdreCDpgY7bfSULLhDCfmwAMLYllGPLAjdzSKY.9ONJ3BKryunUV69QM.w9sqY8iylQ").timeout(5000).get();Elements content = scriptHtml.getElementsByClass("joblist-box__item");for(Element element:content){String price = element.getElementsByClass("jobinfo__salary").text();String company = element.getElementsByClass("companyinfo__name").text();System.out.println(price+" " + company);}
}

2.怎么把爬取下来的数据放到数据库中

(建立数据库不再叙述,后面会提到相关数据库中表的结构)

准备工作

(1)需要使用xml文件 (2)需要一个接口 (3)需要一个java类

注意事项:

mapper标签里面写接口的路径

标签当中的id的值必须和Dao接口中的一致;

parameterType是我们的入参找到新建的java类,写该类的路径

新建一个类ZhoLianController,用于爬取数据

package com.qcby.springbootdemotest.controller;import com.qcby.springbootdemotest.dao.ZhiLianDao;
import com.qcby.springbootdemotest.entity.SalaryAndSector;
import com.qcby.springbootdemotest.entity.SalaryBySectorAndAddress;
import com.qcby.springbootdemotest.entity.SectorAndAddress;
import com.qcby.springbootdemotest.entity.ZhiLian;
import com.qcby.springbootdemotest.url.AddressMap;
import com.qcby.springbootdemotest.url.StringToNumber;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.io.IOException;
import java.util.List;@Controller
@RequestMapping("/zhilian")
public class ZhiLianController {@Autowiredprivate ZhiLianDao zhiLianDao;/*跳转到前端页面* @return* */@RequestMapping("toWorm")public String worm(){return "/Worm";}@RequestMapping("/insert")@ResponseBody           //加上它返回的是字符串数据,不加返回页面public void wormInsert(ZhiLian zhiLian) throws IOException {//通过前端传来的值进行url连接
//        ZhiLian zhiLian =new ZhiLian();String addressCode= AddressMap.addressMap.get(zhiLian.getAddress());String url="https://sou.zhaopin.com/?jl="+addressCode+"&kw="+zhiLian.getSector();System.out.println("url="+url);
//        Document document= Jsoup.connect(url).get();Document document= Jsoup.connect(url).header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9").header("accept-encoding", "gzip, deflate, br").header("accept-language", "zh-CN,zh;q=0.9").header("cache-control","max-age=0")//1..header("Connection","keep-alive").header("cookie", "x-zp-client-id=4eb47c56-083c-437f-9954-be4bd7c64c1d; LastCity=%E7%9F%B3%E5%AE%B6%E5%BA%84; LastCity%5Fid=565; sts_deviceid=190778561d42c8-067ba08b8cf3eb-610a5850-1327104-190778561d5843; FSSBBIl1UgzbN7NO=5N456oUt6XIZ0dS52PldahGoIHM3r5pNAmUYSSxYRnhyO5d.ji9ME12NMfDxmQ9taNtvAI_p4F82_B7IWHmMMsG; _uab_collina=171999335910624941661227; locationInfo_search={%22code%22:%22570%22%2C%22name%22:%22%E4%BF%9D%E5%AE%9A%22%2C%22message%22:%22%E5%8C%B9%E9%85%8D%E5%88%B0%E5%B8%82%E7%BA%A7%E7%BC%96%E7%A0%81%22}; sensorsdata2015jssdkchannel=%7B%22prop%22%3A%7B%22_sa_channel_landing_url%22%3A%22https%3A%2F%2Flanding.zhaopin.com%2Fregister%3Fidentity%3Dc%26channel_name%3Dbaidu_sem_track%26callback_id%3DIQJ0tYqm%26_data_version%3D0.6.1%26channel_utm_content%3Dpp%26project%3Dzlclient%26channel_utm_medium%3Dcpt%26tid%3DxTB%26channel_link_type%3Dweb%26channel_utm_source%3Dbaidupcpz_x%26hash_key%3DuaKIYNjV6PvgIWUUAJ9n%26sat_cf%3D2%26channel_utm_campaign%3Dzbt%26channel_utm_term%3D01%26_channel_track_key%3DM9kzlBZB%26link_version%3D1%26channel_keyword_id%3D%257Bkeywordid%257D%26channel_ad_id%3D%257Bcreative%257D%26channel_account_id%3D%257Buserid%257D%26channel_keyword%3D%257Bkw_enc_utf8%257D%26channel_adgroup_id%3D%257Bunitid%257D%26channel_campaign_id%3D%257Bplanid%257D%22%7D%7D; zp_passport_deepknow_sessionId=6badb2ecs6aefc42fdb2e87e80cfe24b6c04; at=42d86c88e30d4c45accda450280d83a2; rt=db0d505e13b64d9085e8f6d39d530c9c; sts_sg=1; sts_chnlsid=Unknown; zp_src_url=https%3A%2F%2Flanding.zhaopin.com%2F; ZP_OLD_FLAG=false; Hm_lvt_21a348fada873bdc2f7f75015beeefeb=1719993359,1720077082,1720098938,1720253347; HMACCOUNT=69522ADBD7E7EA74; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221204364767%22%2C%22first_id%22%3A%22190778516f7103-036bced636145e2-610a5850-1327104-190778516f88a9%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_referrer%22%3A%22%22%7D%2C%22identities%22%3A%22eyIkaWRlbnRpdHlfY29va2llX2lkIjoiMTkwNzc4NTE2ZjcxMDMtMDM2YmNlZDYzNjE0NWUyLTYxMGE1ODUwLTEzMjcxMDQtMTkwNzc4NTE2Zjg4YTkiLCIkaWRlbnRpdHlfbG9naW5faWQiOiIxMjA0MzY0NzY3In0%3D%22%2C%22history_login_id%22%3A%7B%22name%22%3A%22%24identity_login_id%22%2C%22value%22%3A%221204364767%22%7D%2C%22%24device_id%22%3A%22190778516f7103-036bced636145e2-610a5850-1327104-190778516f88a9%22%7D; ZL_REPORT_GLOBAL={%22/resume/new%22:{%22actionid%22:%2262436bd1-2cf0-4071-87b5-7743c850cb12%22%2C%22funczone%22:%22addrsm_ok_rcm%22}}; Hm_lvt_7fa4effa4233f03d11c7e2c710749600=1719992130,1720254877; HMACCOUNT=69522ADBD7E7EA74; sts_sid=19087ba37ceb92-0e16a87c97a01c-610a5850-1327104-19087ba37cf94e; sts_evtseq=1; acw_tc=276077ca17202640461465087eba9d8b843d450716e18401fd52993b0e0351; Hm_lpvt_7fa4effa4233f03d11c7e2c710749600=1720264257; selectCity_search=570; Hm_lpvt_21a348fada873bdc2f7f75015beeefeb=1720264296; FSSBBIl1UgzbN7NP=5RL9bRCFxxbQqqqDAf4gtoGTGD4GGh5aQBg2fxUIAohkXIwpeZxRk8XvUBUqvlstbzPJL_Pp4I2oMXD_lTYRY2ymhM2dHag3QDlPYmAuTTqgj7Bq7o_H4PrkKMshDVR70P15rquGifog2KjQT7keH6NoCg8orbbrPvjnrqcbJgu7vULmpaEtXVAXbfY5KQsZjasG2wsE3tdDnSL8rI9K9DFmiMLEPfsQAsPVHrcZHE4ayFNtSA8w.h2Qk7.2ZeGCYtw9I883bIHvqbLHa5s1aC2vXhdPp2dXT7VQyaeFZWKYq")
//                .header("referer","https://landing.zhaopin.com/")//2..header("sec-ch-ua","\";Not A Brand\";v=\"99\", \"Chromium\";v=\"94\"").header("sec-ch-ua-mobile","?0")//3..header("sec-ch-ua-platform","\"Windows\"").header("sec-fetch-dest","document").header("sec-fetch-mode","navigate").header("sec-fetch-site","same-site").header("sec-fetch-user","?1").header("upgrade-insecure-requests","1").header("user-agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.225.400 QQBrowser/12.2.5544.400").timeout(50000).get();
//        System.out.println(document);Elements elements=document.getElementsByClass("joblist-box__item");//clearfixfor(Element element:elements){String salary = element.getElementsByClass("jobinfo__salary").text();String company = element.getElementsByClass("companyinfo__name").text();System.out.println(salary+"--"+ company);//判断金额当中是否包含天if(salary.contains("天")){break; //包含天这个数据不能使用}else {String s1 = StringToNumber.getValue(salary); // 获取最小的金额Double money = StringToNumber.StrToNum(s1) * 12;zhiLian.setSalary(String.valueOf(money));}zhiLian.setCompany(company);zhiLianDao.insert(zhiLian);}}@RequestMapping("/findBySalaryAndSector")@ResponseBodypublic List<SalaryAndSector> findBySalaryAndSector(){return zhiLianDao.fundBySalaryAndSector();}@RequestMapping("/findCountBySectorAndAddress")@ResponseBodypublic List<SectorAndAddress> findCountBySectorAndAddress(String address){return zhiLianDao.findCountBySectorAndAddress(address);}@RequestMapping("/findSalaryBySectorAndAddress")@ResponseBodypublic List<SalaryBySectorAndAddress> findSalaryBySectorAndAddress(String address){return zhiLianDao.findSalaryBySectorAndAddress(address);}
}

 

最后一步,我们需要设置对应的数据库

主要修改数据库路径,选择哪个数据库,以及账号密码即可。

然后运行整个项目,在浏览器输入访问的网址 localhost:8080/zhilian/insert

8080是端口号 访问之后就会进行爬取数据然后放入数据库。

3.设置一个简单的页面,进行某些属性的选择,然后点击爬取之后进行爬取,而不是我们去输入上面提到的网址进行爬取。

输入爬取的城市和爬取的职业,就能进行爬取了,但是智联招聘它的地址都是使用代号进行代替,比如北京你就需要输入530

我们可以使用一个Hashmap进行转换,更加方便我们爬取,不需要爬取城市还得不停地查询对应的代号

我们将addressMap设置为static final,它是属于类的(并且不可更改),目的是为了方便我们无需创建对象即可使用

对应Controller也要进行修改,只需要修改url的拼接部分即可。

此时我们输入北京,会自动转换为530,输入上海会自动转换为538等。这样就更加方便我们操作了。

还有一个小问题就是,由于后期需要进行数据的比较,比如工资,但是扒取下来的工资的值是一个字符串范围,比如“5千-1.2万”

我们再定义一个工具类,想办法把字符串转换为Double。

我们选取一个最低值进行转换,然后将‘-’前面的字符串保留,然后进行转换,千为单位就乘以1000,其余类推。工具类StringToNumber不再赘述

同时需要Controller中进行修改,我们将工资最后设置为年薪(*12)。 3. 到此完成。

 

*4.数据库增删改查操作(这部分属于扩展内容,可简单看)

和之前提到的插入类似,只不过多了查询和修改与删除

新建数据库Student

接口StudentDao

类Student

Mapper.xml

resultType 是返回的数据类型,返回的是我们上面提到的类

上面都形成一种套路,别忘了修改数据库(前面提到的数据库路径)

当我们输入网址进行操作时,如何有形参应该这么填:

 

5.前台页面展示数据

后台返回给前端的是Json数据,前端拿到数据后我们可以借助Echarts图进行展示

Apache EChartsApache ECharts,一款基于JavaScript的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。icon-default.png?t=N7T8https://echarts.apache.org/zh/index.html

从网站上找到我们想要的图,复制代码,然后只需要填入我们的Json数据进去即可展示,很方便。

如何去使用Echarts图帮助我们展示呢,这部分知识我在之前的“前端学习系列”中已经总结好了,该系列包含了前端学习的必要重点知识,很适合学习。

大家可以点击直接跳转学习如何使用Echarts图,系列中的其他文章大家也可以去我的主页进行学习,欢迎评论以及私信交流。

前端学习7——自学习梳理-CSDN博客

前端学习7续-CSDN博客

现在要解决的问题变成了后台怎么把Json数据给前端。举个栗子

在前端Html页面写下get方法:

url是Servlet的路径

success:function(data) 当中的data就是后端返回的Json数据。我们可以打印(consloe.log(data) )在浏览器控制台上,可以查看数据

request是前端给的数据,通常在前端会使用data:{} 里面是Json数据传送给后端,我们这里没有写。

我们执行完sql语句将查询到的数据存到data,最后通过response.getWriter.append(data)返回给前端(上面提到的success:function(data)当中的data)。

6.项目整合

后台已经爬取数据并且放到我们的数据库中

接下来就是前台怎么具体获得后台放到数据库中的数据,详细代码和注释都在文档中,并且详细的解释呢我以截图的方式放在下面,大家对照着去学习理解代码会更加容易理解以及印象深刻。

注:其实除了小的改动,大致内容都和上面提到的我的博客 前端学习7 的代码内容差不多,只是修改了如何将前端写死的数据(前端学习7)换成我们真正的数据,这些数据是通过后台调用方法完成的。具体看代码,看的过程中大家就会发现代码很熟悉的,因为就是和前面的代码大差不差。

.html文件

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><!-- 每一款手机有不同的分辨率,不同屏幕大小,如何使我们开发出来的应用或页面大小能适合各种高端手机使用呢?学习html5 viewport的使用能帮你做到这一点. --><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- width - viewport的宽度height - viewport的高度initial-scale - 初始的缩放比例minimum-scale - 允许用户缩放到的最小比例maximum-scale - 允许用户缩放到的最大比例user-scalable - 用户是否可以手动缩放 --><title>Document</title><script src="js/echarts.min.js"></script><script src="js/china.js"></script><script src="js/index.js" defer></script><link rel="stylesheet" href="css/index.css"><script>// 岗位数量fetch('http://localhost:8080/zhilian/zhilianCount').then(response => response.json()).then(data => {if (data) {document.getElementById('jobCount').innerText = data;} else {console.error('No data returned from API');}}).catch(error => console.error('Error fetching data:', error));</script><!-- 使用fetch函数:fetch('http://localhost:8080/zhilian/zhilianCount')这行代码向指定的URL发起了一个GET请求。fetch函数返回一个Promise对象,这个对象代表了异步操作的结果(即HTTP请求的响应)。 --><script>// 平均薪资最高的地区fetch('http://localhost:8080/zhilian/maxSalary').then(response => response.text()).then(data => {console.log(data)document.getElementById('topSector').innerText = data;}).catch(error => console.error('Error fetching data:', error));</script><script>// 岗位数量最多的地区fetch('http://localhost:8080/zhilian/maxWork') // 修改为你的接口地址.then(response => response.json()).then(data => {if (data) {document.getElementById('highestSalaryCity').innerText = data.address;} else {console.error('No data returned from API');}}).catch(error => console.error('Error fetching data:', error));</script>
</head><body><h1>软件工程岗位招聘信息</h1><div class="main"><div class="left"><div class="l1"></div><div class="l2"></div></div><div class="middle"><ul class="top"><li><h2 id="jobCount"></h2><h3>岗位数量</h3></li><li><h2 id="topSector" topSector></h2><h3>平均薪资最高地区</h3></li><li><h2 id="highestSalaryCity"></h2><h3>岗位数量最多地区</h3></li></ul><div class="bottom"></div></div><div class="right"><div class="r1"></div><div class="r2"></div></div></div>
</body></html>

 

.css文件

* {padding: 0;margin: 0;
}body {background: rgb(16, 12, 42);
}h1 {color: #fff;text-align: center;/* vh视口高度 */height: 10vh;font-size: 5.5vh;/* 行高 */line-height: 10vh;/* 字体粗细 */font-weight: 500;
}.main {display: flex;
}.main .left {width: 29%;
}.main .middle {width: 42%;
}.main .right {width: 29%;
}.main .left>div,
.main .right>div {width: 100%;height: 43vh;
}.main .middle .top {display: flex;margin-top: 30px;justify-content: space-between;padding: 0 20px 0 60px;
}.main .middle .top li {/* 文字居中 */text-align: center;/* 列表样式去掉 */list-style: none;
}.main .middle .top li h2 {color: rgb(173, 255, 47);font-size: 4.5vh;
}.main .middle .top li h3 {color: #fff;margin-top: 30px;
}.main .middle .bottom {height: 69vh;
}

 .js文件

(function() {// 初始化 ECharts 实例var myChart = echarts.init(document.querySelector('.l1'));// 指定图表的配置项和数据var option = {// 标题title: {text: '行业岗位数量情况',left: "center",textStyle: {color: "#fff",},},// 提示框组件tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'}},// x轴相关设置xAxis: {type: 'category',axisLabel: {color: "rgb(175,174,197)",interval: 0, // 强制显示所有标签rotate: 90, // 如果标签太长,可以旋转以避免重叠}},// y轴相关设置yAxis: {type: 'value',axisLabel: {color: "rgb(185,184,206)"},// 修改背景线条样式splitLine: {show: true,lineStyle: {color: "rgb(72,71,83)"}}},series: [{type: 'bar',// 柱子宽度barWidth: '50%'}],// 颜色color: ['rgb(51,152,219)'],// 网格配置grid: {// 配合 left right top bottom 设置图表的大小left: '3%',right: '8%',bottom: '5%',// 网格区域是否包含坐标轴的刻度标签 true:包含 false:不包含containLabel: true},// 数据缩放组件dataZoom: [{type: 'slider',show: true,xAxisIndex: [0],start: 0,end: 100,height: 12,bottom: '1%',}]};// 使用刚指定的配置项和数据显示图表myChart.setOption(option);// 从 API 获取数据并更新图表fetch('http://localhost:8080/zhilian/findBySalaryAndCount') // 修改为你的接口地址.then(response => response.json()).then(data => {// 提取数据const sectors = data.map(item => item.sector);const counts = data.map(item => item.count);// 更新 ECharts 的 option 数据option.xAxis.data = sectors;option.series[0].data = counts;// 设置 option 并渲染图表myChart.setOption(option);}).catch(error => console.error('Error fetching data:', error));// 窗口大小变化时,重置图表大小window.addEventListener("resize", function() {myChart.resize();});
})();//————————————————————————————————————————————————————————————————————————————————//
(function() {// 初始化 ECharts 实例var myChart = echarts.init(document.querySelector('.l2'));// 指定图表的配置项和数据var option = {// 标题title: {text: '各地区岗位数量',left: 'center', // 标题居中排列textStyle: {color: "#fff",},},tooltip: {trigger: 'item'},legend: {type: 'scroll',top: 'center',left: 'right',orient: 'vertical',textStyle: {color: 'rgb(172,171,194)'}},series: [{name: '岗位数量',type: 'pie',radius: ['40%', '70%'],avoidLabelOverlap: false,label: {show: false,position: 'center'},emphasis: {label: {show: true,fontSize: 40,fontWeight: 'bold'}},labelLine: {show: false},data: []}],// 颜色color: ['rgb(73,146,255)', 'rgb(136,255,195)', 'rgb(253,221,96)', 'rgb(255,110,118)']};// 使用刚指定的配置项和数据显示图表myChart.setOption(option);// 从 API 获取数据并更新图表fetch('http://localhost:8080/zhilian/findBySalaryAndCount') // 修改为你的接口地址.then(response => response.json()).then(data => {console.log('Data fetched:', data); // 调试输出数据// 提取数据const chartData = data.map(item => ({value: item.count,name: item.sector}));console.log('Chart Data:', chartData); // 调试输出处理后的数据// 更新 ECharts 的 option 数据option.series[0].data = chartData;// 设置 option 并渲染图表myChart.setOption(option);}).catch(error => console.error('Error fetching data:', error));// 窗口大小变化时,重置图表大小window.addEventListener("resize", function() {myChart.resize();});
})();//平均薪资右一
(function() {// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.querySelector('.r1'));// 初始化配置项var option = {title: {text: '平均薪资最多的前5种职业',left: "center",textStyle: {color: "#fff",},},xAxis: {type: 'category',axisLabel: {color: "rgb(175,174,197)",interval: 0, // 强制显示所有标签rotate: 30, // 如果标签太长,可以旋转以避免重叠}},yAxis: {type: 'value',axisLabel: {color: "rgb(175,174,197)"},splitLine: {show: true,lineStyle: {color: "rgb(72,71,83)"}}},grid: {left: '3%',right: '8%',bottom: '5%',containLabel: true},tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'}},series: [{data: [],type: 'line',areaStyle: {color: {type: 'linear',x: 0,y: 0,x2: 0,y2: 1,colorStops: [{offset: 0,color: 'blue' // 0% 处的颜色}, {offset: 1,color: 'transparent' // 100% 处的颜色}],global: false // 缺省为 false}}}]};fetch('http://localhost:8080/zhilian/findBySalaryAndSector') // 修改为你的接口地址.then(response => response.json()).then(data => {// 排序数据,按平均薪资从高到低排序data.sort((a, b) => b.a_salary - a.a_salary);// 只保留前五个城市的数据const topFiveData = data.slice(0, 5);// 提取数据const cities = topFiveData.map(item => item.sector);const avgSalaries = topFiveData.map(item => item.a_salary);console.log('Top Five Cities:', cities); // 调试输出前五个城市数据console.log('Top Five Avg Salaries:', avgSalaries); // 调试输出前五个薪资数据// 更新ECharts的option数据option.xAxis.data = cities;option.series[0].data = avgSalaries;// 设置option并渲染图表myChart.setOption(option);}).catch(error => console.error('Error fetching data:', error));// 窗口大小变化时,重置图表大小window.addEventListener("resize", function() {myChart.resize();});
})();//右2
(function() {// 初始化 ECharts 实例var myChart = echarts.init(document.querySelector('.r2'));// 指定图表的配置项和数据var option = {// 标题title: {text: '各行业平均薪资',left: "center",textStyle: {color: "#fff",},},// X 轴配置xAxis: {type: 'category',axisLabel: {color: "rgb(175,174,197)",interval: 0, // 强制显示所有标签rotate: 90, // 如果标签太长,可以旋转以避免重叠}},// Y 轴配置yAxis: {type: 'value',axisLabel: {color: "rgb(175,174,197)"},splitLine: {show: true,lineStyle: {color: "rgb(72,71,83)"}},axisLine: {show: true}},// 系列配置series: [{data: [],type: 'line',smooth: true}],// 网格配置grid: {left: '3%',right: '8%',bottom: '5%',containLabel: true},// 提示框组件tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'}},// 数据缩放组件dataZoom: [{type: 'slider',show: true,xAxisIndex: [0],start: 0,end: 100,height: 12,bottom: '1%',}]};// 从 API 获取数据并更新图表fetch('http://localhost:8080/zhilian/findBySalaryAndSector') // 修改为你的接口地址.then(response => response.json()).then(data => {const topFiveData = data;// 提取数据const sectors = topFiveData.map(item => item.sector);const avgSalaries = topFiveData.map(item => item.a_salary);console.log('Top Five Sectors:', sectors); // 调试输出前五个行业数据console.log('Top Five Avg Salaries:', avgSalaries); // 调试输出前五个薪资数据// 更新 ECharts 的 option 数据option.xAxis.data = sectors;option.series[0].data = avgSalaries;// 设置 option 并渲染图表myChart.setOption(option);}).catch(error => console.error('Error fetching data:', error));// 窗口大小变化时,重置图表大小window.addEventListener("resize", function() {myChart.resize();});
})();(function() {// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.querySelector('.bottom'));// 请求数据fetch('http://localhost:8080/zhilian/provinceworkCount').then(response => response.json()).then(data => {// 处理数据,将数据转换为 ECharts 所需格式const chartData = data.map(item => ({ name: item.name, value: item.value }));// 指定图表的配置项和数据const option = {// 标题样式title: {text: "全国岗位分布图",textStyle: {color: 'rgb(255,215,0)',},left: 'center',top: '18%'},tooltip: {trigger: 'item'},visualMap: { // 左侧小导航图标show: true,x: 'left',y: 'bottom',textStyle: {fontSize: 9,color: 'rgb(185,184,206)'},splitList: [{ start: 1, end: 9 },{ start: 10, end: 99 },{ start: 100, end: 999 },{ start: 1000, end: 9999 },{ start: 10000 }],color: ['#334271', '#4d619d', '#6e8adf', '#94d7f1', '#cdeaf6']},series: [{name: '岗位人数',type: 'map',mapType: 'china',roam: false, // 禁用拖动和缩放itemStyle: { // 图形样式normal: {borderWidth: .5, // 区域边框宽度borderColor: '#009fe8', // 区域边框颜色areaColor: "#ffefd5", // 区域颜色},emphasis: { // 鼠标滑过地图高亮的相关设置borderWidth: .5,borderColor: '#4b0082',areaColor: "#fff",}},label: { // 图形上的文本标签normal: {show: true, // 省份名称fontSize: 8,},emphasis: { // 鼠标滑过地图高亮的相关设置show: true,fontSize: 8,}},data: chartData}]};// 使用刚指定的配置项和数据显示图表myChart.setOption(option);}).catch(error => {console.error('Error fetching data:', error);});window.addEventListener("resize", function() {myChart.resize();});
})();

项目最终前端截图

附录

Json爬虫(参考代码) 原理其实和Jsoup爬虫差不多,仅供参考

package com.qcby.springbootdemotest.url;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import java.io.IOException;public class XXDemo {public static void main(String[] args) {CloseableHttpClient httpClient = HttpClients.createDefault();try {HttpGet httpGet = new HttpGet("http://localhost:8080/stu/findAll");// 设置请求头httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");httpGet.setHeader("Accept-Encoding", "gzip, deflate, br, zstd");httpGet.setHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");httpGet.setHeader("Cache-Control", "max-age=0");httpGet.setHeader("Connection", "keep-alive");httpGet.setHeader("Cookie", "Idea-d0f2e52d=4cac9bc5-e85c-4d0c-a38a-1455770b689b");httpGet.setHeader("Host", "localhost:8080");httpGet.setHeader("Upgrade-Insecure-Requests", "1");httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0");// 执行请求CloseableHttpResponse response = httpClient.execute(httpGet);// 获取响应实体String responseBody = EntityUtils.toString(response.getEntity(),"UTF-8");//             输出响应内容System.out.println(responseBody);//JSON解析JSONArray json =  JSON.parseArray(responseBody);System.out.println(json.size());System.out.println(json.get(0));// 关闭响应对象response.close();} catch (Exception e) {e.printStackTrace();} finally {try {httpClient.close();} catch (IOException e) {e.printStackTrace();}}}
}

具体代码以及注释详解我已经打包

由于文章限制,无法上传压缩包,如果有需要压缩包可以私信,同时有问题可以评论或者私信都可以,无偿~ 仅仅作为交流学习

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Connecting weaviate with langflow across docker containers
  • 【OpenCV C++20 学习笔记】调节图片对比度和亮度(像素变换)
  • STM32-寄存器DMA配置指南
  • Web响应式设计———1、Grid布局
  • 算法学习笔记(8.8)-多重背包
  • 【运维笔记】数据库无法启动,数据库炸后备份恢复数据
  • 深入理解JS中的发布订阅模式和观察者模式
  • Windows系统设置暂停更新,暂停时间可达3000天,“永久”暂停更新,亲测有效
  • 企业产品网络安全建设日志0725
  • 商场导航系统:从电子地图到AR导航,提升顾客体验与运营效率的智能解决方案
  • 政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署LivePortrait :通过缝合和重定向控制实现高效的肖像动画制作
  • 无人机制造工艺流程详解
  • 深入探索SemiDrive E3系列的MCU SSDK
  • 算法 定长按组翻转链表
  • 合作伙伴中心Partner Center中添加了Copilot预览版
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 《Java编程思想》读书笔记-对象导论
  • DataBase in Android
  • ES6 学习笔记(一)let,const和解构赋值
  • Java新版本的开发已正式进入轨道,版本号18.3
  • macOS 中 shell 创建文件夹及文件并 VS Code 打开
  • vue-router的history模式发布配置
  • 番外篇1:在Windows环境下安装JDK
  • 聊聊directory traversal attack
  • 聊聊hikari连接池的leakDetectionThreshold
  • 批量截取pdf文件
  • 什么是Javascript函数节流?
  • 微信小程序--------语音识别(前端自己也能玩)
  • 一份游戏开发学习路线
  • 阿里云API、SDK和CLI应用实践方案
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • #传输# #传输数据判断#
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • (2015)JS ES6 必知的十个 特性
  • (Git) gitignore基础使用
  • (Oracle)SQL优化技巧(一):分页查询
  • (第61天)多租户架构(CDB/PDB)
  • (二十六)Java 数据结构
  • (分布式缓存)Redis哨兵
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (十)Flink Table API 和 SQL 基本概念
  • (十二)Flink Table API
  • (一)为什么要选择C++
  • .jks文件(JAVA KeyStore)
  • .net core控制台应用程序初识
  • .net Signalr 使用笔记
  • .net 前台table如何加一列下拉框_如何用Word编辑参考文献
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .net6使用Sejil可视化日志
  • .net中生成excel后调整宽度