zoukankan      html  css  js  c++  java
  • 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);
                    }
                }
    
            }
        }
    }
  • 相关阅读:
    VS2008 环境中完美搭建 Qt 4.7.4 静态编译的调试与发布 Inchroy's Blog 博客频道 CSDN.NET
    编写可丢弃的代码
    c++ using namespace std; 海明威 博客园
    解决MySQL server has gone away
    nginx upstream 调度策略
    (2006, 'MySQL server has gone away') 错误解决 dba007的空间 51CTO技术博客
    Linux IO模型漫谈(2) 轩脉刃 博客园
    redis源码笔记 initServer 刘浩de技术博客 博客园
    MySQLdb批量插入数据
    词库的扩充百度百科的抓取你知道这些热词吗? rabbit9898 ITeye技术网站
  • 原文地址:https://www.cnblogs.com/zouhong/p/13871510.html
Copyright © 2011-2022 走看看