JD爬虫综合案例

Posted 保护胖丁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JD爬虫综合案例相关的知识,希望对你有一定的参考价值。

一、需求分析

需求说明:

我们要抓取 多页 京东商品详情页的: 商品的名称、商品价格、商品的id、商品图片、商品的详情的地址
在这里插入图片描述

通过点击F12观察: 所需要爬取的数据在一下这几个地方

在这里插入图片描述
对于商品的详情页: 通过分析发现 , 请详情页的url地址就是通过spu拼接而来的
在这里插入图片描述

spu 和 sku的区别说明
  • SPU = Standard Product Unit (标准产品单位)

    • SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。
    • 例如 iPhone X 可以确定一个产品即为一个SPU
  • SKU=stock keeping unit(库存量单位)

    • SKU即库存进出计量的单位, 可以是以件、盒、托盘等为单位。SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。在服装、鞋类商品中使用最多最普遍。
    • 例如 iPhone X 64G 银色 则是一个SKU。
前端常见选择器
  1. 标签名选择器 : 通过标签名指定标签
     h1{ color:red ; background-color:blue }
     h2,h3,h4{ color: pink }
  1. class选择器 : 通过class属性指定标签
定义 --> 
      .class名{ css代码 }
      .abc{color:green}
使用 --> 
     <h1 class="abc">
  1. id选择器: 通过为标签添加id属性来指定标签样式
     定义 -->  #id值{css代码}       #p1{ color:orange }
     使用 -->  <p  id="p1">
     注意: id属性主要用来在网页里唯一标识一个标签,其次还可以为这个标签指定css样式
  1. 后代选择器
     语法: 选择器1 选择器2{ css代码 }
     定义:  p h1{ color:red }      #d1 .abc h1{ ...... }
     作用: 为所有出现在p里的h1标签定义风格样式(包括儿子,孙子,...)

二、项目的准备工作

表结构的准备工作
CREATE DATABASE `jd_spider`;
USE  `jd_spider`;
CREATE TABLE `t_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(1000) DEFAULT NULL COMMENT '商品标题',
  `price` double(10,0) DEFAULT NULL COMMENT '商品价格',
  `pic` varchar(200) DEFAULT NULL COMMENT '商品图片',
  `url` varchar(1500) DEFAULT NULL COMMENT '商品详情地址',
  `created` varchar(100) DEFAULT NULL COMMENT '创建时间',
  `updated` varchar(100) DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `sku` (`sku`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1116 DEFAULT CHARSET=utf8 COMMENT='京东商品';
更改pom.xml文件
  <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.4</version>
        </dependency>

        <!-- 导入jsoup包 -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>
        <!-- 导入c3p0连接池包-->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.5</version>
            <scope>compile</scope>
        </dependency>
        <!-- 导入mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
            <scope>compile</scope>
        </dependency>
        <!-- 导入lombok包 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
        <!-- 导入jsoup包 -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- java编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

三、开发代码

一:配置文件 c3p0-config.xml
<c3p0-config>
    <!-- 使用默认的配置读取连接池对象 -->
    <default-config>
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jd_spider?serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">****</property>

        <!-- 连接池参数 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">10</property>
        <property name="checkoutTimeout">3000</property>
    </default-config>
</c3p0-config>
二:实体类(使用lombok插件)
package com.itheima.cn.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Item {
    //主键
    private Long id;
    //标准产品单位(商品集合)
    private Long spu;
    //标准产品单位(商品集合)
    private Long sku;
    //商品标题
    private String title;
    //商品价格
    private Double price;
    //商品图片
    private String pic;
    //商品详情地址
    private String url;
    //创建时间
    private String created;
    //更新时间
    private String updated;
}
三、工具类
import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class C3P0Utils {
    //定义C3P0连接池对象
    private static DataSource dataSource = new ComboPooledDataSource();

    private C3P0Utils(){

    }
    //获取数据库连接对象
    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    //关闭数据库相关对象
    @SuppressWarnings("all")
    public static void closeAll(  ResultSet resultSet,Statement statement,Connection connection) {
        try{
            if( resultSet!=null ){
                resultSet.close();
            }

            if( statement!=null ){
                statement.close();
            }

            if( connection!=null ){
                connection.close();
            }

        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
四、dao层
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

public class ItemDao {
    public  void saveData(List<Item> list) throws SQLException {
        Connection conn = C3P0Utils.getConnection();
        String sql = "insert into t_jd_item values(null,?,?,?,?,?,?,?,?)";
        PreparedStatement pstm = conn.prepareStatement(sql);
        for (Item item : list) {
            pstm.setLong(1, item.getSpu());
            pstm.setLong(2, item.getSku());
            pstm.setString(3, item.getTitle());
            pstm.setDouble(4, item.getPrice());
            pstm.setString(5, item.getPic());
            pstm.setString(6, item.getUrl());
            pstm.setString(7, item.getCreated());
            pstm.setString(8, item.getUpdated());
            int i = pstm.executeUpdate();
            if (i==0){
                System.out.println("false");
            }
        }
        C3P0Utils.closeAll(null,pstm,conn);


    }
}

五、主代码
import com.sun.deploy.ui.DialogTemplate;
import org.apache.http.HttpEntity;
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.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.UUID;


/*
    1.明确url
    2.发送请求,获取对象
    3.解析数据
    4.解析数据,关闭连接
    */
public class jdSpider {
    public static void main(String[] args) throws IOException, SQLException {
        //2.发送请求,获取对象 固定用法
        //2.1创建httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // 1.明确url
        int count = 1;
        while (count < 5) {
            String indexUrl = "https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&page=" + (count * 2 - 1) + "&s=1&click=0";
            System.out.println("当前爬的是第" + count + "页,地址是:" + indexUrl);
            //2.2创建请求方式httpget
            HttpGet httpGet = new HttpGet(indexUrl);
            //2.3设置请求头
            httpGet.setHeader("user-agent", " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/89.0.4389.82 Safari/537.36");
            //2.4发送请求,获取对象(response 响应对象)
            CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
            //2.5我们要获得状态码,然后判断是否是200
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            //2.5.1判断是否200
            ifSuccessDo(httpClient, httpResponse, statusCode);
            count++;
        }
        httpClient.close();
    }

    private static void ifSuccessDo(CloseableHttpClient httpClient, CloseableHttpResponse httpResponse, int statusCode) throws IOException, SQLException {
        if (statusCode == 200) {
            //获取响应体对象
            HttpEntity entity = httpResponse.getEntity();
            //2.6获得一个字符串型的html页面,即具体的页面响应信息
            String html = EntityUtils.toString(entity, "UTF-8");
            //2.7释放资源
            httpResponse.close();
            ArrayList<Item> list = new ArrayList<>();
            //3.解析数据
            //3.1通过Jsoup,将字符串形式是的html,转成对应的document对象
            Document document = Jsoup.parse(html);
            //System.out.println("document = " + document);
            //3.2获取所有的商品信息 通过选择器选择到最小的商品单位
            //id="J_goodsList"  class="gl-warp clearfix" li class="gl-item
            //System.out.println(document.select("#J_goodsList>ul[class=gl-warp clearfix]>li"));
            Elements elements = document.select("#J_goodsList>ul[class='gl-warp clearfix']>li");
            getJDItem(httpClient, list, elements);
            ItemDao itemDao = new ItemDao();
            itemDao.saveData(list);
            System.out.println("当前页打印了" + list.size() + "件商品");
        }
    }

    private static void getJDItem(CloseableHttpClient httpClient, ArrayList<Item> list, Elements elements) throws IOException {
        for (Element li : elements) {
            //3.3解析需求
            //3.3.1解析spu sku 用attr可以拿到li标签下大的data-spu data-sku
            String spu = li.attr("data-spu");
            String sku = li.attr("data-sku");
            if (spu == null || "".equals(spu)) {
                spu = sku;
            } else if (sku == null || "".equals(sku)) {
                sku = spu;
            }
            //3.3.2解析标题title 在<em>中
            String title = li.select(".gl-i-wrap>div[class='p-name p-name-type-2'] em").text();
            // System.out.println("title = " + title);
            //3.3.3解析价格
            String price = li.select(".p-price i").text();
            //System.out.println("price = " + price);
            //3.3.4解析商品详情信息
            //String url="https://item.jd.com/"+spu+".html";
            Elements elements1 = li.select(".p-img>a");
            //因为href没有https请求头,所以要补充请求头
            String url = "https:" + elements1.attr("href");
            //System.out.println("url = " + url);
            String pic = getPicItem(httpClient, li);
            searchData(list, spu, sku, title, price, url, pic);
        }
    }

    private static String getPicItem(CloseableHttpClient httpClient, Element li) throws IOException {
        //3.3.5图片解析
        //3.3.5.1拿到图片url
        String pic = "https:" + li.select(".p-img img").attr("data-lazy-img");
        // System.out.println("pic = " + pic);
        //3.3.5.2图片扩展,下载图片到我的电脑
        //图片扩展 创建http请求
        HttpGet picGet = new HttpGet(pic);
        //通过HTTPclient,发送请求获取response
        CloseableHttpResponse responsePic = httpClient.execute(picGet);
        //判断状态码
        if (responsePic.getStatusLine().getStatusCode() == 200) {
            //获取图片response的响应流
            InputStream is = responsePic.getEntity().getContent();
            //用高效缓冲流
            BufferedInputStream bis = new BufferedInputStream(is);
            //常见输出流,关联目标文件夹
            String deastPicPath = "E:/jdimgs/" + UUID.randomUUID() + pic.substring(pic.lastIndexOf("."));
            //System.out.println("deastPicPath = " + deastPicPath);
            FileOutputStream fos = new FileOutputStream(deastPicPath);
            int len;
            byte[] buf = new byte[1024];
            while ((len = bis.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }
            fos.close();
            fos.close();
            responsePic.close();
        }
        return pic;
    }

    private static void searchData(ArrayList<Item> list, String spu, String sku, String title, String price, String url, String pic) {
        // 4.保存数据,关闭连接
        Item item = new Item(
                null,
                Long.parseLong(spu),
                Long.parseLong(sku),
                title,
                Double.parseDouble(price),
                pic,
                url,
                new Date().toLocaleString(),
                new Date().toLocaleString());
        list.add(item);
    }
}

六、效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

以上是关于JD爬虫综合案例的主要内容,如果未能解决你的问题,请参考以下文章

爬虫——综合案例流程版

直播预告|Python网络爬虫程序设计原理与案例

Python 爬虫+可视化,手把手教你使用 Python 爬取 JD 网站并且可视化展示

教您使用java爬虫gecco抓取JD全部商品信息

scrapy按顺序启动多个爬虫代码片段(python3)

Python爬虫Requests库网络爬虫实战