ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

使用jsoup来实现一个简单的java爬虫

2022-02-15 17:03:11  阅读:190  来源: 互联网

标签:钓场 fishery java String area url 爬虫 jsoup select


事件起源

昨天摸了一天的鱼,下午快下班的时候,突然来活了,说要爬取钓友之家的钓场数据!什么?爬虫?之前一直没写过啊啊!为了保住自己的饭碗,赶紧打开百度,开始了自己第一个爬虫程序之旅!

概念

什么是爬虫?
答:简单来讲,爬虫就是一个探测机器,它的基本操作就是模拟人的行为去各个网站溜达,点点按钮,查查数据,或者把看到的信息背回来。就像一只虫子在一幢楼里不知疲倦地爬来爬去。

了解了爬虫的概念之后,我随便翻了几篇博客,发现大部分的人都是用jsoup来实现爬虫的。简单的瞄了一下代码,发现jsoup使用起来还蛮简单,于是我就决定使用jsoup来编写我的第一个爬虫程序!

实战演练

1.需求分析

我接到的任务需求是这样的,爬取钓鱼之家上常州每个钓场的名称、区域、是否收费、收费规则以及描述等数据。这里我了博客写起来方便,我简化一下需求,查询常州所有钓场的数据。

我们打开钓友之家网站,点击钓场,进入如下页面:
钓场之家

随便找个城市点击进去,这里我们按照需求,选择常州,进去之后,我们进入钓场列表页面:

地址栏信息

发现我们需要的钓场名称、收费方式以及描述等数据都能在列表页找到!但是钓场所在区域却没办法直接获取,我们点击进入详情

钓场详情

发现依然没有区域,这可怎么办?

接着,我突然灵机一动,又回到列表页:

列表页区域

列表页有按照区域查询的筛选条件,我们可以先获取所有的区域,再按照每个区域查询出钓场列表,那么每次查询出来的列表都属于我们该次查询时指定的区域!不得不佩服自己的机智!

2.编码

1.新建一个SpringBoot项目,导入相关jsoup依赖

我想SpringBoot大家应该都很熟悉了,怎么新建项目在这里我就不多说了。

项目新建完成之,我们引入jsoup相关依赖

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

2.获取列表页区域信息

在编写代码之前,jsoup中有几个方法需要介绍一下:

  • Document document = Jsoup.connect(url).get();通过该方法可以获取我们的整个页面,我们页面中的所有元素都包含在这个对象里。
  • Element对象,就是我们html页面上的DOM节点
  • Elements列表,DOM节点的集合。
  • Document, Element, Elements对象中的方法都与javascript中的DOM节点方法一样。

我们进入列表页面,F12打开调试,选择Elements分析页面数据:

区域数据elements分析

从中,我们发现我们可以使用 标签的class与标签种类来定位到我们的区域。

@Test
void contextLoads() throws Exception {
    //这个就是博客中的java反射的url
    String url=  "https://www.diaoyu.com/diaochang/changzhou/";
    Document document = Jsoup.connect(url).get();
    // select表示使用css选择器
    // .sort-select li 是一个关系选择器,表示选择class是sort-select的所有li子元素
    Elements elements = document.select(".sort-select li");
    System.out.println(elements);
}

运行测试:

运行结果

我们发现,按照document.select(".sort-select li")选择器筛选出来的条件有三个,对应列表页面的三个查询条件,想要获得区域信息,我们只要拿第一个就可以了。

以下是我们最终代码:

新建一个pojo类,来存放区域信息:

package com.xdw.pojo;

import lombok.Data;

import java.io.Serializable;

@Data
public class Area implements Serializable {

    // 城市名称
    private String cityName;

    // 省
    private String province;

    // 区域名称
    private  String areaName;

    // 区域对应链接
    private String href;

}

编写获取区域代码:

package com.xdw.utils;

import com.xdw.pojo.Area;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("all")
public class JsoupUtils {

    public static List<Area> getAreaList(Document document, String cityName, String provinceName) {
        List<Area> areaList = new ArrayList<>();

        // select表示使用css选择器
        // .sort-select li 是一个关系选择器,表示选择class是sort-select的所有li子元素
        // first()表示选择第一个li,即我们的区域信息
        Element areaElement = document.select(".sort-select li").first();
        // 选择li标签下的dd标签,我们需要的区域名称以及对应的检索链接都在这个DOM节点下
        Elements ddList = areaElement.select("dd");
        if(!CollectionUtils.isEmpty(ddList)) {
            for (Element d : ddList) {
                Area area = new Area();
                area.setAreaName(d.text());
                // 链接位于dd标签下的a标签中
                area.setHref(d.select("a").attr("abs:href"));
                area.setCityName(cityName);
                area.setProvince(provinceName);
                areaList.add(area);
            }
        }
        return areaList;
    }
}

编写测试代码:

@Test
void contextLoads() throws Exception {
    //这个就是博客中的java反射的url
    String url=  "https://www.diaoyu.com/diaochang/changzhou/";
    Document document = Jsoup.connect(url).get();
    List<Area> areaList = JsoupUtils.getAreaList(document, "常州市", "江苏省");
    for (Area area : areaList) {
        System.out.println(area);
    }
}

运行测试:

运行结果

区域列表获取成功。

3.从列表数据中获取钓场信息

进入钓场列表页面,f12进入浏览器调试页面:
列表页

下面开始编写我们的代码:

新建一个钓场类:

package com.xdw.pojo;

import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fishery {

    // 钓场名称
    private String name;

    // 区域名称
    private String areaName;

    // 城市名称
    private String cityName;

    // 省名称
    private String province;

    // 收费方式
    private String priceType;

    // 价格
    private String price;
    
    // 钓场描述
    private String descript;
}

JsoupUtils中新增如下方法,用来获取钓场信息:

 public static List<Fishery> getFisheryListByArea(Area area) {

      List<Fishery> fisheryList =  new ArrayList<>();

      String url = area.getHref();

      try {
          //先获得的是整个页面的html标签页面
          Document doc = Jsoup.connect(url).get();
          // 交集选择器,选出钓场元素所在DOM
          Elements elements = doc.select("ul.article-list").select("li");
          if(!CollectionUtils.isEmpty(elements)) {
              for (Element element : elements) {
                  Fishery fishery = new Fishery();
                  // 设置钓场区域名称
                  fishery.setAreaName(area.getAreaName());
                  fishery.setCityName(area.getCityName());
                  fishery.setProvince(area.getProvince());
                  // 获取钓场名称
                  String name = element.select("dl dt a").text();
                  fishery.setName(name);
                  // 设置钓场描述
                  fishery.setDescript(element.select("dl dd.article-intro").text());
                  // 获取收费类型
                  Elements priceElement = element.select("dl dd.article-charge");
                  if(CollectionUtils.isEmpty(priceElement)) {
                      fishery.setPriceType("免费");
                  } else {
                      fishery.setPriceType("收费");
                      String price = priceElement.select("span").text();
                      fishery.setPrice(price);
                  }

                  fisheryList.add(fishery);
              }
          }
      } catch (Exception e) {
          e.printStackTrace();
      }

      return fisheryList;
  }

编写测试类:

@Test
void contextLoads() throws Exception {
    //这个就是博客中的java反射的url
    String url=  "https://www.diaoyu.com/diaochang/changzhou/";
    Document document = Jsoup.connect(url).get();
    List<Area> areaList = JsoupUtils.getAreaList(document, "常州市", "江苏省");
    List<Fishery> fisheryList = new ArrayList<>();
    if(!CollectionUtils.isEmpty(areaList)) {
        for (Area area : areaList) {
            fisheryList.addAll(JsoupUtils.getFisheryListByArea(area));
        }
    }

    for (Fishery fishery : fisheryList) {
        System.out.println(fishery);
    }
}

运行测试:
结果

我们发现成功获取了钓场数据。

3.代码完善,获取分页数据

问题:刚刚我们只是获得每个区域的第一页数据,我们如何获取后面页的数据呢?

有下一页
尾页

我们发现,当列表页到了尾页时,下一页按钮就会消失,所以我们可以在获取钓场数据方法中添加一个循环,直到没有下一页,才跳出循环!

完善我们的getFisheryListByArea方法如下:

public static List<Fishery> getFisheryListByArea(Area area) {

    List<Fishery> fisheryList =  new ArrayList<>();

    String url = area.getHref();

    try {
        boolean flag = true;
        while(flag) {
            //先获得的是整个页面的html标签页面
            Document doc = Jsoup.connect(url).get();
            // 交集选择器,选出钓场元素所在DOM
            Elements elements = doc.select("ul.article-list").select("li");
            if(!CollectionUtils.isEmpty(elements)) {
                for (Element element : elements) {
                    Fishery fishery = new Fishery();
                    // 设置钓场区域名称
                    fishery.setAreaName(area.getAreaName());
                    fishery.setCityName(area.getCityName());
                    fishery.setProvince(area.getProvince());
                    // 获取钓场名称
                    String name = element.select("dl dt a").text();
                    fishery.setName(name);
                    // 设置钓场描述
                    fishery.setDescript(element.select("dl dd.article-intro").text());
                    // 获取收费类型
                    Elements priceElement = element.select("dl dd.article-charge");
                    if(CollectionUtils.isEmpty(priceElement)) {
                        fishery.setPriceType("免费");
                    } else {
                        fishery.setPriceType("收费");
                        String price = priceElement.select("span").text();
                        fishery.setPrice(price);
                    }
                    fisheryList.add(fishery);
                }
            }

            // 获取页面信息
            Elements pageElements = doc.select(".page a");
            // 没有页面,说明只有一页
            if(CollectionUtils.isEmpty(pageElements)) {
                flag = false;
            } else if(!pageElements.text().contains("下一页")) {
                // 没有下一页,直接跳出循环
                flag = false;
            } else {
                for (Element pe : pageElements) {
                    if(pe.text().equals("下一页")) {
                        // 存在下一页,获取页面信息
                        url = pe.attr("abs:href");
                    }
                }
            }

            if(!flag) {
                break;
            }
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return fisheryList;
}

至此,我们的爬虫大功告成!

我擦了擦额头的汗水,庆幸保住了的饭碗!

标签:钓场,fishery,java,String,area,url,爬虫,jsoup,select
来源: https://www.cnblogs.com/xshangao/p/15897281.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有