栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA网络爬爬学习之HttpClient+Jsoup

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA网络爬爬学习之HttpClient+Jsoup

JAVA网络爬爬学习
  • HttpClient用法简单整理
    • GET请求
      • 无参
      • 带参
    • POST请求
      • 无参
      • 带参
    • 连接池
    • 请求request的相关配置
    • httpclient用法详解
  • Jsoup用法简单整理
    • jsoup解析
      • 解析URL
      • 解析字符串
      • 解析文件
    • 使用dom方式遍历文档
    • 使用选择器语法查找元素
    • Selector选择器概述
    • Selector选择器组合使用
    • Jsoup参考资料
  • 爬虫案例
    • 开发准备
  • 封装HttpClient
  • 实现数据抓取
  • 爬虫演示
  • 错误记录
  • gitee源码链接


HttpClient用法简单整理

引入HttpClient和日志依赖

    

        
            org.apache.httpcomponents
            httpclient
            4.5.13
        
        
        
            org.slf4j
            slf4j-log4j12
            1.7.25
        
    

GET请求 无参
public class Main
{
    public static void main(String[] args)
    {
        //1.创建默认的HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2.创建HttpGet请求
        HttpGet httpGet=new HttpGet("https://blog.csdn.net/m0_53157173");

        try(
                //3.使用HttpClient发请求
            CloseableHttpResponse  response = httpClient.execute(httpGet);)
        {
            //判断响应状态码是否为200
            if(response.getStatusLine().getStatusCode()==200)
            {
                //如果为200表示请求成功,获取返回数据
                HttpEntity entity = response.getEntity();
                //使用工具类
                String content = EntityUtils.toString(entity,"UTF-8");
                //打印数据内容
                System.out.println(content);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


带参


另一种写法:

       //带参
        URIBuilder urIBuilder=new URIBuilder("https://blog.csdn.net/m0_53157173/article/details/121876392");
           urIBuilder.setParameter("name","大忽悠").setParameter("age","18");
        //2.创建HttpPOST请求
        HttpGet httpGet=new HttpGet(urIBuilder.build());

POST请求 无参


带参

        //2.创建HttpPOST请求
        HttpPost httpPost=new HttpPost();
        //声明存放参数的List集合
        List params = new ArrayList();
        params.add(new BasicNamevaluePair("keys", "java"));
        //创建表单数据Entity
        UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(params, "UTF-8");
        //设置表单Entity到httpPost请求对象中
        httpPost.setEntity(formEntity);

连接池

如果每次请求都要创建HttpClient,会有频繁创建和销毁的问题,可以使用连接池来解决这个问题。

public class Main
{
    public static void main(String[] args) {
        //连接池管理器
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

        //设置最大连接数
        cm.setMaxTotal(200);

        //设置每个主机的并发数
        cm.setDefaultMaxPerRoute(20);

        doGet(cm);

        doGet(cm);

    }

    private static void doGet(PoolingHttpClientConnectionManager cm) {
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

        HttpGet httpGet = new HttpGet("https://www.baidu.com");

        CloseableHttpResponse response = null;

        try {
            response = httpClient.execute(httpGet);

            // 判断状态码是否是200
            if (response.getStatusLine().getStatusCode() == 200) {
                // 解析数据
                String content = EntityUtils.toString(response.getEntity(), "UTF-8");
                System.out.println(content.length());
            }


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //释放连接
            if (response == null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //不能关闭HttpClient
                //httpClient.close();
            }
        }
    }
}

请求request的相关配置

有时候因为网络,或者目标服务器的原因,请求需要更长的时间才能完成,我们需要自定义相关时间

public static void main(String[] args) throws IOException {
    //创建HttpClient对象
 CloseableHttpClient httpClient = HttpClients.createDefault();

    //创建HttpGet请求
    HttpGet httpGet = new HttpGet("http://www.itcast.cn/");

    //设置请求参数
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(1000)//设置创建连接的最长时间
            .setConnectionRequestTimeout(500)//设置获取连接的最长时间
            .setSocketTimeout(10 * 1000)//设置数据传输的最长时间
            .build();

    httpGet.setConfig(requestConfig);

    CloseableHttpResponse response = null;
    try {
        //使用HttpClient发起请求
        response = httpClient.execute(httpGet);

        //判断响应状态码是否为200
        if (response.getStatusLine().getStatusCode() == 200) {
            //如果为200表示请求成功,获取返回数据
            String content = EntityUtils.toString(response.getEntity(), "UTF-8");
            //打印数据长度
            System.out.println(content);
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //释放连接
        if (response == null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            httpClient.close();
        }
    }
}

httpclient用法详解

HttpClient详细使用示例

111

HttpClient用法–这一篇全了解(内含例子)

HttpClient高并发-httpClient连接池

httpclient架构原理介绍 & 连接池详解


Jsoup用法简单整理

我们抓取到页面之后,还需要对页面进行解析。可以使用字符串处理工具解析页面,也可以使用正则表达式,但是这些方法都会带来很大的开发成本,所以我们需要使用一款专门解析html页面的技术。

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

jsoup的主要功能如下:

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

先加入依赖:


    org.jsoup
    jsoup
    1.10.3



    junit
    junit
    4.12



    org.apache.commons
    commons-lang3
    3.7


    commons-io
    commons-io
    2.6


jsoup解析 解析URL

Jsoup可以直接输入url,它会发起请求并获取数据,封装为document对象

public class Main
{
    public static void main(String[] args) throws IOException {
        //解析url地址
        document document = Jsoup.parse(new URL("https://www.baidu.com/"), 1000);

        //获取title的内容
        Element title = document.getElementsByTag("title").first();
        System.out.println(title.text());
    }
}


解析字符串

先准备以下html文件




    

    
    
    
    
    测试文件
    
    







String fileToString = FileUtils.
readFileToString(new File("C:\\Users\\zdh\\Desktop\\test.html"), Charset.defaultCharset());
        document document = Jsoup.parse(fileToString);

        System.out.println(document.body());

解析文件
      //解析文件
        document document = Jsoup.parse(new File("C:\Users\zdh\Desktop\test.html"), "UTF-8");

        String html = document.getElementsByTag("title").first().html();
        System.out.println(html);

使用dom方式遍历文档

元素获取

  • 1.根据id查询元素getElementById
  • 2.根据标签获取元素getElementsByTag
  • 3.根据class获取元素getElementsByClass
  • 4.根据属性获取元素getElementsByAttribute
//1.    根据id查询元素getElementById
Element element = document.getElementById("city_bj");

//2.   根据标签获取元素getElementsByTag
element = document.getElementsByTag("title").first();

//3.   根据class获取元素getElementsByClass
element = document.getElementsByClass("s_name").last();

//4.   根据属性获取元素getElementsByAttribute
element = document.getElementsByAttribute("abc").first();
element = document.getElementsByAttributevalue("class", "city_con").first();

元素中获取数据

  • 1.从元素中获取id
  • 2.从元素中获取className
  • 3.从元素中获取属性的值attr
  • 4.从元素中获取所有属性attributes
  • 5.从元素中获取文本内容text
//获取元素
Element element = document.getElementById("test");

//1.   从元素中获取id
String str = element.id();

//2.   从元素中获取className
str = element.className();

//3.   从元素中获取属性的值attr
str = element.attr("id");

//4.   从元素中获取所有属性attributes
str = element.attributes().toString();

//5.   从元素中获取文本内容text
str = element.text();

使用选择器语法查找元素

jsoup elements对象支持类似于CSS (或jquery)的选择器语法,来实现非常强大和灵活的查找功能。这个select 方法在document, Element,或Elements对象中都可以使用。且是上下文相关的,因此可实现指定元素的过滤,或者链式选择访问。

Select方法将返回一个Elements集合,并提供一组方法来抽取和处理结果。


Selector选择器概述
  • tagname: 通过标签查找元素,比如:span
  • #id: 通过ID查找元素,比如:# city_bj
  • .class: 通过class名称查找元素,比如:.class_a
  • [attribute]: 利用属性查找元素,比如:[abc]
  • [attr=value]: 利用属性值来查找元素,比如:[class=s_name]
//tagname: 通过标签查找元素,比如:span
Elements span = document.select("span");
for (Element element : span) {
    System.out.println(element.text());
}

//#id: 通过ID查找元素,比如:#city_bjj
String str = document.select("#city_bj").text();

//.class: 通过class名称查找元素,比如:.class_a
str = document.select(".class_a").text();

//[attribute]: 利用属性查找元素,比如:[abc]
str = document.select("[abc]").text();

//[attr=value]: 利用属性值来查找元素,比如:[class=s_name]
str = document.select("[class=s_name]").text();

Selector选择器组合使用
  • el#id: 元素+ID,比如: h3#city_bj
  • el.class: 元素+class,比如: li.class_a
  • el[attr]: 元素+属性名,比如: span[abc]
  • 任意组合: 比如:span[abc].s_name
  • ancestor child: 查找某个元素下子元素,比如:.city_con li 查找"city_con"下的所有li
  • parent > child: 查找某个父元素下的直接子元素,比如:
  • .city_con > ul > li 查找city_con第一级(直接子元素)的ul,再找所有ul下的第一级li
  • parent > *: 查找某个父元素下所有直接子元素
//el#id: 元素+ID,比如: h3#city_bj
String str = document.select("h3#city_bj").text();

//el.class: 元素+class,比如: li.class_a
str = document.select("li.class_a").text();

//el[attr]: 元素+属性名,比如: span[abc]
str = document.select("span[abc]").text();

//任意组合,比如:span[abc].s_name
str = document.select("span[abc].s_name").text();

//ancestor child: 查找某个元素下子元素,比如:.city_con li 查找"city_con"下的所有li
str = document.select(".city_con li").text();

//parent > child: 查找某个父元素下的直接子元素,
//比如:.city_con > ul > li 查找city_con第一级(直接子元素)的ul,再找所有ul下的第一级li
str = document.select(".city_con > ul > li").text();

//parent > * 查找某个父元素下所有直接子元素.city_con > *
str = document.select(".city_con > *").text();

Jsoup参考资料

Jsoup

2

开源中国

Jsoup学习总结

Jsoup详解

【Java爬虫】Jsoup


爬虫案例

首先访问京东,搜索手机,分析页面,我们抓取以下商品数据:商品图片、价格、标题、商品详情页

SPU和SKU
除了以上四个属性以外,我们发现上图中的苹果手机有四种产品,我们应该每一种都要抓取。那么这里就必须要了解spu和sku的概念

SPU = Standard Product Unit (标准产品单位)
SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。

例如上图中的苹果手机就是SPU,包括红色、深灰色、金色、银色
SKU=stock keeping unit(库存量单位)
SKU即库存进出计量的单位, 可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。在服装、鞋类商品中使用最多最普遍。

例如上图中的苹果手机有几个款式,红色苹果手机,就是一个sku

查看页面的源码也可以看出区别


开发准备

根据需求,建立对应的数据库

CREATE TABLE `jd_item` (
  `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `spu` bigint(15) DEFAULT NULL COMMENT '商品集合id',
  `sku` bigint(15) DEFAULT NULL COMMENT '商品最小品类单元id',
  `title` varchar(100) DEFAULT NULL COMMENT '商品标题',
  `price` bigint(10) DEFAULT NULL COMMENT '商品价格',
  `pic` varchar(200) DEFAULT NULL COMMENT '商品图片',
  `url` varchar(200) DEFAULT NULL COMMENT '商品详情地址',
  `created` datetime DEFAULT NULL COMMENT '创建时间',
  `updated` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `sku` (`sku`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='京东商品表';

依赖引入



    4.0.0

    spring-boot-parent
    org.springframework.boot
    2.0.4.RELEASE


    org.example
    Pa
    1.0-SNAPSHOT

    
        8
        8
    

    

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.apache.httpcomponents
            httpclient
            4.5.13
        
        
        
            org.slf4j
            slf4j-log4j12
            1.7.25
        
        
        
            org.jsoup
            jsoup
            1.10.3
        
        
        
            junit
            junit
            4.12
        
        
        
            org.apache.commons
            commons-lang3
            3.7
        
        
            commons-io
            commons-io
            2.6
        



        
        
            mysql
            mysql-connector-java
            runtime
        
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.3.1
        

        
        
            com.baomidou
            mybatis-plus-generator
            3.4.1
        
        
            org.apache.velocity
            velocity-engine-core
            2.0
        


        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
        
            org.junit.jupiter
            junit-jupiter
            RELEASE
            test
        
        
            org.projectlombok
            lombok
        
    



mybaits-plus相关配置

  • 配置数据源
spring:
  datasource:                        #是否使用安全连接
    #mysal 8驱动不同com.mysql.cj.jdbc.Driver,还需要增加时区的配置 serverTimezone=GMT%2B8
    url: jdbc:mysql://localhost:3306/xxx?userSSL=false&useUnicode=true&characterEncoding=utf-8
    username: root
    password: xxx
    driver-class-name: com.mysql.jdbc.Driver
  • MP代码生成器
//MP代码生成器
@SpringBootTest(classes = Main.class,webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ExtendWith(SpringExtension.class)
public class Generator
{
    //代码生成的文件路径                  当前系统的目录
    private final String outPutDir=System.getProperty("user.dir")+"/src/main/java/dhy/com";
    @Autowired
    private HttpUtils httpUtils;
    //数据库驱动名
    @Value("${spring.datasource.driver-class-name}")
    private String dbDriverClassName;
    //数据库地址
    @Value("${spring.datasource.url}")
    private String dbUrl;
    //用户名
    @Value("${spring.datasource.username}")
    private String dbUsername;
    //密码
    @Value("${spring.datasource.password}")
    private String dbPassword;

    @Test
   public void CodeAutoGenerated()
   {


       //1.全局策略配置
       GlobalConfig config = new GlobalConfig();
       config.setAuthor("大忽悠")//作者
               .setOutputDir(outPutDir)//生成路径
               .setFileOverride(true)//文件覆盖
               .setIdType(IdType.AUTO)//主键策略
               .setServiceName("%sService")//设置生成service接口名字的首字母是否为I(默认会生成I开头的IStudentService)
               .setbaseResultMap(true)//自动SQL映射文件,生成基本的ResultMap
               .setbaseColumnList(true);//生成基本的SQL片段

       //2.数据源配置
       DataSourceConfig dataSourceConfig = new DataSourceConfig();
       dataSourceConfig.setDbType(DbType.MYSQL)//设置数据库类型
               .setDriverName(dbDriverClassName)
               .setUrl(dbUrl)//数据库地址
               .setUsername(dbUsername)//数据库名字
               .setPassword(dbPassword);//数据库密码


       //3.策略配置
       StrategyConfig strategy = new StrategyConfig();
       strategy.setCapitalMode(true)//全局大写命名
               .setNaming(NamingStrategy.underline_to_camel)//数据库表映射到实体的命名策略
               .setColumnNaming(NamingStrategy.underline_to_camel)//列的命名也支持驼峰命名规则
               .setTablePrefix("jd_")//数据库表的前缀
               .setInclude("jd_item")//设置要映射的表名,这里可以写多个
               .setEntityLombokModel(true)  //使用Lombok开启注解
               .setControllerMappingHyphenStyle(true);//controller层,开启下划线url : //localhost:8080/hello_id_2

       //4.包名策略
       PackageConfig packageConfig = new PackageConfig();
       packageConfig
              // .setModuleName("generator")
               .setParent("com")//所放置的包(父包)
               .setMapper("mapper")//Mapper包
               .setService("service")//服务层包
               .setController("controller")//控制层
               .setEntity("beans")//实体类
               .setXml("mapper");//映射文件
       //5.整合配置
       AutoGenerator autoGenerator = new AutoGenerator();
       autoGenerator.setGlobalConfig(config)
               .setDataSource(dataSourceConfig)
               .setStrategy(strategy)
               .setPackageInfo(packageConfig);
       //6.执行
       autoGenerator.execute();
   }
}

  • 实体类增加自动填充字段功能

  • 自定义填充策略
@Component//填充处理器MymetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入
public class MymetaObjectHandler implements metaObjectHandler
{
    //插入时填充策略
    @Override
    public void insertFill(metaObject metaObject) {
        this.setFieldValByName("created", LocalDateTime.now(),metaObject);
        this.setFieldValByName("updated",LocalDateTime.now(),metaObject);
    }

    //更新时填充策略
    @Override
    public void updateFill(metaObject metaObject) {
        this.setFieldValByName("updated",new Date(),metaObject);
    }
}

封装HttpClient
  • HttpClientUtils
package dhy.com.utils;

import org.apache.http.Header;
import org.apache.http.client.config.RequestConfig;
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.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;


import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.UUID;

@Component
public class HttpUtils {

    //连接池管理器
    private PoolingHttpClientConnectionManager cm;

    //输出的文件地址
    @Value("${image.file.path}")
    private String outputFilePath;

    public HttpUtils() {
        this.cm = new PoolingHttpClientConnectionManager();
        //设置最大连接数
        cm.setMaxTotal(200);
        //设置每个主机的并发数
        cm.setDefaultMaxPerRoute(20);
    }

    //获取内容
    public String getHtml(String url) {

        // 获取HttpClient对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

        // 声明httpGet请求对象
        HttpGet httpGet = new HttpGet(url);
        // 设置请求参数RequestConfig
        httpGet.setConfig(this.getConfig());
       //模拟浏览器访问行为
        httpGet.setHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0");

        CloseableHttpResponse response = null;
        try {
            // 使用HttpClient发起请求,返回response
            response = httpClient.execute(httpGet);
            // 解析response返回数据
            if (response.getStatusLine().getStatusCode() == 200) {
                String html = "";

                // 如果response.getEntity获取的结果是空,在执行EntityUtils.toString会报错
                // 需要对Entity进行非空的判断
                if (response.getEntity() != null) {
                    html = EntityUtils.toString(response.getEntity(), "UTF-8");
                }

                return html;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    // 关闭连接
                    response.close();
                }
                // 不能关闭,现在使用的是连接管理器
                // httpClient.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    //获取图片
    public String getImage(String url) {
        // 获取HttpClient对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

        // 声明httpGet请求对象
        HttpGet httpGet = new HttpGet(url);
        // 设置请求参数RequestConfig
        httpGet.setConfig(this.getConfig());

        CloseableHttpResponse response = null;
        try {
            // 使用HttpClient发起请求,返回response
            response = httpClient.execute(httpGet);
            // 解析response下载图片
            if (response.getStatusLine().getStatusCode() == 200&&response.getEntity()!=null)
            {

                Header contentType = response.getEntity().getContentType();
                String name = contentType.getName();//key: contentType
                String value = contentType.getValue();//value: MediaType类型
                //如果不是图片
                if(!value.equals(MediaType.IMAGE_JPEG_VALUE)&&!value.equals(MediaType.IMAGE_PNG_VALUE))
                {
                    return null;
                }
                // 获取文件类型
                String extName = url.substring(url.lastIndexOf("."));
                // 使用uuid生成图片名
                String imageName = UUID.randomUUID().toString() + extName;
                // 声明输出的文件
                File file = new File(outputFilePath + imageName);
                OutputStream outstream = new FileOutputStream(file);

                System.out.println(file.getAbsolutePath());
                System.out.println(file.exists());

                // 使用响应体输出文件
                response.getEntity().writeTo(outstream);

                // 返回生成的图片名
                return imageName;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null) {
                    // 关闭连接
                    response.close();
                }
                // 不能关闭,现在使用的是连接管理器
                // httpClient.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    //获取请求参数对象
    private RequestConfig getConfig() {
        RequestConfig config = RequestConfig.custom().setConnectTimeout(1000)// 设置创建连接的超时时间
                .setConnectionRequestTimeout(500) // 设置获取连接的超时时间
                .setSocketTimeout(10000) // 设置连接的超时时间
                .build();

        return config;
    }

}



实现数据抓取
  • 使用定时任务,可以定时抓取最新的数据

商品定位分析:


获取到所有spu商品信息对应的代码为:

     //获取商品数据
        Elements spus = document.select("div#J_goodsList > ul > li");


获取每一个商品对应的唯一spu标识:

  //遍历商品spu数据
        for (Element spuEle : spus) {
            //获取商品spu
            Long spuId = Long.parseLong(spuEle.attr("data-spu"));
            .....
            }


获取商品sku数据

      //获取商品sku数据
            Elements skus = spuEle.select("li.ps-item img");


通过上面获取到的当前img的dom对象,就可以获取到里面每个我们需要的属性了

完整代码:

package dhy.com.sechudle;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import dhy.com.beans.Item;
import dhy.com.service.ItemService;
import dhy.com.utils.HttpUtils;
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.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class ItemTask {

    @Autowired
    private HttpUtils httpUtils;
    @Autowired
    private ItemService itemService;

    public static final ObjectMapper MAPPER = new ObjectMapper();

    //抓取页面的url
    @Value("${clawer.page.url}")
    private String pageUrl;
    //页码
    private final String pageNumStr="&page=";

    //设置定时任务执行完成后,再间隔100秒执行一次
    @Scheduled(fixedDelay = 1000 * 100)
    public void process() throws Exception {
        //对于京东来说,2个页面对应一页,即99和100都对应第50也
        //101对应第51页

        //遍历执行,获取所有的数据
        for (int i = 1; i < 6; i = i + 2) {
            //发起请求进行访问,获取页面数据,先访问第一页
            String html = this.httpUtils.getHtml(pageUrl+pageNumStr + i);
            //解析页面数据,保存数据到数据库中
            this.parseHtml(html);

        }
        System.out.println("执行完成");
    }


  //  解析页面,并把数据保存到数据库中
    private void parseHtml(String html) throws Exception {
        //使用jsoup解析页面
        document document = Jsoup.parse(html);

        //获取商品数据
        Elements spus = document.select("div#J_goodsList > ul > li");

        //遍历商品spu数据
        for (Element spuEle : spus) {
            //获取商品spu
            String attr = spuEle.attr("data-spu");
            Long spuId = Long.parseLong(attr.equals("")?"0":attr);

            //获取商品sku数据
            Elements skus = spuEle.select("li.ps-item img");
            for (Element skuEle : skus)
            {
                //获取商品sku
                Long skuId = Long.parseLong(skuEle.attr("data-sku"));

                //判断商品是否被抓取过,可以根据sku判断
                Item param = new Item();
                param.setSku(skuId);
                List list = this.itemService.list(new QueryWrapper().eq("sku",skuId));
                //判断是否查询到结果
                if (list.size() > 0) {
                    //如果有结果,表示商品已下载,进行下一次遍历
                    continue;
                }

                //保存商品数据,声明商品对象
                Item item = new Item();
                //商品spu
                item.setSpu(spuId);
                //商品sku
                item.setSku(skuId);
                //商品url地址
                item.setUrl("https://item.jd.com/" + skuId + ".html");
                //获取商品标题
                String itemHtml = this.httpUtils.getHtml(item.getUrl());
                String title = Jsoup.parse(itemHtml).select("div.sku-name").text();
                item.setTitle(title);

                //获取商品价格
                String priceUrl = "https://p.3.cn/prices/mgets?skuIds=J_"+skuId;

                String priceJson = this.httpUtils.getHtml(priceUrl);
                //解析json数据获取商品价格
                double price = MAPPER.readTree(priceJson).get(0).get("p").asDouble();
                item.setPrice(price);

                //获取图片地址
                String pic = "https:" + skuEle.attr("data-lazy-img").replace("/n9/","/n1/");
                System.out.println(pic);
                //下载图片
                String picName = this.httpUtils.getImage(pic);
                item.setPic(picName);

                //保存商品数据
                this.itemService.save(item);
            }
        }
    }
}

爬虫演示


错误记录


httpClient访问京东网站的时候,需要加上请求头,模拟是浏览器访问,否则京东默认会跳到登录页面

 //模拟浏览器访问行为
        httpGet.setHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0");


gitee源码链接

gitee

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/666635.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号