Java实现网络爬虫:爬取京东商品案例

爬取京东商品案例

需求分析

一、需求
抓取京东商城的数据,把商品数据保存到数据库。

二、功能分析

  1. 使用HttpClient发送一个get请求,请求搜索url,得到商品列表
  2. 使用jsoup解析搜索结果页面。
  3. 把商品信息封装一个对象中。
  4. 把商品数据保存到数据库。

三、京东页面分析
当在京东的搜索框输入手机时,此时的url为

https://search.jd.com/Search?keyword=手机&wq=手机&page=1&s=1&click=1

当进行翻页操作,发现page值为1,3,5,7…进一步分析发现,京东商品每一页展示两页数据,当看完一页数据后,拉到底部,会显示第二页数据,当看完第二页数据,才会进行翻页操作,此时真正进入第二页。

问题:京东商城每次只展示30条数据,后30条数据(每页的第二页)是ajax动态加载的,使用HttpClient解决非常麻烦,此处不进行爬取后30条数据。

爬取页数:取10页数据。

爬取的信息:商品标题,价格,图片,skuid,spuid,商品详情地址等
在这里插入图片描述
在这里插入图片描述

四、数据库表创建

保存到数据库:创建一个数据库。需要的字段都是商品列表中可以解析出来的字段。

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(1000) DEFAULT NULL COMMENT '商品标题',
  `price` float(10,0) DEFAULT NULL COMMENT '商品价格',
  `pic` varchar(200) DEFAULT NULL COMMENT '商品图片',
  `url` varchar(1500) 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=217 DEFAULT CHARSET=utf8 COMMENT='京东商品';

代码实现

编写爬虫的业务逻辑
1、使用工具类创建一个HttpClient对象。
2、使用HttpClient发送请求,请求就是搜索的url+页码
3、接收服务端响应html
4、使用Jsoup解析html
5、把解析的商品数据封装成Item对象
6、使用dao把商品写入数据库。
7、需要翻页。

注意:图片也是一个http请求,需要再次发起请求爬取图片,然后存储到本地

@Component
public class Crawler {

    @Autowired
    private ItemDao itemDao;

    private String startUrl = "https://search.jd.com/Search?keyword=手机&wq=手机&s=1&click=1&page=";

    /**
     * 爬取页面
     */
    public void doCrawler() {
        try {
            //1、使用工具类创建一个HttpClient对象。
            CloseableHttpClient httpClient = HttpsUtils.getHttpClient();
            for (int i = 0; i < 10; i++) {
                //2、使用HttpClient发送请求,请求就是搜索的url+页码
                HttpGet get = new HttpGet(startUrl + (i * 2 + 1));
                get.addHeader("User-Agent", " Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
                CloseableHttpResponse response = httpClient.execute(get);
                //3、接收服务端响应html
                HttpEntity entity = response.getEntity();
                String html = EntityUtils.toString(entity, "utf-8");
                //4、使用Jsoup解析html
                parseHtml(html);
                //5、把解析的商品数据封装成Item对象
                //6、使用dao把商品写入数据库。
                //7、需要翻页。
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 页面解析
     * @param html
     */
    private void parseHtml(String html) throws Exception {
        //4、使用Jsoup解析html
        Document document = Jsoup.parse(html);
        //解析商品列表
        Elements elements = document.select("li.gl-item");
        for (Element element : elements) {
            //解析节点中的商品数据
            //spu
            String spu = element.attr("data-spu");
            //sku
            String sku = element.attr("data-sku");
            //title
            String title = element.select("div.p-name em").text();
            //price
            String price = element.select("div.p-price i").text();
            //图片
            String imgUrl = element.select("div.p-img img").attr("src");
            String imageName = downloadImage(imgUrl);
            //商品的url
            String itemUrl = element.select("div.p-img > a").attr("href");
            //5、把解析的商品数据封装成Item对象
            Item item = new Item();
            item.setSpu(Long.parseLong(spu));
            item.setSku(Long.parseLong(sku));
            item.setTitle(title);
            if (StringUtils.isNotBlank(price)) {
                item.setPrice(Float.parseFloat(price));
            }
            item.setPic(imageName);
            item.setUrl(itemUrl);
            item.setCreated(new Date());
            item.setUpdated(new Date());
            //6、使用dao把商品写入数据库。
            itemDao.save(item);
        }
    }

    /**
     * 爬取图片
     * @param imageUrl
     * @return
     */
    private String downloadImage(String imageUrl) throws Exception {
        //创建一个HttpClient对象
        CloseableHttpClient httpClient = HttpsUtils.getHttpClient();
        //创建一个HttpGet对象
        HttpGet get = new HttpGet("https:" + imageUrl);
        get.addHeader("User-Agent", " Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
        //发送请求
        CloseableHttpResponse response = httpClient.execute(get);
        //接收服务端响应的内容。
        HttpEntity entity = response.getEntity();
        //需要截取扩展名
        String extName = imageUrl.substring(imageUrl.lastIndexOf("."));
        //需要生成文件名。可以使用uuid生成文件名。
        String fileName = UUID.randomUUID() + extName;
        //D:\temp\term331\images
        //创建一个文件输出流,把文件保存到磁盘
        FileOutputStream outputStream = new FileOutputStream("D:\\temp\\images\\" + fileName);
        //接收流,把内容保存到磁盘。
        entity.writeTo(outputStream);
        //关闭流
        outputStream.close();
        //关闭Response对象
        response.close();
        return fileName;
    }
}

开启新线程可以让爬取在后台进行,而不是页面一直在转

@RestController
public class CrawlerController {
    @Autowired
    private Crawler crawler;

    @RequestMapping("/start")
    public String startCrawler() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程已经启动。。。。。");
                crawler.doCrawler();

            }
        }).start();
        return "OK";
    }
}

猜你喜欢

转载自blog.csdn.net/Sakuraaaaaaa/article/details/107299900