Python_JAVA第一章

python第一章

一、什么是爬虫

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

二、爬虫的抓取环节

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

三、HttpClient

  1. 可以使用HttpClient模拟浏览器。

    • 使用HttpClient发送get请求

      • 步骤:
        1)创建一个HttpClient对象,使用CloseableHttpClient,使用HttpClients工具类创建。
        2)创建一个HttpGet对象,get对象封装请求的url
        3)使用HttpClient执行请求
        4)接收服务端响应的内容。
        响应的内容包含响应头
        包含响应的内容(html)
        5)关闭连接

      • /**
         * 使用HttpClient发送get请求
         * @throws Exception
         */
        @Test
        public void testGet() throws Exception {
            //1)创建一个HttpClient对象,使用CloseableHttpClient,使用HttpClients工具类创建。
            CloseableHttpClient httpClient = HttpClients.createDefault();
            //2)创建一个HttpGet对象,get对象封装请求的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=6c76113fd91343dbaf0cc9037e3433c3");
            //3)使用HttpClient执行请求
            CloseableHttpResponse response = httpClient.execute(get);
            //4)接收服务端响应的内容。
            StatusLine statusLine = response.getStatusLine();
            System.out.println(statusLine);// HTTP/1.1 200 OK
            //响应的内容包含响应头
            int statusCode = statusLine.getStatusCode();
            System.out.println(statusCode);// 200
            //包含响应的内容(html)
            HttpEntity entity = response.getEntity();
            String html = EntityUtils.toString(entity);
            System.out.println(html);
            //5)关闭连接
            response.close();
            httpClient.close();
        }
        
    • 使用HttpClient发送Post请求

      • 步骤:

        ​ 1)创建一个HttpClient对象
        ​ 2)创建HttpPost对象,封装一个url
        ​ 3)如果有参数就应该把参数封装到表单中。
        ​ 4)使用HttpClient执行请求。
        ​ 5)接收服务端响应html
        ​ 6)关闭连接

      • /**
         * 使用HttpClient发送Post请求
         * @throws Exception
         */
        @Test
        public void testPost() throws Exception {
            //1)创建一个HttpClient对象
            CloseableHttpClient httpClient = HttpClients.createDefault();
            //2)创建HttpPost对象,封装一个url
            HttpPost post = new HttpPost("https://search.jd.com/");
            //3)如果有参数就应该把参数封装到表单中。
            List<NameValuePair> form = new ArrayList<>();
            form.add(new BasicNameValuePair("keyword", "手机"));
            form.add(new BasicNameValuePair("enc", "utf-8"));
            form.add(new BasicNameValuePair("wq", "手机"));
            form.add(new BasicNameValuePair("pvid", "929b7523b89642b2a92b655a111424d0"));
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(form);
            post.setEntity(entity);
            //4)使用HttpClient执行请求。
            CloseableHttpResponse response = httpClient.execute(post);
            //5)接收服务端响应html
            String html = EntityUtils.toString(response.getEntity());
            System.out.println(html);
            //6)关闭连接
            response.close();
            httpClient.close();
        }
        
    • 使用连接池创建HttpClient对象

      • 1)创建一个连接池对象。在系统中应是单例的。

      • 2)使用HttpClients工具类,设置使用的连接池对象。基于连接池创建HttpClient对象。

      • 3)使用HttpClient发送请求。

      • 4)接收服务端响应的数据。

      • 5)关闭Response对象,HttpClient对象不需要关闭。

      •  /**
          * 使用连接池创建HttpClient对象
          * @throws Exception
          */
        @Test
        public void createHttpClientUserPool() throws Exception {
            //1)创建一个连接池对象。在系统中应是单例的。
            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
            //2)使用HttpClients工具类,设置使用的连接池对象。基于连接池创建HttpClient对象。
            CloseableHttpClient httpClient = HttpClients.custom()
                //设置使用的连接池对象
                .setConnectionManager(cm)
                .build();
            //3)使用HttpClient发送请求。
            HttpGet get = new HttpGet("https://www.jd.com/");
            CloseableHttpResponse response = httpClient.execute(get);
            //4)接收服务端响应的数据。如果乱码第二个参数可以设置编码集
            String html = EntityUtils.toString(response.getEntity(),"utf-8");
            System.out.println(html);
            //5)关闭Response对象,HttpClient对象不需要关闭。
            response.close();
        }
        
    • 使用自动工具类 HttpsUtils 访问被拦截的请求

      • package cn.sgwks.crawler;
        
        import org.apache.http.config.Registry;
        import org.apache.http.config.RegistryBuilder;
        import org.apache.http.conn.socket.ConnectionSocketFactory;
        import org.apache.http.conn.socket.PlainConnectionSocketFactory;
        import org.apache.http.conn.ssl.NoopHostnameVerifier;
        import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
        import org.apache.http.conn.ssl.TrustStrategy;
        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.ssl.SSLContextBuilder;
        import java.security.cert.CertificateException;
        import java.security.cert.X509Certificate;
        
        public class HttpsUtils {
            private static final String HTTP = "http";
            private static final String HTTPS = "https";
            private static SSLConnectionSocketFactory sslsf = null;
            private static PoolingHttpClientConnectionManager cm = null;
            private static SSLContextBuilder builder = null;
            static {
                try {
                    builder = new SSLContextBuilder();
                    // 全部信任 不做身份鉴定
                    builder.loadTrustMaterial(null, new TrustStrategy() {
                        @Override
                        public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                            return true;
                        }
                    });
                    sslsf = new SSLConnectionSocketFactory(builder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
                    Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                            .register(HTTP, new PlainConnectionSocketFactory())
                            .register(HTTPS, sslsf)
                            .build();
                    cm = new PoolingHttpClientConnectionManager(registry);
                    cm.setMaxTotal(200);//max connection
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        
            public static CloseableHttpClient getHttpClient() throws Exception {
                CloseableHttpClient httpClient = HttpClients.custom()
                        .setSSLSocketFactory(sslsf)
                        .setConnectionManager(cm)
                        .setConnectionManagerShared(true)
                        .build();
                return httpClient;
            }
        }
        
      •  /**
          * 使用工具类访问不到的数据
          * 访问互联网购物商城类似:京东,淘宝的搜索页面抓取不到的数据,但这里只解决了页面加载后的数据,后面的ajax数据却无法获取
          */
        @Test
        public void testHttps2() throws Exception {
            //1.创建一个HttpClient对象
            CloseableHttpClient httpClient = HttpsUtils.getHttpClient();
            //2.创建一个HttGet对象,封装url
            HttpGet get = new HttpGet("https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&pvid=d57afafc61054075b2819ab21b6e55b1");
            //3.设置访问头,火狐浏览器
            get.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0");
            //设置访问头,谷歌浏览器
            //get.addHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.100 Safari/537.36");
            //4.执行请求
            CloseableHttpResponse response = httpClient.execute(get);
            //5.接收结果
            String html = EntityUtils.toString(response.getEntity(),"utf-8");
            //打印结果
            System.out.println(html);
            //关闭Response
            response.close();
        }
        

四、jsoup

  1. jsoup就是一个java的工具包。解析html的工具包。

    • 使用方法

      • 1)使用Jsoup工具类提供的方法parse,解析html
        parse的参数可以是url、本地文件、String(html)、InputStream
        解析之后得到一个Document对象。

      • /**
         * 根据标签获取指定数据
         * @throws Exception
         */
        @Test
        public void parseHtml() throws Exception {
            //使用Jsoup工具类提供的方法parse,解析html
            Document document = Jsoup.parse(new URL("https://www.jd.com"), 3000);
            //Document document = Jsoup.parse(new File("src/main/java/cn/sgwks/crawler/login.html"), "utf-8");
            //解析title
            Elements elements = document.getElementsByTag("title");
            for (Element element : elements) {
                System.out.println("初始html:"+element);
                System.out.println("去除标签html:"+element.text());
            }
            //取链接地址
            System.out.println("取链接地址-----------------------");
            Elements elements1 = document.getElementsByTag("a");
            for (Element element : elements1) {
                System.out.println(element.attr("href"));
            }
            System.out.println("获取id为index值得标签-------------------");
            Element element = document.getElementById("index");
            System.out.println(element.text());
            System.out.println("获取包含id的属性标签-------------------");
        }
        
      • 2)可以使用Document对象的方法对页面进行解析

        • 1、常用的方法
          根据id选择
          根据节点名称选择
          根据属性选择
          根据属性名称选择
          根据class名称选择

        • Elements elements2 = document.getElementsByAttribute("id");
          for (Element element1 : elements2) {
              System.out.println(element1);
          }
          System.out.println("获取属性为target得值为_blank的标签--------------");
          Elements elements3 = document.getElementsByAttributeValue("target", "_blank");
          for (Element element1 : elements3) {
              System.out.println(element1);
          }
          System.out.println("获取class标签-------------");
          Elements elements4 = document.getElementsByClass("setting");
          for (Element element1 : elements4) {
              System.out.println(element1);
          }
          
        • 2、使用css选择器
          和jQuery相同的css选择器。
          使用select方法执行css选择器。

    • Document的常用方法

      • ​ 根据标签名称选择节点:
        ​ getElementsByTag

      • ​ 根据id选择节点:
        ​ getElementById

      • ​ 根据属性选择节点:判断节点是否包含此属性。
        ​ getElementsByAttribute

      • ​ 根据属性的值选择节点:
        ​ getElementsByAttributeValue

      • ​ 根据class选择节点:
        ​ getElementsByClass

      • 取标签内的文本信息:
        text()

      • 取标签的属性:
        attr(“属性名称”)

      • /**
         * 根据属性获取数据
         * @throws Exception
         */
        @Test
        public void testCssSelector() throws Exception {
            Document document = Jsoup.parse(new URL("http://www.jd.com"), 3000); 
            //解析document对象
            //根据id选择
            Elements elements = document.select("#video");
            for (Element element : elements) {
                System.out.println(element);
            }
            //根据标签名称选择
            Elements elements1 = document.select("li");
            for (Element element : elements1) {
                System.out.println(element);
            }
            //根据class选择
            Elements elements2 = document.select(".s_name");
            for (Element element : elements2) {
                System.out.println(element);
            }
            //根据属性选择
            Elements elements3 = document.select("[class='slogan']");
            for (Element element : elements3) {
                System.out.println(element);
            }
            //css选择器可以组合使用
            Elements elements4 = document.select(".box_know > div:nth-child(1) > div:nth-child(1) > h2:nth-child(1)");
            for (Element element : elements4) {
                System.out.println(element);
            }
        }
        
  2. Document对象的css选择器
    select方法解析css选择器。

五、案例

  1. 需求

    • 抓取京东商城的数据,把商品数据保存到数据库。
  2. 功能分析

    • 1)使用HttpClient发送一个get请求,请求搜索url,得到商品列表

    • 2)使用jsoup解析搜索结果页面。

    • 3)把商品信息封装一个对象中。

    • 4)把商品数据保存到数据库。

      • https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&wq=%E6%89%8B%E6%9C%BA&pvid=6c76113fd91343dbaf0cc9037e3433c3
        
      • 京东商城每次只展示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>cn.sgwks</groupId>
            <artifactId>crawler-jd</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>
            <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
            </properties>
            <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>
        
    • 3)创建一个application.properties
      其中配置数据库的连接信息

      • #DB Configuration:
        spring:
          datasource:
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://127.0.0.1:3306/crawler-sgw?useUnicode=true&characterEncoding=utf8
            username: root
            password: root
        #JPA Configuration:
          jpa:
            database: mysql
            show-sql: true
            generate-ddl: true
            hibernate:
              ddl-auto: update
        
    • 4)创建实体类、dao

      • package cn.sgwks.crawlerjd.entity;
        
        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 Float price;
            private String pic;
            private String url;
            private Date created;
            private Date updated;
            public Long getId() {
                return id;
            }
            public void setId(Long id) {
                this.id = id;
            }
            public Long getSpu() {
                return spu;
            }
            public void setSpu(Long spu) {
                this.spu = spu;
            }
            public Long getSku() {
                return sku;
            }
            public void setSku(Long sku) {
                this.sku = sku;
            }
            public String getTitle() {
                return title;
            }
            public void setTitle(String title) {
                this.title = title;
            }
            public Float getPrice() {
                return price;
            }
            public void setPrice(Float price) {
                this.price = price;
            }
            public String getPic() {
                return pic;
            }
            public void setPic(String pic) {
                this.pic = pic;
            }
            public String getUrl() {
                return url;
            }
            public void setUrl(String url) {
                this.url = url;
            }
            public Date getCreated() {
                return created;
            }
        
            public void setCreated(Date created) {
                this.created = created;
            }
            public Date getUpdated() {
                return updated;
            }
            public void setUpdated(Date updated) {
                this.updated = updated;
            }
        }
        
      • package cn.sgwks.crawlerjd.dao;
        
        import cn.sgwks.crawlerjd.entity.Item;
        import org.springframework.data.jpa.repository.JpaRepository;
        
        public interface ItemDao extends JpaRepository<Item,Long> {
        }
        
    • 5)创建引导类。

      • package cn.sgwks.crawlerjd;
        
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        
        @SpringBootApplication
        public class Application {
            public static void main(String[] args) {
                SpringApplication.run(Application.class , args);
            }
        }
        --启动类
        package cn.sgwks.crawlerjd.controller;
        
        import cn.sgwks.crawlerjd.component.Crawler;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RestController;
        
        @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";
            }
        }
        
        
    • 6)编写爬虫的业务逻辑

      • 1、使用工具类创建一个HttpClient对象。

        • @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&s=61&click=0&page=";
          }
          
      • 2、使用HttpClient发送请求,请求就是搜索的url+页码

        • public void doCrawler() {
              try {
                  //1、使用工具类创建一个HttpClient对象。
                  CloseableHttpClient httpClient = HttpsUtils.getHttpClient();
                  //2、使用HttpClient发送请求,请求就是搜索的url+页码
                  for (int i = 0; i < 5; i++) {
                      //7、需要翻页。
                      HttpGet get = new HttpGet(startUrl + (i * 2 + 1));
                      //3.设置访问头,火狐浏览器
                      get.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0");
                      CloseableHttpResponse response = httpClient.execute(get);
                      //3、接收服务端响应html
                      String html = EntityUtils.toString(response.getEntity(), "utf-8");
                      //4、使用Jsoup解析html
                      parseHtml(html);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
          
      • 3、接收服务端响应html

      • 4、使用Jsoup解析html

      • 5、把解析的商品数据封装成Item对象

      • 6、使用dao把商品写入数据库。

        • /**
           * 页面解析的业务逻辑
           *
           * @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) {
                  //解析节点中的商品数据
                  //标准产品,商品信息
                  String spu = element.attr("data-spu");
                  //商品库存量
                  String sku = element.attr("data-sku");
                  //商品标题
                  String title = element.select("div.p-name em").text();
                  //商品价格
                  String price = element.select("div.p-price i").text();
                  //图片
                  String imgUrl = element.select("div.p-img img").attr("source-data-lazy-img");
                  //执行图片下载
                  String imgName = downloadImage(imgUrl,title);
                  //商品的url
                  String url = 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(imgName);
                  item.setUrl(url);
                  //创建时间
                  item.setCreated(new Date());
                  //修改时间
                  item.setUpdated(new Date());
                  //6、使用dao把商品写入数据库。
                  itemDao.save(item);
              }
          }
          
      • 7、需要翻页。并且指定下载图片

        • /**
               * 图片下载
               * @param imgUrl
               * @return
               */
              private String downloadImage(String imgUrl,String title) throws Exception {
                  //创建一个HttpClient对象
                  CloseableHttpClient httpClient = HttpsUtils.getHttpClient();
                  //创建一个HttpGet对象
                  HttpGet get = new HttpGet("https:" + imgUrl);
                  //3.设置访问头,火狐浏览器
                  get.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0");
                  //发送请求
                  CloseableHttpResponse response = httpClient.execute(get);
                  //接收服务端响应的内容。
                  HttpEntity entity = response.getEntity();
                  //需要截取扩展名
                  String extName = imgUrl.substring(imgUrl.lastIndexOf("."));
                  //需要生成文件名。可以使用uuid生成文件名。并去除特殊字符
                  String regEx="[\n`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。, 、?]";
                  String uuid = UUID.randomUUID().toString().substring(0, 5);
                  String prefix = title.replaceAll(regEx, "");
                  String fileName = prefix.substring(0, 15)+ uuid + extName;
                  //需要生成文件名。可以使用uuid生成文件名。
          
                  //String fileName = UUID.randomUUID() + extName;
                  //存放地址 C:\Users\acer\Desktop\jdPhone
                  //创建一个文件输出流,把文件保存到磁盘
                  FileOutputStream fos = new FileOutputStream("C:\\Users\\acer\\Desktop\\jdPhone\\" + fileName);
                  //接收流,把内容保存到磁盘。
                  entity.writeTo(fos);
                  //关闭流
                  fos.close();
                  //关闭Response对象
                  response.close();
                  return fileName;
              }
          
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值