zoukankan      html  css  js  c++  java
  • java爬虫案例学习

    最近几天很无聊,学习了一下java的爬虫,写一些自己在做这个案例的过程中遇到的问题和一些体会
    1.学习目标
             练习爬取京东的数据,图片+价格+标题等等

    2.学习过程

          1·开发工具
               JDK1.8
               IntelliJ IDEA
               IDEA自带的Maven

           2.使用技术
               Spring Boot+Spring Data JPA
           3.数据库准备

    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.添加依赖

    <?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.itcast.crawler</groupId>
        <artifactId>itcast-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>
        </dependencies>
    </project>

       5.添加配置文件

    #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=root
    
    #JPA Configuration:
    spring.jpa.database=MySQL
    spring.jpa.show-sql=true

      6.代码实现

    1.pojo

    @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; 
    set/get
    }

    2.编写dao

    public interface ItemDao extends JpaRepository<Item,Long> {
        
    }

    3.编写service接口

    public interface ItemService {
    
        //根据条件查询数据
        public List<Item> findAll(Item item);
    
        //保存数据
        public void save(Item item);
    }

    4.ItemServiceImpl实现类

    @Service
    public class ItemServiceImpl implements ItemService {
    
        @Autowired
        private ItemDao itemDao;
    
        @Override
        public List<Item> findAll(Item item) {
            Example example = Example.of(item);
            List list = this.itemDao.findAll(example);
            return list;
        }
    
        @Override
        @Transactional
        public void save(Item item) {
            this.itemDao.save(item);
        }
    }

    5.编写引导类

    @SpringBootApplication
    //设置开启定时任务
    @EnableScheduling
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }

    6. 封装HttpClient

    @Component
    public class HttpUtils {
    
        private PoolingHttpClientConnectionManager cm;
    
        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());
    
            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) {
                    // 获取文件类型
                    String extName = url.substring(url.lastIndexOf("."));
                    // 使用uuid生成图片名
                    String imageName = UUID.randomUUID().toString() + extName;
    
                    // 声明输出的文件
                    OutputStream outstream = new FileOutputStream(new File("D:/images/" + imageName));
                    // 使用响应体输出文件
                    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;
        }
    
    }

    7. 实现数据抓取

    @Component
    public class ItemTask {
    
        @Autowired
        private HttpUtils httpUtils;
        @Autowired
        private ItemService itemService;
    
        public static final ObjectMapper MAPPER = new ObjectMapper();
    
    
        //设置定时任务执行完成后,再间隔100秒执行一次
        @Scheduled(fixedDelay = 1000 * 100)
        public void process() throws Exception {
            //分析页面发现访问的地址,页码page从1开始,下一页oage加2
            String url = "https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&cid2=653&cid3=655&s=5760&click=0&page=";
    
            //遍历执行,获取所有的数据
            for (int i = 1; i < 10; i = i + 2) {
                //发起请求进行访问,获取页面数据,先访问第一页
                String html = this.httpUtils.getHtml(url + 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
                Long spuId = Long.parseLong(spuEle.attr("data-spu"));
    
                //获取商品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<Item> list = this.itemService.findAll(param);
                    //判断是否查询到结果
                    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");
                    //创建时间
                    item.setCreated(new Date());
                    //修改时间
                    item.setUpdated(item.getCreated());
    
    
                    //获取商品标题
                    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);
                }
            }
        }
    }

    3.结果

    4.总结全文

    在写代码代码的时候遇到一下的错误,首先打开了京东的要抓取的页面url="xxxxxx",但是在抓取的时候总是抓取不到数据 循环里面的spus总是为0,经过我多方查找信息,把上面的数据修改为一下代码,就成功了

    package cn.itboxue.jd.task;
    
    import cn.itboxue.jd.pojo.Item;
    import cn.itboxue.jd.serivce.ItemService;
    import cn.itboxue.jd.util.HttpUtils;
    import com.fasterxml.jackson.databind.ObjectMapper;
    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 {
            //声明需要解析的初始地址
    /*
            https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq%22%20+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22=%E6%89%8B%E6%9C%BA&cid2=653&cid3=655&s=113&click=0&page=
    */
            String url = "https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq%22%20+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22=%E6%89%8B%E6%9C%BA&cid2=653&cid3=655&s=113&click=0&page=";
    
            //按照页面对手机的搜索结果进行遍历解析
            for (int i = 1; i < 100; i = i + 2) {
                Document doc = Jsoup.connect(url+i).maxBodySize(0).get();
                //doc获取整个页面的所有数据
                Elements ulList = doc.select("ul[class='gl-warp clearfix']");
                Elements liList = ulList.select("li[class='gl-item']");
    
                this.parse(liList);
            }
    
    
            System.out.println("手机数据抓取完成!");
    
    
        }
    
        //解析页面,获取商品数据并存储
        private void parse( Elements liList) throws Exception {
            //解析html获取Document
           // Document doc = Jsoup.parse(html);
            //System.out.print(doc);
            //获取spu信息
            //Elements elements = doc.select("div#J_goodsList > ul > li");
            //System.out.println(elements+"是否有数据");
    
            for (Element spuEle : liList) {
                //获取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]").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("/n9/","/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());
                    String title = Jsoup.parse(itemInfo).select("div.sku-name").text();
                    item.setTitle(title);
    
    
                    item.setCreated(new Date());
                    item.setUpdated(item.getCreated());
    
                    //保存商品数据到数据库中
                    this.itemService.save(item);
    
                }
            }
        }
    
    }

    今天的笔记就做到这里吧,希望以后的想学习java爬虫的爱好者少走弯路,谢谢。。。。。。。

    2019-04-1921:07:56

    作者:何秀好

  • 相关阅读:
    sprintf函数%u输入long long int型数值异常
    关于smarty模板display函数的$compile_id 参数的意义
    打log的时候如果少写一个%d,cgi会core掉
    c++标准库的源码和SIG实现是什么关系
    2010
    jquery 中jsonp原理最简说明
    1月17日stl string阅读笔记
    Moss母版页制作详解(一)
    Moss中的权限操作
    动态添加和删除表格行
  • 原文地址:https://www.cnblogs.com/itboxue/p/10738810.html
Copyright © 2011-2022 走看看