JAVA网络爬虫
HttpClient

导航

 

项目结构图

在这里插入图片描述

配置信息

  1. pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.2.RELEASE</version>
        </parent>
    
        <groupId>com.xiaoge</groupId>
        <artifactId>xiaoge-crawler-jd</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <!-- SpringMVC -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- SpringData Jpa -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
    
            <!-- MySQL链接包 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <!-- HttpClient -->
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
            </dependency>
    
            <!-- jsoup -->
            <dependency>
                <groupId>org.jsoup</groupId>
                <artifactId>jsoup</artifactId>
                <version>1.10.3</version>
            </dependency>
    
            <!-- 工具包 -->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
        </dependencies>
    
    </project>
    
  2. application.properties

    # DB Configuration
    spring.datasource.driverClassName=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/crawler
    spring.datasource.username=root
    spring.datasource.password=123456
    
    # JPA Configuration
    spring.jpa.database=mysql
    spring.jpa.show-sql=true
    

启动类

  1. ItemApplication

    package com.xiaoge.jd;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    /**
     * @Author: 潇哥
     * @DateTime: 2020/9/29 下午9:07
     * @Description: TODO
     */
    @SpringBootApplication
    // 使用定时任务, 需要先开启定时任务, 需要添加注解
    @EnableScheduling
    public class ItemApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ItemApplication.class);
        }
    
    }
    

实体类

  1. Item

    package com.xiaoge.jd.pojo;
    
    import lombok.Data;
    
    import javax.persistence.*;
    import java.util.Date;
    
    /**
     * @Author: 潇哥
     * @DateTime: 2020/9/29 下午8:39
     * @Description: TODO
     */
    @Data
    @Entity // 声明它是一个实体
    @Table(name = "jd_item") // 数据库表与该实体做映射
    public class Item {
    
        // 主键
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY) // 声明主键的自增方式
        private Long id;
    
        // 标准产品单位 (商品集合)
        private Long spu;
    
        // 库存量单位 (最小品类单元)
        private Long sku;
    
        // 商品标题
        private String title;
    
        // 商品价格
        private Double price;
    
        // 商品图片
        private String pic;
    
        // 商品链接
        private String url;
    
        // 商品创建时间
        private Date created;
    
        // 商品更新时间
        private Date updated;
    
    }
    

持久层

  1. ItemDao

    package com.xiaoge.jd.dao;
    
    import com.xiaoge.jd.pojo.Item;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    /**
     * @Author: 潇哥
     * @DateTime: 2020/9/29 下午8:53
     * @Description: TODO
     */
    // JpaRepository泛型 第一个泛型为实体类名称, 第二个泛型为id类型
    public interface ItemDao extends JpaRepository<Item, Long> {
    }
    

业务层

  1. ItemService

    package com.xiaoge.jd.service;
    
    import com.xiaoge.jd.pojo.Item;
    
    import java.util.List;
    
    /**
     * @Author: 潇哥
     * @DateTime: 2020/9/29 下午8:56
     * @Description: TODO
     */
    public interface ItemService {
    
        /**
         * 保存商品
         */
        public void save(Item item);
    
        /**
         * 根据条件查询商品
         * @param item
         * @return
         */
        public List<Item> findAll(Item item);
    
    }
    
  2. ItemServiceImpl

    package com.xiaoge.jd.service.impl;
    
    import com.xiaoge.jd.dao.ItemDao;
    import com.xiaoge.jd.pojo.Item;
    import com.xiaoge.jd.service.ItemService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Example;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.List;
    
    /**
     * @Author: 潇哥
     * @DateTime: 2020/9/29 下午8:57
     * @Description: TODO
     */
    @Service("itemService")
    public class ItemServiceImpl implements ItemService {
    
        @Autowired
        private ItemDao itemDao;
    
        /**
         * 保存商品
         */
        @Transactional
        @Override
        public void save(Item item) {
            itemDao.save(item);
        }
    
        /**
         * 根据条件查询商品
         * @param item
         * @return
         */
        @Override
        public List<Item> findAll(Item item) {
            // 声明查询条件
            Example<Item> example = Example.of(item);
    
            // 根据查询条件进行查询数据
            List<Item> list = itemDao.findAll(example);
    
            return list;
        }
    }
    

工具类

  1. HttpUtils

    package com.xiaoge.jd.util;
    
    import org.apache.commons.lang3.StringUtils;
    import org.apache.http.HttpEntity;
    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.stereotype.Component;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.UUID;
    
    /**
     * @Author: 潇哥
     * @DateTime: 2020/9/29 下午10:00
     * @Description: TODO
     */
    @Component
    public class HttpUtils {
    
        // 连接池管理
        private PoolingHttpClientConnectionManager cm;
    
        /**
         * 构造方法, 初始化连接池管理工具
         */
        public HttpUtils() {
    
            this.cm = new PoolingHttpClientConnectionManager();
    
            // 设置最大连接数
            this.cm.setMaxTotal(100);
    
            // 设置每个主机的最大连接数
            this.cm.setDefaultMaxPerRoute(10);
    
        }
    
    
        /**
         * 根据请求地址下载页面数据
         * @param url
         * @return
         */
        public String doGetHtml(String url) {
            // 创建HttpClient
            CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
    
            // 创建get请求, 访问url地址
            HttpGet httpGet = new HttpGet(url);
            // 添加头信息, 让京东知道你是普通用户, 用电脑设备访问的, 而不是用程序访问的
            httpGet.addHeader( "user-agent", "Mozilla/5.0" );
    
            // 设置请求信息
            httpGet.setConfig(this.config());
    
            CloseableHttpResponse response = null;
    
            try {
                // 使用HttpClient发请请求, 获取响应
                response = httpClient.execute(httpGet);
    
                // 解析响应, 返回结果
                if (response.getStatusLine().getStatusCode() == 200) {
                    // 判断响应体Entity是否不为空, 如果不为空可以使用EntityUtils
                    if (response.getEntity() != null) {
                        // 获取响应体数据, 并转换成字符串
                        String content = EntityUtils.toString(response.getEntity(), "utf8");
                        return content;
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            // 响应体为空, 返回空字符串
            return "";
        }
    
    
        /**
         * 根据请求地址下载图片
         * @param url
         * @return
         */
        public String doGetImage(String url) {
            // 创建HttpClient
            CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(this.cm).build();
    
            // 创建HttpGet请求毒性, 设置url地址
            HttpGet httpGet = new HttpGet(url);
            // 添加头信息, 让京东知道你是普通用户, 用电脑设备访问的, 而不是用程序访问的
            httpGet.addHeader( "user-agent", "Mozilla/5.0" );
    
            // 设置请求信息
            httpGet.setConfig(this.config());
    
            CloseableHttpResponse response = null;
    
            try {
                // 使用HttpClient发请请求, 获取响应
                response = httpClient.execute(httpGet);
    
                // 解析响应, 返回结果
                if (response.getStatusLine().getStatusCode() == 200) {
                    // 判断响应体Entity是否不为空, 如果不为空可以使用EntityUtils
                    if (response.getEntity() != null) {
                        // 获取图片后缀
                        String extName = StringUtils.substringAfterLast(url, ".");
    
                        // 创建图片, 重命名图片
                        String picName = UUID.randomUUID().toString().concat(".").concat(extName);
    
                        // 声明OutputStream
                        OutputStream outputStream = new FileOutputStream(new File("/Users/xiaoge/Desktop/images/" + picName));
    
                        // 把响应体内容写入到这个流中
                        response.getEntity().writeTo(outputStream);
    
                        // 返回图片名
                        return picName;
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            // 响应体为空, 返回空字符串
            return "";
        }
    
    
        /**
         * 设置请求信息
         * @return
         */
        private RequestConfig config() {
    
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(1000) // 创建连接的最长时间
                    .setConnectionRequestTimeout(500) // 获取链接的最长时间
                    .setSocketTimeout(10000) // 数据传输的最长时间
                    .build();
            return requestConfig;
    
        }
    
    
    }
    

定时任务

  1. ItemTask

    package com.xiaoge.jd.task;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.xiaoge.jd.pojo.Item;
    import com.xiaoge.jd.service.ItemService;
    import com.xiaoge.jd.util.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.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    import org.springframework.util.StringUtils;
    
    import java.util.Date;
    import java.util.List;
    
    /**
     * @Author: 潇哥
     * @DateTime: 2020/9/29 下午10:37
     * @Description: TODO
     */
    @Component
    public class ItemTask {
    
        @Autowired
        private HttpUtils httpUtils;
    
        @Autowired
        private ItemService itemService;
    
        private static final ObjectMapper MAPPER = new ObjectMapper();
    
        // 当下载任务完成后, 间隔多长时间进行下一次的任务.  这里 单位/毫秒  100*1000=100秒
        @Scheduled(fixedDelay = 100 * 1000)
        public void itemTask() throws Exception {
    
            String url = "https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&wq=%E6%89%8B%E6%9C%BA&s=51&click=0&page=";
    
            // 按照页面对手机的搜索结果进行遍历解析
            for(int i = 1; i < 10; i += 2) {
                String html = this.httpUtils.doGetHtml(url + i);
    
                // 解析页面, 获取商品数据并保存
                this.parse(html);
    
            }
    
            System.out.println("页面爬取完成!");
    
        }
    
    
        // 解析页面, 获取商品数据并保存
        public void parse(String html) throws Exception {
    
            // 获取document对象
            Document document = Jsoup.parse(html);
    
            // 获取spu信息
            Elements spuEles = document.select("div#J_goodsList > ul > li");
    
            for (Element spuEle : spuEles) {
    
                // 获取spu
                String spuStr = spuEle.attr("data-spu");
                long spu = StringUtils.isEmpty(spuStr) == true ? 0: Long.parseLong(spuStr);
    
                // 获取sku信息
                Elements skuEles = spuEle.select("li.ps-item");
    
                for (Element skuEle : skuEles) {
                    // 获取sku
                    long sku = Long.parseLong(skuEle.select("img[data-sku]").attr("data-sku"));
    
                    // 根据sku查询商品数据
                    Item item = new Item();
                    item.setSku(sku);
    
                    // 查询数据库是否有该商品, 有跳过, 没有保存进数据库
                    List<Item> itemList = this.itemService.findAll(item);
                    if(!CollectionUtils.isEmpty(itemList)) {
                        continue;
                    }
    
                    // 设置商品的spu
                    item.setSpu(spu);
    
                    // 获取商品的详细链接
                    String itemUrl = "https://item.jd.com/"+ sku +".html";
                    item.setUrl(itemUrl);
    
                    // 获取商品图片
                    String picUrl = "https:" + skuEle.select("img[data-sku]").attr("data-lazy-img");
                    picUrl = picUrl.replace("/n7/", "/n1/");
                    String picName = this.httpUtils.doGetImage(picUrl);
                    item.setPic(picName);
    
                    // 获取商品价格
                    String priceJson = this.httpUtils.doGetHtml("https://p.3.cn/prices/mgets?skuIds=J_" + sku);
                    double price = MAPPER.readTree(priceJson).get(0).get("p").asDouble();
                    item.setPrice(price);
    
                    // 获取商品标题
                    String itemInfo = this.httpUtils.doGetHtml(item.getUrl());
                    Document itemDocument = Jsoup.parse(itemInfo);
                    String title = itemDocument.select("div.sku-name").first().text();
                    item.setTitle(title);
    
                    // 商品创建时间
                    item.setCreated(new Date());
    
                    // 商品更新时间
                    item.setUpdated(item.getCreated());
    
                    // 保存进数据库
                    this.itemService.save(item);
                }
    
            }
        }
    
    }
    

posted on 2020-10-02 21:03  gmlgxx  阅读(44)  评论(0)    收藏  举报