day74,爬虫01,HttpClient工具类的使用步骤,jsoup的解析常用方法,爬京东数据的案例

一、什么是爬虫
爬虫是一段程序,抓取互联网上的数据,保存到本地。
抓取过程:
1、使用程序模拟浏览器
2、向服务器发送请求。
3、服务器响应html
4、把页面中的有用的数据解析出来。
解析页面中的链接地址。
把链接地址添加到url队列中。
5、爬虫从url队列中取url,返回的操作。

二、爬虫的抓取环节
1、抓取页面。
可以使用java api中提供的URLConnection类发送请求。
推荐使用工具包HttpClient。
是apache旗下的一个开源项目。
可以模拟浏览器。
2、对页面进行解析。
使用Jsoup工具包。
可以像使用jQuery一样解析html。

二、HttpClient
可以使用HttpClient模拟浏览器。
1、使用HttpClient发送get请求
步骤:

@Test
    public void testGet() throws Exception {
        //1)创建一个HttpClient对象,使用CloseableHttpClient,使用HttpClients工具类创建。
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2)创建一个HttpGet对象,get对象封装请求的url
        //HttpGet get = new HttpGet("http://bbs.itheima.com/search.php?mod=forum&searchid=50&orderby=lastpost&ascdesc=desc&searchsubmit=yes&kw=java");
        HttpGet get = new HttpGet("https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&wq=%E6%89%8B%E6%9C%BA&pvid=2d8c452b8a904b16afc71167f307f975");
        //3)使用HttpClient执行请求
        CloseableHttpResponse response = httpClient.execute(get);
        //4)接收服务端响应的内容。
        StatusLine statusLine = response.getStatusLine();
        System.out.println(statusLine);
        //	响应的内容包含响应头
        int statusCode = statusLine.getStatusCode();
        System.out.println(statusCode);
        //	包含响应的内容(html)
        HttpEntity entity = response.getEntity();
        String html = EntityUtils.toString(entity);
        System.out.println(html);
        //5)关闭连接
        response.close();
        httpClient.close();
    }

2、使用HttpClient发送Post请求
步骤:

 @Test
    public void testPost() throws Exception {
        //1)创建一个HttpClient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //2)创建HttpPost对象,封装一个url
        HttpPost post = new HttpPost("http://bbs.itheima.com/search.php");
        //3)如果有参数就应该把参数封装到表单中。
        List<NameValuePair> form = new ArrayList<>();
        form.add(new BasicNameValuePair("mod", "forum"));
        form.add(new BasicNameValuePair("searchid", "50"));
        form.add(new BasicNameValuePair("orderby", "lastpost"));
        form.add(new BasicNameValuePair("ascdesc", "desc"));
        form.add(new BasicNameValuePair("searchsubmit", "yes"));
        form.add(new BasicNameValuePair("kw", "java"));
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(form);
        post.setEntity(entity);
        //4)使用HttpClient执行请求。
        CloseableHttpResponse response = httpClient.execute(post);
        //5)接收服务端响应html
        HttpEntity resultEntity = response.getEntity();
        String html = EntityUtils.toString(resultEntity);
        System.out.println(html);
        //6)关闭连接
        response.close();
        httpClient.close();
    }

爬京东的数据,需要加上请求头(最近新出的规则,具体可以百度):

 @Test
    public void testHtts() throws Exception {
        //创建一个HttpClient对象
        CloseableHttpClient httpClient = HttpsUtils.getHttpClient();
        //创建一个HttGet对象,封装url
        HttpGet get = new HttpGet("https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&wq=%E6%89%8B%E6%9C%BA&pvid=2d8c452b8a904b16afc71167f307f975");
        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 html = EntityUtils.toString(entity, "utf-8");
        //打印结果
        System.out.println(html);
        //关闭Response
        response.close();
    }

三、jsoup
jsoup就是一个java的工具包。解析html的工具包。
1、使用方法
1)使用Jsoup工具类提供的方法parse,解析html
parse的参数可以是url、本地文件、String(html)、InputStream
解析之后得到一个Document对象。
2)可以使用Document对象的方法对页面进行解析
1、常用的方法
根据id选择
根据节点名称选择
根据属性选择
根据属性名称选择
根据class名称选择

2、使用css选择器
和jQuery相同的css选择器。
使用select方法执行css选择器。

2、Document的常用方法
根据标签名称选择节点:
getElementsByTag
根据id选择节点:
getElementById
根据属性选择节点:判断节点是否包含此属性。
getElementsByAttribute
根据属性的值选择节点:
getElementsByAttributeValue
根据class选择节点:
getElementsByClass
取标签内的文本信息:
text()
取标签的属性:
attr(“属性名称”)
取标签中的子标签,返回集合:
nodes()

 @Test
    public void parseHtml() throws Exception {
        //使用Jsoup工具类提供的方法parse,解析html
        //Document document = Jsoup.parse(new URL("http://www.itcast.cn"), 3000);
        Document document = Jsoup.parse(new File("D:\\temp\\term331\\test.html"), "utf-8");
        //解析title
        Elements elements = document.getElementsByTag("title");
        for (Element element : elements) {
            System.out.println(element.text());
        }
        //取链接地址
        System.out.println("取链接地址-----------------------");
        Elements elements1 = document.getElementsByTag("a");
        for (Element element : elements1) {
            System.out.println(element.attr("href"));
        }
        
        System.out.println("取标签内的文本信息-------------------");
        Element element = document.getElementById("test");
        System.out.println(element.text());

        System.out.println("根据属性选择节点:判断节点是否包含此属性,有就取,没有就不取------------");
        Elements elements2 = document.getElementsByAttribute("id");
        for (Element element1 : elements2) {
            System.out.println(element1);
        }
        System.out.println("根据属性的值选择节点------");
        Elements elements3 = document.getElementsByAttributeValue("target", "_blank");
        for (Element element1 : elements3) {
            System.out.println(element1);
        }
        System.out.println("根据class选择节点-------------");
        Elements elements4 = document.getElementsByClass("s_name");
        for (Element element1 : elements4) {
            System.out.println(element1);
        }
    }

3、Document对象的css选择器
select方法解析css选择器。

 @Test
    public void testCssSelector() throws Exception {
        Document document = Jsoup.parse(new File("D:\\temp\\term331\\test.html"), "utf-8");
        //解析document对象
        //根据id选择
        //Elements elements = document.select("#city_bj");
        //根据标签名称选择
        //Elements elements = document.select("li");
        //根据class选择
        //Elements elements = document.select(".s_name");
        //根据属性选择
        //Elements elements = document.select("[class='slogan']");
        //css选择器可以组合使用
        //Elements elements = document.select("div.city > div.city_in div.city_con span[abc='123']");
        Elements elements = document.select("#test > a:nth-child(1) > span:nth-child(1)");
        for (Element element : elements) {
            System.out.println(element);
        }
    }

四、案例
1、需求
抓取京东商城的数据,把商品数据保存到数据库。
2、功能分析
1)使用HttpClient发送一个get请求,请求搜索url,得到商品列表
2)使用jsoup解析搜索结果页面。
3)把商品信息封装一个对象中。
4)把商品数据保存到数据库。
https://search.jd.com/ Search?keyword=%E7%94%B5%E8%A7%86&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E7%94%B5%E8%A7%86&stock=1&s=55&click=0&page=1
京东商城每次只展示30条数据,后30条数据是ajax动态加载的。
取10页数据。

保存到数据库:
	创建一个数据库。
	需要的字段都是商品列表中可以解析出来的字段。
持久层框架可以使用springDataJpa,使用springboot搭建工程。

3、工程搭建
1)创建一个springboot工程

2)添加父工程及依赖的jar包
	SpringDataJpa的起步依赖
	mysql的数据库驱动
	添加spring-boot-stater-web模块
	HttpClient
	jsoup
<?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>

    <groupId>com.itheima</groupId>
    <artifactId>crawler-331</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
    </parent>
    <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>

    <!--appch 封装了String的工具包-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
        <dependency>
            <groupId>xml-apis</groupId>
            <artifactId>xml-apis</artifactId>
        </dependency>
    </dependencies>

</project>
3)创建一个application.properties
	其中配置数据库的连接信息
#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/crawler-331
spring.datasource.username=root
spring.datasource.password=123

#JPA Configuration:
spring.jpa.database=MySQL
spring.jpa.show-sql=true

4)创建实体类、dao
day74,爬虫01,HttpClient工具类的使用步骤,jsoup的解析常用方法,爬京东数据的案例_第1张图片

@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 float price;
    private String url;
    private String pic;
    private Date created;
    private Date updated;

5)创建引导类。
day74,爬虫01,HttpClient工具类的使用步骤,jsoup的解析常用方法,爬京东数据的案例_第2张图片
6)编写爬虫的业务逻辑

package com.itheima.component;

import com.itheima.dao.ItemDao;
import com.itheima.entity.Item;
import com.itheima.utils.HttpsUtils;
import org.apache.commons.lang3.StringUtils;
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.util.EntityUtils;
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.stereotype.Component;

import javax.swing.text.html.parser.Entity;
import java.io.FileOutputStream;
import java.util.Date;
import java.util.UUID;

@Component
public class Crawler {

    @Autowired
    private ItemDao itemDao;

    private String startUrl = "https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA" +
            "&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E6%89%8B%E6%9C%BA&cid2=653" +
            "&cid3=655&s=58&click=0&page=";

    //获取京东页面的方法
    public void doCrawler() {

    //1、使用工具类创建一个HttpClient对象。
        try {
            for (int i = 0; i < 10; i++) {
                CloseableHttpClient httpClient = HttpsUtils.getHttpClient();
                //2、使用HttpClient发送请求,请求就是搜索的url+页码
                HttpGet httpGet = new HttpGet(startUrl+(i*2+1));
                httpGet.addHeader("User-Agent", " Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
                CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
                //3、接收服务端响应html
                HttpEntity entity = httpResponse.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("source-data-lazy-img");
            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();
        //需要截取扩展名,subString截取最后一个点,得到.加后面的字符
        String extName = imageUrl.substring(imageUrl.lastIndexOf("."));
        //需要生成文件名。可以使用uuid生成文件名。
        String fileName = UUID.randomUUID() + extName;
        //D:\temp\term331\images
        //创建一个文件输出流,把文件保存到磁盘
        FileOutputStream outputStream = new FileOutputStream("D:\\Code\\crawler331\\src\\images\\" + fileName);
        //接收流,把内容保存到磁盘。
        entity.writeTo(outputStream);
        //关闭流
        outputStream.close();
        //关闭Response对象
        response.close();
        return fileName;
    }


}

你可能感兴趣的:(day74,爬虫01,HttpClient工具类的使用步骤,jsoup的解析常用方法,爬京东数据的案例)