Java-网络爬虫(一)

文章目录

  • 前言
  • 一、网络爬虫
    • 1. 介绍
    • 2. 爬虫协议
    • 3. 法律法规
  • 二、相关知识
    • 1. HttpClient
    • 2. Jsoup
  • 三、综合案例
    • 1. 案例一
    • 2. 案例二
  • 四、总结


前言

下篇:Java-网络爬虫(二)

在大数据时代,信息采集是一项重要的工作,而互联网中的数据是海量的,如果单纯靠人力进行信息获取,不仅低效繁琐,而且搜集的成本也会提高,如何自动高效地获取互联网中的数据是一个重要的问题,而爬虫技术就是针对这些问题而生的。

一、网络爬虫

1. 介绍

网络爬虫(Web crawler)又称为网络蜘蛛或网络机器人,是一种自动化程序,用于在互联网上浏览和抓取信息,是互联网时代一项普遍运用的网络信息搜集技术。

在这里插入图片描述

该项技术最早应用于搜索引擎领域,是搜索引擎获取数据来源的支撑性技术之一。随着数据资源的爆炸式增长,网络爬虫的应用场景和商业模式变得更加广泛和多样,较为常见的有新闻平台的内容汇聚和生成、电子商务平台的价格对比功能、基于气象数据的天气预报应用等等。

一个出色的网络爬虫工具能够处理大量的数据,大大节省了人类在该类工作上所花费的时间。网络爬虫作为数据抓取的实践工具,构成了互联网开放和信息资源共享理念的基石,如同互联网世界的一群工蜂,不断地推动网络空间的建设和发展。

原理:

传统爬虫从一个或者若干个初始网页的 URL 开始,通过模拟浏览器行为,自动访问并解析网页。它们可以跟踪链接,从一个网页到另一个网页,逐层遍历整个互联网。通过取网页的HTML源代码,并从中提取有用的信息,如文本、图像、链接等。

功能与价值:

网络爬虫技术是互联网开放共享精神的重要实现工具。允许收集者通过爬虫技术收集数据是数据开放共享的重要措施,网络爬虫能够通过聚合信息、提供链接,为数据所有者的网站带来更多的访问量,这些善意、适量的数据抓取行为,符合数据所有者开放共享数据的预期。

从功能上来讲,爬虫一般分为数据采集、处理、存储三个部分。

爬虫的应用:

  • 实现和优化搜索引擎
  • 获取更多的数据源

2. 爬虫协议

爬虫的功能十分强大,但是我们并不能为所欲为的使用爬虫,爬虫需要遵循 robots 协议,该协议是国际互联网界通行的道德规范,每一个爬虫都应该遵守。

Robots 协议(也称为爬虫协议、机器人协议等)的全称是 “网络爬虫排除标准”(Robots Exclusion Protocol),网站通过 Robots 协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,该协议属于一个规范,并不能保证网站的隐私。

Robots 协议是国际互联网界通行的道德规范,基于以下原则:

  1. 搜索技术应服务于人类,同时尊重信息提供者的意愿,并维护其隐私权。

  2. 网站有义务保证其使用者的个人信息和隐私不被侵犯。

在使用爬虫的时候我们应当注意一下几点:

  1. 拒绝访问和抓取有关不良信息的网站。

  2. 注意版权意识,对于原创内容,未经允许不要将信息用于其他用途,特别是商业方面。

  3. 严格遵循 robots.txt 协议。

  4. 爬虫协议查看方式

大部分网站都会提供自己的 robots.txt 文件,这个文件会告诉我们该网站的爬取准则,查看方式是在域名加 /robots.txt 并回车。

例如百度的爬虫协议:https://www.baidu.com/robots.txt

在这里插入图片描述

  • User-agent:为访问用户
  • Allow:允许爬行的目录
  • Disallow:不允许爬行的目录
  • Sitemap:网站地图,告诉爬虫这个页面是网站地图

从上述协议可以看到百度对于普通使用者为:

User-agent: *
Disallow: /

则表示禁止所有搜索引擎访问网站的任何部分。

而对于 Baiduspider 这类用户

User-agent: Baiduspider
Disallow: /baidu
Disallow: /s?
Disallow: /ulink?
Disallow: /link?
Disallow: /home/news/data/
Disallow: /bh

则不能爬取 /baidu、/s?、/ulink?... 下面目录的数据。


3. 法律法规

网络爬虫规制的必要性:

  • (一)恶意抓取侵害他人权益和经营自由通过网络爬虫访问和收集网站数据行为本身已经产生了相当规模的网络流量,但是,有分析表明其中三分之二的数据抓取行为是恶意的,并且这一比例还在不断上升:恶意机器人可以掠夺资源、削弱竞争对手。恶意机器人往往被滥用于从一个站点抓取内容,然后将该内容发布至另一个站点,而不显示数据源或链接,这一不当手段将帮助非法组织建立虚假网站,产生欺诈风险,以及对知识产权、商业秘密的窃取行为。
  • (二)恶意爬虫危及网络安全从行为本身来讲,恶意爬虫会对目标网站产生 DDOS 攻击的效果,当有成百上千的爬虫机器人与同一网站进行交互,网站将会失去对真实目标的判断,其很难确定哪些流量来自真实用户,哪些流量来自机器人。若平台使用了掺杂虚假访问行为的缺陷数据,做出相关的营销决策,可能会导致大量时间和金钱的损失。尽管 robots 协议作为国际通行的行业规范,能够帮助网站在 robot.txt文件中明确列出限制抓取的信息范围,但并不能从根本上阻止机器人的恶意爬虫行为,其协议本身无法为网站提供任何技术层面的保护。目前恶意的网络爬虫行为已经给互联网平台带来了一定的商业和技术风险,影响了其正常的平台运营和业务开展。
  • (三)现行法律规制方式及其不足之处网络爬虫的不当访问、收集、干扰行为应当受到法律规制。目前,我国已有法律对网络爬虫进行规制主要集中在刑法有关计算机信息系统犯罪的相关条文上。从刑法所追求的法益来看,刑法规范的是对目标网站造成严重影响并具有社会危害性的数据抓取行为。若行为人违反刑法的相关规定,通过网络爬虫访问收集一般网站所存储、处理或传输的数据,可能构成刑法中的非法获取计算机信息系统数据罪;如果在数据抓取过程中实施了非法控制行为,可能构成非法控制计算机信息系统罪。此外,由于使用网络爬虫造成对目标网站的功能干扰,导致其访问流量增大、系统响应变缓,影响正常运营的,也可能构成破坏计算机信息系统罪。

由于刑法的谦抑性,其只能在网络爬虫行为产生严重社会危害而无刑罚以外手段进行规制的情形下起到惩治效果,而对于网络爬虫妨碍其他网站正常运行、过量访问收集数据等一般性危害行为很难起到规制作用,因此我国需要建立在刑法以外的行政规制手段,构建完善的刑事责任、行政责任乃至民事责任体系,以保护互联网平台的合法权益,维护网络空间的正常秩序。

完善网络爬虫规制方式的建议:

从网络爬虫的相关案例来看,其使用者往往有充分的理由做出可能涉嫌违法的数据抓取行为,其辩护理由通常包括:“我可以用公开访问的数据做任何事”“这是合理使用行为”“这与搜索引擎行为类似”“只是使用了自动脚本,而未使用在建立网站上”“我已经遵守了它们的 robots 协议”“该网站没有 robots 协议”“这些数据我只是个人研究使用,并没有商业目的”。由此可见,依托行为是否具有恶意或者通过主观层面来判断爬虫行为违法与否是具有难度的。网络爬虫规制的目标是在数据资源开放共享与互联网平台经营自由、网站安全之间取得平衡,遵循技术中立性原则,对网络爬虫进行规制应当基于客观结果,即是否妨碍网站的正常运行或者对他人合法权益造成严重危害。

数字时代,在数据利用成为网络产业中心的背景下,亟待确立数据访问、获取的规则。在技术手段、市场手段之外,需要采用法律手段规制爬虫技术的应用,对特定的数据访问场景进行规范。通过数据安全立法设置爬虫技术严重影响网站正常运行的判断标准,对具有危害性的网络爬虫行为进行适当规制,是我国安全与发展并重互联网治理根本准则在数据治理领域的体现,其目标是在数据活动各方主体中找到平衡点,兼顾数据开放共享与数据所有者经营自由和安全、社会公共利益,确保数据依法有序自由流动。

谨慎使用的技术:

  1. 爬虫访问频次要控制,别把对方服务器搞崩溃
  2. 涉及个人隐私的信息不能爬
  3. 突破网站的反爬措施,后果很严重
  4. 不要把爬取的数据做不正当竞争
  5. 付费内容,不要抓
  6. 突破网络反爬措施的代码,最好不要上传到网络上

二、相关知识

1. HttpClient

因为爬虫技术是模仿游览器行为,那么必然是需要发送 HTTP 请求,在 Javaapache 有提供支持 HTTP 协议的客户端编程工具包 HttpClient,可以使用 HttpClient 来发送请求,

例如:使用 HttpClient 请求 https://www.rgbku.com/chaxun.html(rgb颜色查询器)

在这里插入图片描述

那么代码可以这样写:

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
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.util.EntityUtils;import java.io.IOException;
import java.util.Objects;public class HttpClientDemo {public static void main(String[] args) {// 创建 httpClient 对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建 httpGet 对象,设置访问 URLHttpGet httpGet = new HttpGet("https://www.rgbku.com/chaxun.html");CloseableHttpResponse response = null;try {// 发送请求response = httpClient.execute(httpGet);// 根据状态码判断是否响应成功(一般是 200)if (response.getStatusLine().getStatusCode() == 200) {// 解析响应HttpEntity entity = response.getEntity();String html = EntityUtils.toString(entity, Consts.UTF_8);// 打印响应内容System.out.println(html);}} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源try {httpClient.close();if (Objects.nonNull(response)) {response.close();}} catch (IOException e) {e.printStackTrace();}}}
}

可以从打印信息中就能看出已获取到该网站的 HTML 信息了

在这里插入图片描述

HttpClient 不仅可以发送 GET 请求,还能够发起 POSTPUTDELETE 等等各种请求,同时还能携带参数、tokencookie 和设置 User-Agent 等功能,可以做到很好的模拟用户在游览器上面访问网站。

GET 请求:

    /*** 发送不带参数的 GET 请求*/public static void sendGet() throws Exception{// 创建 httpClient 对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建 httpGet 对象,设置访问 URLHttpGet httpGet = new HttpGet("https://www.xxxx.com");CloseableHttpResponse response = httpClient.execute(httpGet);// 对响应信息进行处理 ...// 关闭资源response.close();httpClient.close();}/*** 发送带参数的 GET 请求*/public static void sendGetHasParam() throws Exception{// 创建 httpClient 对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建 URIBuilderURIBuilder uriBuilder = new URIBuilder("https://www.xxxx.com");// 设置参数uriBuilder.setParameter("param1", "value1").setParameter("param2", "value2");// 创建 httpGet 对象,设置 URIHttpGet httpGet = new HttpGet(uriBuilder.build());CloseableHttpResponse response = httpClient.execute(httpGet);// 对响应信息进行处理 ...// 关闭资源response.close();httpClient.close();}

POST 请求:

    /*** 发送不带参数的 POST 请求*/public static void sendPost() throws Exception{// 创建 httpClient 对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建 httpPost 对象,设置访问 URLHttpPost httpPost = new HttpPost("https://www.xxxx.com");CloseableHttpResponse response = httpClient.execute(httpPost);// 对响应信息进行处理 ...// 关闭资源response.close();httpClient.close();}/*** 发送带参数的 POST 请求*/public static void sendPostHasParam() throws Exception{// 创建 httpClient 对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建 httpPost 对象,设置访问 URLHttpPost httpPost = new HttpPost("https://www.xxxx.com");// 封装表单中的参数List<NameValuePair> params = new ArrayList<>();params.add(new BasicNameValuePair("param1", "value1"));params.add(new BasicNameValuePair("param2", "value2"));/** 创建表单的 entity 对象*      parameters:表单数据*      charset:编码*/UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, Consts.UTF_8);// 设置表单的 entity 对象到 Post 请求中httpPost.setEntity(entity);CloseableHttpResponse response = httpClient.execute(httpPost);// 对响应信息进行处理 ...// 关闭资源response.close();httpClient.close();}

连接池:

每次发送请求时都需要创建 HttpClient,会有频繁创建和销毁的问题,对性能会有一定的影响,可以使用连接池来解决这个问题

    /*** 连接池*/public static void poolManager() throws Exception {// 创建连接池管理器PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();// 设置最大连接数connectionManager.setMaxTotal(100);// 设置每个主机的最大连接数:因为在爬取数据的时候可通会访问多个主机,如果不设置可能会导致连接不均衡connectionManager.setDefaultMaxPerRoute(10);// 使用连接池管理器发起请求doGet(connectionManager);doGet(connectionManager);}/*** 通过连接池发送 http 请求* @param connectionManager 连接池*/private static void doGet(PoolingHttpClientConnectionManager connectionManager) throws Exception {// 从连接池中获取 HttpClient 对象CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();// 创建 httpGet 对象,设置访问 URLHttpGet httpGet = new HttpGet("https://www.xxxx.com");CloseableHttpResponse response = httpClient.execute(httpGet);// 对响应信息进行处理 ...// 关闭资源response.close();// 这里要注意的是 httpClient 不需要再关闭了,因为是连接池管理的// httpClient.close();}

设置请求信息:

有时候因为网络或者目标服务器的原因,请求需要更长的时间才能完成,或者需要改变 User-Agent 的设置才能正常发起请求时,这个时候就需要自定义设置这些参数

    /*** 配置请求信息*/private static void setRequestInfo() throws Exception {// 创建 httpClient 对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建 httpGet 对象,设置访问 URLHttpGet httpGet = new HttpGet("https://www.xxxx.com");// 配置请求信息RequestConfig requestConfig = RequestConfig.custom()// 创建连接的最长时间,单位是毫秒.setConnectTimeout(1000)// 设置获取连接的最长时间,单位是毫秒.setConnectionRequestTimeout(500)// 设置数据传输的最长时间.setSocketTimeout(10 * 1000)// 还可以设置其它的设置 ....build();// 设置请求信息httpGet.setConfig(requestConfig);CloseableHttpResponse response = httpClient.execute(httpGet);// 对响应信息进行处理 ...// 关闭资源response.close();httpClient.close();}

虽然说 HttpClient 已经具备了爬数据的功能,但是使用 HttpClient 得到的响应信息比较难解析其中的内容,要对 html 进行进行大量的字符串处理,编写正则表达式去匹配想要获取的信息,所以通常情况下我们并不会使用这种方式进行数据分析。


2. Jsoup

Jsoup 是一款 JavaHTML 解析器,可直接解析某个 URL 地址、HTML 文本内容,它提供了一套非常省力的 API,可通过 DOMCSS 以及类似于 jQuery 的操作方法来取出操作数据。

主要功能如下:

  1. 从一个 URL、文件或字符串中解析 HTML
  2. 使用 DOMCSS 选择器来查找,取出数据
  3. 可操作 HTML 元素、属性、文本

引入依赖:

<!-- jsoup -->
<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.15.3</version>
</dependency>

示例:还是以 https://www.rgbku.com/chaxun.html(rgb颜色查询器) 这个网址为例,获取该网址的 title 内容

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;import java.net.URL;public class JsoupDemo {public static void main(String[] args) throws Exception {// 解析 URLDocument document = Jsoup.parse(new URL("https://www.rgbku.com/chaxun.html"), 1000);// 比如我想要获取 html 文件中 <title> 部分的内容String title = document// 获取所有的 title 标签.getElementsByTag("title")// 拿到第一个.first()// 获取标签中的文本内容.text();// 打印System.out.println("title = " + title);}
}

日志信息:

在这里插入图片描述

虽然使用 Jsoup 可以替代 HttpClient 直接发起请求解析数据,但是往往不会这样使用,因为实际的开发过程中,需要使用到多线程、连接池、代理等等方式,而 Jsoup 对这些的支持不是很友好,所以一般把 Jsoup 仅仅作为 Html 解析工具使用

(一)加载文档:

    /*** 通过 URL 加载文档*/public static void loadingUrl() throws Exception {/** 解析 URL:*      spec:访问问的 url*      timeoutMillis:超时时间*/Document document = Jsoup.parse(new URL("https://www.rgbku.com/chaxun.html"), 1000);// 解析 document}/*** 通过字符串加载文档*/public static void loadingString() throws Exception {String html = "html-content";// 解析字符串Document document = Jsoup.parse(html);// 解析 document}/*** 通过文件架子啊文档*/public static void loadingFile() throws Exception {// html 文件File file = new File("D:\\demo.html");// 解析字符串Document document = Jsoup.parse(file, "utf8");// 解析 document}

(二)提取数据:

获取元素

  • 方式一:使用 DOM 方法提取文档数据
    • getElementById(String id):根据 id 获取元素
    • getElementsByTag(String tag):根据标签获取元素
    • getElementsByClass(String className):根据 class 获取元素
    • getElementsByAttribute(String key):根据属性获取元素
    • getElementsByAttributeValue(String key, String value):根据属性和属性值获取元素

示例:

    /*** 使用DOM方法获取元素*/public static void getElementByDom() throws Exception {// 加载 documentDocument document = Jsoup.parse(new URL("https://www.xxxx.com"), 1000);// 根据 id 获取元素Element idElement = document.getElementById("id");// 根据标签获取元素Elements tagElements = document.getElementsByTag("tag_name");// 根据 class 获取元素Elements classElements = document.getElementsByClass("class_name");// 根据属性获取元素Elements attributeElements = document.getElementsByAttribute("attribute");// 通过属性值获取元素Elements attributeValueElements = document.getElementsByAttributeValue("attribute", "value");}
  • 方式二:使用选择器获取元素
    • select(String cssQuery):通过选择器获取元素

示例:

    /*** 使用选择器获取元素*/public static void getElementBySelector() throws Exception {// 加载 documentDocument document = Jsoup.parse(new URL("https://www.xxxx.com"), 1000);// 通过 id 查找元素Element idElement = document.select("#id").first();// 通过标签名称查找元素Elements tagElements = document.select("tag_name");// 通过 class 名称查找元素Elements classElements = document.select(".class_name");// 通过属性获取元素Elements attributeElements = document.select("[attribute]");// 通过属性值获取元素Elements attributeValueElements = document.select("[attribute=value]");/** 选择器可以任意的组合使用*/// tag#id:标签+IDElements tagIdElements = document.select("tag_name#id");// tag.class:标签+classElements tagClassElements = document.select("tag_name.class_name");// tag[attribute]:标签+属性名Elements tagAttributeElements = document.select("tag_name[attribute]");// tag[attribute].class:标签+属性名+classElements tagAttributeClassElements = document.select("tag_name[attribute].class_name");// ancestor child:查询某个元素下的子元素Elements ancestorChildElements = document.select("ancestor child");// parent > child:查询直接子元素Elements parentChildElements = document.select("parent > child");// parent > *:查找所有子元素Elements allChildElements = document.select("parent > *");}

处理元素数据

  • attr(String key):获取属性
  • attr(String key, String value):设置属性
  • attributes():获取所有属性
  • id():获取 id
  • className():获取类名
  • classNames():获取类名集
  • text():获取文本内容
  • text(String value):设置文本内容
  • html():获取内部 HTML 内容
  • html(String value):设置内部 HTML 内容
  • outerHtml():获取外部 HTML 值
  • data():获取数据内容(例如 script 和 style 标签)
  • tag():获取标签
  • tagName():获取标签名称

三、综合案例

爬虫的工作流程通常包括以下几个步骤:

  1. 确定起始点:需要指定一个或多个起始 URL 作为抓取的入口点。

  2. 下载网页:使用 HTTPHTTPS 协议向服务器发送请求,下载网页的 HTML 源代码。

  3. 解析网页:解析 HTML 源代码,提取出所需的信息。

  4. 处理数据:对提取的数据进行处理和清洗,以便后续分析和存储。

  5. 跟踪链接:从当前网页中提取所有链接,并将它们添加到待抓取的 URL 队列中,以便进一步遍历。

  6. 控制抓取速度:为了避免给服务器带来过大的负载,爬虫通常会设置抓取速度限制,包括请求间隔时间和并发请求数量。

  7. 存储数据:将提取的数据保存到数据库、文件或其他存储介质中,以便后续使用和分析。

以下案例我会将抓取到的数据存放在 excel 文件中,会使用到 EasyExcel,对于 EasyExcel 的使用可参考博客: Java-easyExcel入门教程

1. 案例一

将 RBG 颜色查询器页面中的数据抓取出来之后保存到 excel 表格中

在这里插入图片描述

分析:

在这里插入图片描述

从该网址的 HTML 源码分析可得,RGB 的数据来源于 <table> 表格中,<tbody> 定义了表格主题,用于存放数据,每个单元格的数据存放在 <td> 标签中,所以只要拿到 中的文本数据,再剔除掉表头相关的数据即可。

代码实现:

RgbEntity.java

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.*;
import com.alibaba.excel.enums.poi.BorderStyleEnum;
import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** RGB 实体类*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
// 头背景设置
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, horizontalAlignment = HorizontalAlignmentEnum.CENTER, borderLeft = BorderStyleEnum.THIN, borderTop = BorderStyleEnum.THIN, borderRight = BorderStyleEnum.THIN, borderBottom = BorderStyleEnum.THIN)
//标题高度
@HeadRowHeight(40)
//内容高度
@ContentRowHeight(30)
//内容居中,左、上、右、下的边框显示
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, borderLeft = BorderStyleEnum.THIN, borderTop = BorderStyleEnum.THIN, borderRight = BorderStyleEnum.THIN, borderBottom = BorderStyleEnum.THIN)
public class RgbEntity {@ApiModelProperty(value = "英文代码")@ExcelProperty("英文代码")@ColumnWidth(15)private String engName;@ApiModelProperty(value = "中文名")@ExcelProperty("中文名")@ColumnWidth(15)private String zhName;@ApiModelProperty(value = "十六进制")@ExcelProperty("十六进制")@ColumnWidth(15)private String code;@ApiModelProperty(value = "RGB颜色值")@ExcelProperty("RGB颜色值")@ColumnWidth(15)private String value;
}

ReptileDemo.class

import com.alibaba.excel.EasyExcel;
import com.mike.server.system.domain.excel.RgbEntity;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;public class ReptileDemo {public static void main(String[] args) throws Exception {demo01();}/*** 案例一:将 RBG 颜色查询器页面中的数据抓取出来之后保存到 excel 表格中*/public static void demo01() throws Exception {// 获取 document 文档Document document = Jsoup.parse(new URL("https://www.rgbku.com/chaxun.html"), 1000);// 通过 id = color 获取 tbody 元素Element tbodyElement = document.getElementById("color");// 获取 tbody 下所有的 <tr> 标签assert tbodyElement != null;Elements childrenElements = tbodyElement.children();// 创建 RgbEntity 集合存放数据List<RgbEntity> list = new ArrayList<>();// 遍历for (Element childrenElement : childrenElements) {String tagName = childrenElement.tagName();String align = childrenElement.attr("align");// 筛选出每一行的数据,剔除表头if ("tr".equals(tagName) && !"center".equals(align)) {// 获取每个单元格的数据Elements tdElements = childrenElement.children();/** 根据 html 源码分析可得:*      (1)每个 <tr> 标签下都有 5 个 <td> 标签*      (2)这五个 <td> 标签中的内容分别对应:颜色 英文代码 中文名 十六进制 RGB颜色值*/String engName = tdElements.get(1).text();String zhName = tdElements.get(2).text();String code = tdElements.get(3).text();String value = tdElements.get(4).text();// 添加到集合中list.add(RgbEntity.builder().engName(engName).zhName(zhName).code(code).value(value).build());}}// 写入到 excel 中File file = new File("D:\\rgb.xlsx");OutputStream os = new FileOutputStream(file);EasyExcel.write(os, RgbEntity.class).sheet("Sheet1").doWrite(list);}
}

运行代码生成 excel 文件:

在这里插入图片描述


2. 案例二

通过食品营养成分查询平台获取所有食品营养成分数据,并持久化到 excel 文件中

在这里插入图片描述

在这里插入图片描述

这个案例的代码就不太方便展示了,我就简单的说下实现的逻辑:

在这里插入图片描述

通过分析 HTML 源码可知,从大类(一级分类)列表页面中可以获取到大类的名称和图片以及进入到类别(二级分类)列表页的 URL,在类别(二级分类)的页面中又可以获取到类别的名称和进入到食品列表页的 URL,在食品列表页中可以获取到食品的名称和进入到食品成分页面的 URL,在食品成分页中再拿到所有的成分数据。

实体类设计:

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FoodInfo {/*** 大类集*/List<Category> categoryList;/*** 大类(一级分类)*/@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class Category {// 大类名称private String name;// 大类图片private String imageUrl;// 类别集private List<Type> typeList;/*** 类别(二级分类)*/@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class Type {// 类别名称private String name;// 食物集private List<Food> foodList;/*** 食物*/@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class Food {// 食物名称private String name;// 组成成分集private List<Component> componentList;/*** 组成成分*/@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class Component {// 营养素类型private String nutrientType;// 项目private String itemName;// 含量private String value;// 同类排名private String sort;// 同类均值private String avgValue;}}}}
}

这里要注意的首先是 URL 的拼接,因为链接是相对路径的形式,其次是要创建连接池避免资源的浪费,设置访问间隔的时间别把对方服务器搞崩溃。


四、总结

在实际应用中,爬虫可能需要处理一些挑战和限制,如动态网页、反爬虫机制、登录和验证码等。为了应对这些问题,爬虫可能需要使用代理、用户代理伪装、验证码识别等技术。

值得注意的是,尽管爬虫可以自动化地抓取网页,但在使用爬虫时,需要遵守法律法规和网站的使用规则,避免侵犯他人的权益或引起不良后果。

下篇:Java-网络爬虫(二)


参考文献:

爬虫协议:https://www.dotcpp.com/course/317

网络爬虫的法律规制:http://www.cac.gov.cn/2019-06/16/c_1124630015.htm?from=singlemessage

Java 爬虫之 JSoup 使用教程:https://my.oschina.net/suveng/blog/4796066

JSoup教程:https://www.yiibai.com/jsoup

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

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

相关文章

Maven之私服

1 介绍 团队开发现状分析私服是一台独立的服务器&#xff0c;用于解决团队内部的资源共享与资源同步问题Nexus Sonatype公司的一款maven私服产品 下载地址&#xff1a;https://help.sonatype.com/repomanager3/download win版安装包&#xff1a;https://pan.baidu.com/s/1wk…

【我的Rust库】get_local_info 0.1.6发布

get_local_info是一个获取linux本地信息的Rust三方库&#xff0c;其目标是降低获取本地linux系统信息的难度。支持银河麒麟10、UOS、鸿蒙等国产系统。 项目维护&#xff1a;长期 当前版本&#xff1a;0.1.6 当前功能&#xff1a; 1.网络功能 1.1获取活动网卡信息&#xff…

集团企业OA办公协同平台建设方案

一、企业对协同应用的需求分析 实现OA最核心、最基础的应用 业务流转&#xff1a;收/发文、汇报、合同等各种审批事项的业务协作与办理 信息共享&#xff1a;规章制度、业务资料、共享信息资源集中存储、统一管理 沟通管理&#xff1a;电子邮件、手机短信、通讯录、会议协作等…

6.云原生之jenkins集成SonarQube

文章目录 搭建 SonarQube配置SonarQube创建sonar-token生成令牌查看jenkins暴露的NodePort端口创建Webhook 服务器将 SonarQube 配置添加到 ks-installer Jenkins集成SonarQube将 SonarQube 服务器添加至 Jenkinsjenkins配置SonarQubejenkins中配置SonarQube创建Jenkins凭证将 …

Django 10 表单

表单的使用流程 1. 定义 1. terminal 输入 django-admin startapp the_14回车 2. tutorial子文件夹 settings.py INSTALLED_APPS 中括号添加 "the_14", INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.contrib.contenttypes,django.contrib…

Eslint+Prettier

1.Eslint js验证的规则标准,Vue也有自己的独特的验证规则,vue-eslint-plugin属于vue自己的验证规则。 如果不想报错,可以在package.json/rules里面进行关闭,默认是开启的,默认缩进是两个空格。 2.Prettier - Code formatter 使写代码更加的美观 可选的配置项: 例如: module…

STM32---中断

中断框图 一.中断 中断&#xff1a;当有中断请求时&#xff0c;CPU会停止处理当前的任务&#xff0c;转而去处理中断任务。 中断输入线有19/20根&#xff08;互联型号20根&#xff09;。 分类&#xff1a;系统异常&#xff08;10个&#xff09;和外部中断&#xff08;60个&…

编程题实训-排序

第1关&#xff1a;基于链表的简单选择排序 任务描述 试以单链表为存储结构&#xff0c;实现简单选择排序算法。 编程要求 输入 多组数据&#xff0c;每组数据两行。第一行为序列的长度n&#xff0c;第二行为序列的n个元素&#xff08;元素之间用空格分隔&#xff0c;元素都…

20240107让Firefly的AIO-3399J开发板的Android11下配置为默认1080p录像

20240107让Firefly的AIO-3399J开发板的Android11下配置为默认1080p录像 2024/1/7 23:01 开发板&#xff1a;Firefly的AIO-3399J【RK3399】 SDK&#xff1a;rk3399-android-11-r20211216.tar.xz【Android11】 Android11.0.tar.bz2.aa【ToyBrick】 Android11.0.tar.bz2.ab Androi…

QT 的信号和槽机制实现原理的常见问题问答

1. QT的信号和槽的机制实现的原理是什么&#xff1f; Qt的信号和槽机制是通过元对象系统&#xff08;Meta-Object System&#xff09;来实现的。 元对象系统是Qt的一个核心特性&#xff0c;它通过在编译期间为每个QObject派生类生成元对象&#xff08;Meta Object&#xff09…

芯片验证入门踩坑指南(1)

因为一些原因&#xff0c;从华为数通C软件开发到海思这边做芯片验证&#xff0c;快一个月&#xff0c;说下一些心得与体会&#xff1a; 如何快速上手&#xff1a; 因为项目非常赶&#xff0c;几乎没有脱产学习时间&#xff0c;就是直接干项目&#xff0c;一开始不需要知道原理…

OpenCV-18图像的翻转和旋转

一、图像的翻转 使用API---cv.flip&#xff08;src, flipCode&#xff09; flipCode 0表示上下翻转 flipCode > 0表示左右翻转 flipCode < 0上下 左右翻转 或者使用np的翻转src[: : -1,: : -1]实现上下翻转。 示例代码如下&#xff1a; import cv2 import numpy…

项目管理工具Maven

Maven Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c;Java 仍是企业和开发人员的首选开发平台。 课程内容的介绍 1. Maven基础内容 2. Maven的依赖管…

数据版本控制利器LakeFS的介绍,以及其使用方法,与其它工具结合案例

LakeFS介绍 LakeFS 是一个开源的数据湖版本控制系统&#xff0c;可以帮助用户管理和控制数据湖中的数据版本。以下是LakeFS的一些主要用处和功能介绍&#xff1a; 数据版本控制&#xff1a;LakeFS 提供了类似于 Git 的版本控制功能&#xff0c;可以跟踪和管理数据湖中的数据版…

【一】使用vue-cli创建vue3的helloworld项目

不再推荐使用vue-cli命令创建vue3的项目&#xff0c;vue-cli 是 Vue 早期推出的一款脚手架&#xff0c;使用 webpack 创建 Vue 项目。后期推荐使用 create-vue&#xff0c;create-vue 是 Vue3 的专用脚手架&#xff0c;使用 vite 创建 Vue3 的项目(关注【二】使用create-vue创建…

超维空间M1无人机使用说明书——41、ROS无人机使用yolo进行物体识别

引言&#xff1a;用于M1无人机使用的18.04系统&#xff0c;采用的opencv3.4.5版本&#xff0c;因此M1无人机只提供了基于yolov3和yolov4版本的darknet_ros功能包进行物体识别&#xff0c;识别效果足够满足日常的物体识别使用&#xff0c;如果需要更高版本的yolov7或者yolov8&am…

十八:爬虫-JS逆向(下)

一&#xff1a;AES与DES DES对称加密,是一种比较传统的加密方式,其加密运算、解密运算使用的是同样的密钥&#xff0c;信息的发送者。和信息的接收者在进行信息的传输与处理时&#xff0c;必须共同持有该密钥(称为对称密码),是一种对称加密算法。一般来说加密用的是encrypt()函…

thinkadmin安装步骤

一,先cmd运行安装命令 ### 创建项目( 需要在英文目录下面执行 ) composer create-project zoujingli/thinkadmin二,在confing中的database.php配置数据库 三,将仓库的data复制到app目录下 https://gitee.com/zoujingli/think-plugs-data 四,在cmd运行命令安装数据库 //…

第二百五十二回

文章目录 概念介绍实现方法示例代码 我们在上一章回中介绍了如何在页面中添加图片相关的内容&#xff0c;本章回中将介绍如何给组件添加阴影.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在本章回中介绍的阴影类似影子&#xff0c;只是它不像影子那么明显&a…

PTA | 6-2 使用函数实现字符串部分复制

题目 本题要求编写函数&#xff0c;将输入字符串t中从第m个字符开始的全部字符复制到字符串s中。 函数接口定义&#xff1a; void strmcpy( char *t, int m, char *s );函数strmcpy将输入字符串char *t中从第m个字符开始的全部字符复制到字符串char *s中。若m超过输入字符串…