引言:
我们在工作中会遇见一些业务就是将某个网址的数据给获取下来,我们首先想到的就是爬虫,接下来演示的就是使用Java的Jsoup框架进行获取数据,
1、环境准备
首先无论我们平常使用什么框架基本上都是先引入他的依赖,Jsoup也不例外我们在maven中引入我们的Jsoup依赖
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.1</version>
</dependency>
2、演示简单的demo
我们在引入依赖之后我们就可以创建一个Java类开始操作一个简单的获取数据代码
@Test
public void test() throws IOException {
//设置商品链接
String url = "https://search.xxx.com/Search";
try {
//发送HTTP请求获取页面内容
Document doc = Jsoup.connect(url).get();
//解析D0获取商品信息
Elements goods = doc.select(".gl-item");
for (Element element : goods) {
//获取商品名称
String name = element.select(".p-name").text();
//获取商品价格
String price = element.select(".p-price").text();
//输出商品信息
System.out.println("商品名称:" + name);
System.out.println("商品价格:" + price);
System.out.println("---");
}
} catch (IOException e) {
e.printStackTrace();
}
}
这段代码就是一段演示的demo,我们传进去一个URL,然后设置连接,在使用get方法就可以获取一个Document的,然后从里面获取元素或者是标签都可以了,
3、进阶版本
我们上面的是一个简单的demo,平常不是很推荐使用,我们最好是根据网址接口的C_URL命令进行设置请求头的方式来获取Document,因为使用上面的方法有时候是获取不到Document,
3.1、如何获取C_URL命令

3.2、获取之后如何使用
我们在获取到这个命令之后可以粘贴到postman上进行发送请求,然后根据请求头设置我们代码里面的请求头,下面是一段演示代码
private static Document getOneAndTotalDocument(String url) {
for (int i = 1; i <= Constant.MAX_ERROR_3; i++) {
try {
Document document = Jsoup.connect(url)
.header("authority", "xxx.xxxx.xxxx")
.header("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
.header("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
.header("cache-control", "max-age=0")
.header("if-none-match", "W/\"cacheable:a697d3df8be1f56329f78f02f21727ef\"")
.header("referer", "https://converse.co.jp/collections/shoes?page=2")
.header("sec-ch-ua", "\"Not_A Brand\";v=\"99\", \"Chromium\";v=\"96\", \"Microsoft Edge\";v=\"96\"")
.header("sec-ch-ua-mobile", "?0")
.header("sec-ch-ua-platform", "Windows")
.header("sec-fetch-dest", "document")
.header("sec-fetch-mode", "navigate")
.header("sec-fetch-site", "same-origin")
.header("sec-fetch-user", "?1")
.header("upgrade-insecure-requests", "1")
.header("user-agent", RequestUtils.randomUserAgent())
.get();
return document;
} catch (Exception e) {
if (i == Constant.MAX_ERROR_3) {
e.printStackTrace();
log.error("抓取失败!相关链接{},错误信息{}", url, e.getMessage());
}
}
}
return null;
}
4、获取Document之后操作
我们获取Document之后一切都简单了,我们可以从里面获取各种所看到的页面数据,只要是页面上有的我们基本上都可以获取到,下面是我写的一部分代码,可以进行参考
private static List<ConverseVo> getConverseSkuDetail(String spuUrl) {
List<ConverseVo> converseList = new ArrayList<>();
Document document = getSkuDttailDocument(spuUrl);
String Kids = document.select(".breadcrumb a").text();
if (Kids.contains(CHILDREN) || Kids.contains(BABY)) {
return null;
}
Elements elements = document.select(".product-single__variants.no-js option");
List<ConverseVo> voList = elements.stream().map(element -> {
ConverseVo converseVo = new ConverseVo();
String value = element.attr("value");
if (!StringUtils.hasText(value)) {
return null;
} else {
String priceAndProperties = element.select("option").text();
String[] splits = priceAndProperties.split("-");
String split = splits[0];
String Properties = split.split(" ")[0];
converseVo.setProperties(Properties);
String price = splits[1].replaceAll("[^\\d]", "");
converseVo.setPriceReplace(price);
String valueId = element.attr("value");
converseVo.setId(valueId);
}
String Discounts = document.select(".product__price-savings").text();
if (StringUtils.hasText(Discounts)) {
String[] split = Discounts.split("%");
BigDecimal discount = BigDecimal.valueOf(Integer.valueOf(split[0]));
BigDecimal discounts = discount == null || discount.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ONE : BigDecimal.valueOf(100).subtract(discount).multiply(BigDecimal.valueOf(0.01));
converseVo.setDiscount(discounts);
} else {
converseVo.setDiscount(BigDecimal.valueOf(1));
}
converseVo.setHreFurl(spuUrl);
Pattern pattern = Pattern.compile("[^/]+$");
Matcher matcher = pattern.matcher(spuUrl);
if (matcher.find()) {
String output = matcher.group();
converseVo.setArticleNumber(output);
}
return converseVo;
}).filter(Objects::nonNull).collect(Collectors.toList());
List<String> volueList = voList.stream().map(ConverseVo::getId).collect(Collectors.toList());
Elements stockElements = document.select(".hide.js-product-inventory-data div");
HashMap<String, String> stockMap = new HashMap<>();
for (Element element : stockElements) {
String dataId = element.attr("data-id");
String stock = element.attr("data-quantity");
stockMap.put(dataId, stock);
}
ArrayList<String> dataIds = new ArrayList<>(stockMap.keySet());
Collection<String> intersection = CollectionUtils.retainAll(volueList, dataIds);
if (!CollectionUtils.isEmpty(intersection)) {
voList.forEach(item -> {
ConverseVo converseVo = new ConverseVo();
converseVo.setArticleNumber(item.getArticleNumber());
converseVo.setHreFurl(item.getHreFurl());
converseVo.setPriceReplace(item.getPriceReplace());
converseVo.setProperties(item.getProperties());
converseVo.setId(item.getId());
converseVo.setDiscount(item.getDiscount());
if (intersection.contains(item.getId())) {
String stock = stockMap.get(item.getId());
converseVo.setStock(stock);
}
converseList.add(converseVo);
});
}
return converseList;
}
5、使用RestTemplate调用第三方接口
上面了是一种最平常使用Document来对数据的获取数据,其实我们可以在网页页面找到第三方接口,我们使用RestTemplate来进行调用然后获取Json数据然后进行封装一个Vo进行返回,下面是一个使用RestTemplate调用第三方接口的方法,可以进行参考
private String postStock(String pid) {
String url = "https://www.XXXX.co.XXX/on/demandware.store/Sites-tbl-jp-Site/ja_JP/Cart-AddProduct";
HttpHeaders headers = new HttpHeaders();
headers.add("authority", "www.XXXX.co.XXXX");
headers.add("accept", "*/*");
headers.add("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
headers.add("content-type", "application/x-www-form-urlencoded; charset=UTF-8");
headers.add("origin", "https://www.XXXX.co.XXX");
JSONObject entries = new JSONObject();
entries.put("pid", pid);
entries.put("quantity", "1");
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("pid", entries.getStr("pid"));
formData.add("quantity", entries.getStr("quantity"));
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(formData, headers);
ResponseEntity<JSONObject> response = restTemplate.exchange(url, HttpMethod.POST, entity, JSONObject.class);
JSONObject object = response.getBody();
Integer stock = getMaxStock(object, pid);
if (stock == null) {
return null;
}
String string = stock.toString();
return string;
}
6、技术总结以及注意事项
6.1、注意事项
在整个代码里面没有比较突出的技术,也就是根据Jsoup框架官网跑的demo,稍微注意一点的就是有个别的朋友在按照我的方法获取C_URL的命令放在poatman上跑不成功的原因大概有一点,是postman版本过低,那就需要获取上面我给大家截图如何获取C_URL命令标注的的下面那个,然后使用postman的导入进行发送请求,poatman的如下,这个只是postman上的问题,不影响别的,

6.2、方法调用
使用RestTemplate调用第三方接口,因为我之前使用了hutool的工具类发送请求,以及Http的那个,但是用着不是很方便,而且这个RestTemplate有一个很方便的地方就是可以自定义返回结果类型,在这里先说一下简单的使用,直接注入RestTemplate就可以了,其余什么都不用,然后也可以进行一些配置,下一篇文章讲解
6.3、优化类容
至于优化类容也就是方法的一些优化,比如使用Lambda表达式什么的,不要使用for循环,该判空判空,如果有人获取的网址数据比较慢,个人建议使用多线程去执行任务,看公司业务,使用多线程也要注意任务数量,否则会出现一些问题
最后如果大家有问题的话或者好的建议,大家可以私信或者评论指出
本文详细介绍了如何使用Java的Jsoup库进行网页数据抓取,包括环境准备、基本的抓取示例、根据C_URL定制请求头、使用RestTemplate调用第三方接口,以及优化和注意事项,适合IT开发者学习和实践爬虫技术。

5750

被折叠的 条评论
为什么被折叠?



