• java爬虫系列(二)


    copy自:http://www.ayulong.cn/types/2     视频教程:https://www.bilibili.com/video/BV1cE411u7RA?p=1

    虽然前面的知识看着比较零散, 第一次接触到爬虫的我不禁在想这些真的和爬虫有关系吗, 但是事实上前面的知识确实就是爬虫的核心内容, 当这些内容拼接成程序后, 能够获取并保存互联网上的数据, 这就是爬虫了

    抓取部分京东手机信息案例

    1. 环境搭建

    导入数据库

    // 创建crawler数据库, 再创建表
    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='京东商品表';

    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>cn.ayulong</groupId>
        <artifactId>ayulong-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>
                <version>8.0.11</version>
            </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>
        </dependencies>
    </project>

    配置application.yml文件

    spring:
      # 数据库配置 mysql8
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql:///crawler?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: mima
      # jpa配置
      jpa:
        database: MySQL
        show-sql: true

    创建pojo实体类

    package com.ayulong.jd.pojo;
    
    import javax.persistence.*;
    import java.util.Date;
    @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;
        
        // 生成get/set方法
        

    创建Dao以及Service等

    创建Dao接口继承Jpa接口

    package com.ayulong.jd.dao;
    
    import com.ayulong.jd.pojo.Item;
    import org.springframework.data.jpa.repository.JpaRepository;
    // Item的Dao接口
    public interface ItemDao extends JpaRepository<Item, Long> {
    
    }

    创建Service接口

    package com.ayulong.jd.service;
    
    import com.ayulong.jd.pojo.Item;
    
    import java.util.List;
    
    // Item的Service接口
    public interface ItemService {
    
        /**
         * 保存商品
         * @param item
         */
        public void save(Item item);
    
        /**
         * 根据条件查询商品
         * @param item
         * @return
         */
        public List<Item> findAll(Item item);
    }

    实现Service接口

    package com.ayulong.jd.service.impl;
    
    import com.ayulong.jd.dao.ItemDao;
    import com.ayulong.jd.pojo.Item;
    import com.ayulong.jd.service.ItemService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Example;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service
    public class ItemServiceImpl implements ItemService {
    
        @Autowired
        private ItemDao itemDao;
    
        @Override
        public void save(Item item) {
            this.itemDao.save(item);
        }
    
        @Override
        public List<Item> findAll(Item item) {
            // 声明查询条件
            Example<Item> example = Example.of(item);
            // 根据查询条件进行查询数据
            List<Item> list = this.itemDao.findAll(example);
            return list;
        }
    }

    创建引导类

    package com.ayulong.jd;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    @SpringBootApplication
    // 使用定时任务, 需要先开启定时任务, 需要添加注解
    @EnableScheduling
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }

    创建HttpUtils类

    ps: 创建前先创建好存放图片的目录, 直接使用绝对路径

    package com.ayulong.jd.util;
    
    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;
    
    @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();
    
            // 创建httpGet请求对象, 设置url地址
            HttpGet httpGet = new HttpGet(url);
    
            // 设置请求信息
            httpGet.setConfig(getConfig());
    
            // 设置请求头, 伪装用户
            setHeaders(httpGet);
    
            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 {
                // 关闭response
                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.setConfig(getConfig());
    
            // 设置请求头, 伪装用户
            setHeaders(httpGet);
    
            CloseableHttpResponse response= null;
    
            try {
                // 使用HttpClient发起请求, 获取响应
                response = httpClient.execute(httpGet);
    
                // 解析响应, 返回结果
                if (response.getStatusLine().getStatusCode() == 200) {
                    // 判断响应体Entity是否不为空, 如果不为空就可以使用EntityUtils
                    if (response.getEntity() != null) {
                        // 下载图片
                        // 获取图片的后缀
                        String extName = url.substring(url.lastIndexOf("."));
                        // 创建图片名, 重命名图片
                        String picName = UUID.randomUUID().toString() + extName;
                        // 下载图片
                        // 声明OutPutStream
                        OutputStream outputStream = new FileOutputStream(new File("D:\ideaWorkspace\pachong\ayulong-crawler-jd\src\main\resources\static\images\" + picName));
                        response.getEntity().writeTo(outputStream);
                        // 返回图片名称
                        return picName;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭response
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            // 如果下载失败, 返回空串
            return "";
        }
    
        // 设置请求信息
        private RequestConfig getConfig() {
            RequestConfig config = RequestConfig.custom()
                    .setConnectTimeout(1000) // 创建连接的最长时间
                    .setConnectionRequestTimeout(500) // 获取连接的最长时间
                    .setSocketTimeout(10000) // 数据传输的最长时间
                    .build();
            return config;
        }
    
        // 设置请求头
        private void setHeaders(HttpGet httpGet) {
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:6.0.2) Gecko/20100101 Firefox/6.0.2");
        }
    }

    注意: 使用HttpClient爬取数据时, 为了防止被网站拦截, 应该设置请求头

    2. 实现抓取京东页面

    package com.ayulong.jd.task;
    
    import com.ayulong.jd.pojo.Item;
    import com.ayulong.jd.service.ItemService;
    import com.ayulong.jd.util.HttpUtils;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.commons.lang3.StringUtils;
    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 java.util.Date;
    import java.util.List;
    
    
    @Component
    public class ItemTask {
    
        @Autowired
        private HttpUtils httpUtils;
    
        @Autowired
        private ItemService itemService;
    
        private static final ObjectMapper MAPPER = new ObjectMapper();
    
        // 当下载任务完成后, 间隔多长时间进行下一次的任务.
        @Scheduled(fixedDelay = 100 * 1000)
        public void itemTask() throws Exception {
            // 声明需要解析的初始地址
            String url = "https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8" +
                    "&pvid=f112521d94c04007826aa41adcbb0658&page=";
            // 按照页面对手机的搜索结果进行遍历解析
            for (int i = 1; i < 2; i = i + 2) {
                String html = httpUtils.doGetHtml(url + i);
                //  解析页面, 获取商品数据并存储
                this.parse(html);
            }
    
            System.out.println("手机数据抓取完成!");
        }
    
        // 解析页面, 获取商品数据并存储
        private void parse(String html) throws Exception {
            // 解析html获取Document对象
            Document doc = Jsoup.parse(html);
            // 获取spu信息
            Elements spuEles = doc.select("div#J_goodsList > ul > li");
    
            for (Element spuEle : spuEles) {
                // 排除没有data-spu的值的广告
                if (StringUtils.isNotEmpty(spuEle.attr("data-spu"))) {
                    // 获取spu
                    long spu = Long.parseLong(spuEle.attr("data-spu"));
                    // 获取sku信息
                    Elements skuEles = spuEle.select("li.ps-item");
                    for (Element skuEle : skuEles) {
                        // 获取sku
                        long sku = Long.parseLong(skuEle.select("[data-sku]").first().attr("data-sku"));
                        // 根据sku查询商品数据
                        Item item = new Item();
                        item.setSku(sku);
                        List<Item> list = this.itemService.findAll(item);
                        if (list.size() > 0) {
                            // 如果商品存在, 就进行下一个循环, 该商品不保存, 因为已存在
                            continue;
                        }
                        // 设置商品的spu
                        item.setSpu(spu);
                        // 获取商品的详情的url
                        String itemUrl = "https://item.jd.com/" + sku + ".html";
                        item.setUrl(itemUrl);
                        // 获取商品的图片
                        String picUrl = "https:" + skuEle.select("img[data-sku]").first().attr("data-lazy-img");
                        picUrl = picUrl.replace("/n7/", "/n0/" );
                        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());
                        String title = Jsoup.parse(itemInfo).select("div.sku-name").text();
                        item.setTitle(title);
                        //item.setTitle();
                        item.setCreated(new Date());
                        item.setUpdated(item.getCreated());
    
                        // 保存商品数据到数据库中
                        this.itemService.save(item);
                    }
                }
    
            }
        }
    }
  • 相关阅读:
    MongoDB的查询
    商品订购及货物采购信息系统(代码分析)
    Java连接数据库(mysql,sqlserver)
    开通博客第一天
    Ubuntu下java环境的搭建
    商品订购及货物采购信息系统(需求分析)
    GitHub客户端发布托管代码
    property中copy和strong修饰符的使用指北
    iOS界面间传值
    GPUImage的滤镜功能一览表
  • 原文地址:https://www.cnblogs.com/zouhong/p/13871510.html
Copyright © 2020-2023  润新知