Python_JAVA第二章

Python第二章

一、webmagic入门程序

  1. 使用方法

    • 1)创建工程

    • 2)添加jar包

      • <?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-webmagic</artifactId>
            <version>1.0-SNAPSHOT</version>
        
        
            <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>
                <!--WebMagic-->
                <dependency>
                    <groupId>us.codecraft</groupId>
                    <artifactId>webmagic-core</artifactId>
                    <version>0.7.3</version>
                </dependency>
                <dependency>
                    <groupId>us.codecraft</groupId>
                    <artifactId>webmagic-extension</artifactId>
                    <version>0.7.3</version>
                </dependency>
                <!--WebMagic对布隆过滤器的支持-->
                <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>16.0</version>
                </dependency>
            </dependencies>
        </project>
        
    • 3)创建一个PageProcess对象,需要实现PageProcessor接口创建实现类。

      • package cn.sgwks.webmagic;
        
        import us.codecraft.webmagic.Page;
        import us.codecraft.webmagic.Site;
        import us.codecraft.webmagic.Spider;
        import us.codecraft.webmagic.processor.PageProcessor;
        import us.codecraft.webmagic.selector.Html;
        import us.codecraft.webmagic.selector.Selectable;
        
        public class MyPageProcessor implements PageProcessor {
            /**
             * 页面解析的业务逻辑
             * @param page 下载结果封装到Page对象中
             */
            @Override
            public void process(Page page) {
                //抓取京东首页打印html
                Html html = page.getHtml();
                //把Html对象装换成String对象
                String strHtml = html.toString();
                //<title>京东商场</title>
                System.out.println(strHtml.contains("title3"));//false
                //把解析结果传递给pipeline
                /*ResultItems resultItems = page.getResultItems();
                resultItems.put("html", strHtml);*/
                //page.putField("html", strHtml);
                //获取当前访问的链接地址
                Selectable url = page.getUrl();
                System.out.println(url);//https://www.jd.com/
            }
        
            /**
             * 抓取站点信息的参数配置
             * 抓取的频率
             * 超时时间
             * 编码格式
             * 。。。
             * @return
             */
            @Override
            public Site getSite() {
                return Site.me();
            }
        
            public static void main(String[] args) {
                Spider.create(new MyPageProcessor())
                        .addUrl("https://www.jd.com/")
                        //启动爬虫
                        //同步方法,在当前线程中启动爬虫 .run();
                        //异步方法,创建新的线程启动爬虫
                        .start();
            }
        }
        
    • 4)在实现类中实现页面解析的业务逻辑。

    • 5)创建一个main方法,在方法中初始化爬虫并启动。

二、组件介绍

  1. Downloader组件
    下载器组件,从互联网现在html页面,并封装成Page对象,传递给PageProcess组件。
    就是使用HttpClient实现。
    如果没有特殊需求,此组件无需自定义。

  2. PageProcess组件
    爬虫的核心组件,使用爬虫时,此组件 必须自定义。
    1)Site
    站点参数的配置。
    抓取的间隔时间
    超时时间
    编码格式
    UserAgent

    一般情况下使用默认参数即可。
    返回一个Site对象,使用Site.me();
    2)Page
    Html:Downloader组件抓取的页面封装成Html对象。也是一个Selectable对象
    url:当前页面对应的url地址,是一个Selectable对象
    ResultItems:从PageProcess向pipeline传递数据的对象。
    本质上就是一个map
    Request:一个Request就是一个url。
    page对象:
    addTargetRequest:向队列中添加一个url
    addTargetRequests:向队列中批量添加url

    3)Selectable

    • – Html对象支持使用Jsoup原生api

    • css选择器解析页面:
      css()、$()

      • package cn.sgwks.webmagic;
        
        import org.jsoup.nodes.Document;
        import org.jsoup.nodes.Element;
        import org.jsoup.select.Elements;
        import us.codecraft.webmagic.Page;
        import us.codecraft.webmagic.Site;
        import us.codecraft.webmagic.Spider;
        import us.codecraft.webmagic.processor.PageProcessor;
        import us.codecraft.webmagic.selector.Html;
        import us.codecraft.webmagic.selector.Selectable;
        
        import java.util.List;
        
        public class MyPageProcessorCss implements PageProcessor {
            /**
             * 页面解析的业务逻辑
             * @param page 下载结果封装到Page对象中
             */
            @Override
            public void process(Page page) {
                //抓取传智播客首页打印html
                Html html = page.getHtml();
                //解析title
                Document document = html.getDocument();
                Elements elements = document.getElementsByTag("title");
                for (Element element : elements) {
                    System.out.println(element);// <title>京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!</title>
                }
                Elements elements1 = document.select("title");
                System.out.println(elements1);// <title>京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!</title>
        
                //使用css选择器解析1
                Selectable title = html.css("title", "text");
                System.out.println(title);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!
                //使用css选择器解析2
                Selectable title2 = html.$("title", "text");
                System.out.println(title2);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!
                //把Selectable对象转换成String对象
                String title_s = title.get();
                System.out.println(title_s);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!
        
                //选择meta
                //这里是获取第一个meta节点的标签
                String meta = html.css("meta").get();
                System.out.println(meta);//<meta charset="utf8" version="1">
                //获取全部带meta节点的标签
                List<String> metaList = html.$("meta").all();
                /*[<meta charset="utf8" version="1">, <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">,
                 <meta name="description" content="京东JD.COM-专业的综合网上购物商城,销售家电、数码通讯、电脑、家居百货、服装服饰、母婴、图书、食品等数万个品牌优质商品.便捷、诚信的服务,为您提供愉悦的网上购物体验!">,
                 <meta name="Keywords" content="网上购物,网上商城,手机,笔记本,电脑,MP3,CD,VCD,DV,相机,数码,配件,手表,存储卡,京东">,
                 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">, <meta name="renderer" content="webkit">]*/
                System.out.println(metaList);
                //取链接地址列表,得到第一个链接地址
                String links = html.css("ul.fr").links().get();
                System.out.println(links);
                //取全部链接地址列表
                List<String> linkList = html.css("ul.fr").links().all();
                System.out.println(linkList);//[, , https://order.jd.com/center/list.action, https://home.jd.com/, https://vip.jd.com/, https://b.jd.com/]
        
                //nodes方法,返回一个Selectable对象的集合
                List<Selectable> nodes = html.css("meta").nodes();
                System.out.println(nodes);
        
                //取节点的属性,去单个第一个得到的数据
                String metaName = html.css("meta", "name").get();
                System.out.println("该meta节点的属性是否包含name:"+metaName);
                //取节点的属性,包含得到全部数据
                List<String> metaNameList = html.$("meta", "name").all();
                System.out.println(metaNameList);//[, viewport, description, Keywords, , renderer]
            }
        
            /**
             * 抓取站点信息的参数配置
             * 抓取的频率
             * 超时时间
             * 编码格式
             * 。。。
             * @return
             */
            @Override
            public Site getSite() {
                return Site.me();
            }
        
            public static void main(String[] args) {
                Spider.create(new MyPageProcessorCss())
                        .addUrl("https://www.jd.com/")
                        //启动爬虫
                        //同步方法,在当前线程中启动爬虫 .run();
                        //异步方法,创建新的线程启动爬虫
                        .start();
            }
        }
        
    • 使用xpath解析页面:
      xpath()
      解析xml的一种方法。

      • package cn.sgwks.webmagic;
        
        import us.codecraft.webmagic.Page;
        import us.codecraft.webmagic.Site;
        import us.codecraft.webmagic.Spider;
        import us.codecraft.webmagic.processor.PageProcessor;
        import us.codecraft.webmagic.selector.Html;
        import us.codecraft.webmagic.selector.Selectable;
        
        import java.util.List;
        
        public class MyPageProcessorXpath implements PageProcessor {
            /**
             * 页面解析的业务逻辑
             * @param page 下载结果封装到Page对象中
             */
            @Override
            public void process(Page page) {
                Html html = page.getHtml();
                //使用xpath解析title
                String title = html.xpath("title").get();
                System.out.println(title);//<title>京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!</title>
                // text()就代表去标签内的文本值
                String title2 = html.xpath("title/text()").get();
                System.out.println(title2);//京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!
        
                //取第一个link节点的href属性,属性前要加 @
                String links = html.xpath("link/@href").get();
                System.out.println(links);// //static.360buyimg.com
                //取全部link节点的href属性
                List<String> linkList = html.xpath("link/@href").all();
                /*[//static.360buyimg.com, //misc.360buyimg.com, //img10.360buyimg.com, //img11.360buyimg.com,
                // img12.360buyimg.com, //img13.360buyimg.com, //img14.360buyimg.com,
                // img20.360buyimg.com, //img30.360buyimg.com, //d.3.cn, //d.jd.com,
                // www.jd.com/favicon.ico,
                // misc.360buyimg.com/mtd/pc/index_2019/1.0.0/static/css/first-screen.chunk.css,
                // misc.360buyimg.com/mtd/pc/index_2019/1.0.0/static/css/index.chunk.css]*/
                System.out.println(linkList);
        
                //取京东的LOGO里面的h1标记内容
                String jdNameStr = html.xpath("/html/body/div[1]/div[4]/div/div[1]/h1/a/text()").get();
                System.out.println(jdNameStr);//京东
            }
        
            /**
             * 抓取站点信息的参数配置
             * 抓取的频率
             * 超时时间
             * 编码格式
             * 。。。
             * @return
             */
            @Override
            public Site getSite() {
                return Site.me();
            }
        
            public static void main(String[] args) {
                Spider.create(new MyPageProcessorXpath())
                        .addUrl("https://www.jd.com/")
                        //启动爬虫
                        //同步方法,在当前线程中启动爬虫 .run();
                        //异步方法,创建新的线程启动爬虫
                        .start();
            }
        }
        
    • 使用正则表达式
      regex()
      解析页面不推荐使用正则表达式。推荐使用css选择器。

    • 把Selectable对象转换成字符串:

      • toString()、get()
        如果结果是一个列表的话,只取第一个元素。
      • all()
        可以吧Selectable转换成字符串,转换成一个List
      • links()
        取当前节点中所有的链接地址。
      • nodes()
        返回一个List对象
  3. pipeline

    • 数据持久化组件。
    • 框架默认提供三个实现。
      ConsolePipeline:框架默认使用,向控制台输出
      FilePipeline:向磁盘输出,可以把抓取到结果保存到磁盘。
      JsonFilePipeline:向磁盘输出保存json数据。
    • 使用方法:
      1)创建一个FilePipeline对象
      2)给FilePipeline对象设置文件输出路径。
      3)使用Spider工具类初始化爬虫时装配组件。
    • 如果需要把数据保存到数据库,需要自定义pipeline。
      需要实现Pipeline接口即可。
  4. Scheduler

    • url队列。
      实现方法:
      QueueScheduler:java内存队列(默认使用)
      FileCacheQueueScheduler:使用文件做缓存的队列。
      RedisScheduler:使用redis做队列,解决分布式爬虫问题

    • 向队列中添加url时需要去重

      • 1)使用java的hashset去重(默认使用)

      • 2)使用redis的set去重

      • 3)使用布隆过滤器去重。

        • 优点:
              成本低
              效率高
              速度快
              占内存小
              缺点:
              可能误判
              不能删除元素
          
      • ​ 布隆过滤器的使用方法:

        • 1)创建一个QueryScheduler对象
        • 2)创建一个布隆过滤器的对象。
        • 3)给Scheduler对象设置布隆过滤器。
        • 4)使用Spider初始化爬虫时,使用自定义的scheduler。
      • 布隆过滤器需要一个jar包guava

      • package cn.sgwks.webmagic;
        
        import us.codecraft.webmagic.*;
        import us.codecraft.webmagic.pipeline.ConsolePipeline;
        import us.codecraft.webmagic.pipeline.FilePipeline;
        import us.codecraft.webmagic.processor.PageProcessor;
        import us.codecraft.webmagic.scheduler.BloomFilterDuplicateRemover;
        import us.codecraft.webmagic.scheduler.QueueScheduler;
        import us.codecraft.webmagic.selector.Html;
        
        import java.util.List;
        
        public class MyPageProcessorGuava implements PageProcessor {
            /**
             * 页面解析的业务逻辑
             * @param page 下载结果封装到Page对象中
             */
            @Override
            public void process(Page page) {
                //取页面内容
                Html html = page.getHtml();
                //把html文本信息传递给pipeline
                page.putField("html", html.get());
                String jdName = html.css("title", "text").get();
                //把title的信息传递给pipeline
                page.putField("jdName", jdName);
                //取页面中所有的url
                List<String> linkList = html.links().all();
                //把地址添加到访问队列
                page.addTargetRequests(linkList);
            }
        
            /**
             * 抓取站点信息的参数配置
             * 抓取的频率
             * 超时时间
             * 编码格式
             * 。。。
             * @return
             */
            @Override
            public Site getSite() {
                Site site = Site.me();
                //设置抓取频率,一秒抓一次
                site.setSleepTime(1000);
                return site;
            }
            /**
             * ConsolePipeline:框架默认使用,向控制台输出
             * FilePipeline:向磁盘输出,可以把抓取到结果保存到磁盘。
             * JsonFilePipeline:向磁盘输出保存json数据。
             */
            public static void main(String[] args) {
                //创建一个Pipeline对象
                FilePipeline filePipeline = new FilePipeline();
                filePipeline.setPath("C:\\Users\\acer\\Desktop\\sgw");
                //创建一个Scheduler对象
                QueueScheduler scheduler = new QueueScheduler();
                //创建一个布隆过滤器对象
                //构造参数就是布隆过滤器的容量。
                BloomFilterDuplicateRemover remover = new BloomFilterDuplicateRemover(1000000);
                scheduler.setDuplicateRemover(remover);
        }
        
  5. Spider工具类
    初始化爬虫,可以装配各个组件,同时设置一些参数。
    启动爬虫。

    • Spider.create(new MyPageProcessorGuava())
                      //设置pipeline组件
                      .addPipeline(filePipeline)
                      //向控制台输出,如果不设置自定义pipeline组件就不用设置ConsolePipeline,改组件是默认的
                      .addPipeline(new ConsolePipeline())
                      //设置自定义的scheduler
                      .setScheduler(scheduler)
                      //设置起始的url
                      .addUrl("https://www.jd.com")
                      .start();
      

三、综合案例

  1. 需求
    抓取51job网站的招聘信息,把招聘信息保存到数据库。

  2. 功能分析

    • 数据库:
      job_info

      CREATE TABLE `job_info` (
        `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
        `company_name` varchar(100) DEFAULT NULL COMMENT '公司名称',
        `company_addr` varchar(200) DEFAULT NULL COMMENT '公司联系方式',
        `company_info` text COMMENT '公司信息',
        `job_name` varchar(100) DEFAULT NULL COMMENT '职位名称',
        `job_addr` varchar(50) DEFAULT NULL COMMENT '工作地点',
        `job_info` text COMMENT '职位信息',
        `salary_min` int(10) DEFAULT NULL COMMENT '薪资范围,最小',
        `salary_max` int(10) DEFAULT NULL COMMENT '薪资范围,最大',
        `url` varchar(150) DEFAULT NULL COMMENT '招聘信息详情页',
        `time` varchar(10) DEFAULT NULL COMMENT '职位最近发布时间',
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='招聘信息';
      
    • 步骤:
      1)访问职位列表页面
      2)解析职位详情的url地址,添加到队列中。
      3)解析“下一页”的url,添加到队列中。
      4)解析页面的业务逻辑中,判断是列表页面还是详情页面。
      5)如果是列表页面,执行2-3的动作。
      6)如果是详情页面,解析职位详情信息
      7)把职位详情保存到数据库。

  3. 功能实现

    • 1)创建工程
      1、创建一个Springboot工程
      2、添加依赖
      添加SpringDataJpa的起步依赖
      添加web模块的起步依赖
      mysql数据库驱动
      webmagic相关的jar包。

      • <?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-51job</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>
        
                <!--WebMagic核心包-->
                <dependency>
                    <groupId>us.codecraft</groupId>
                    <artifactId>webmagic-core</artifactId>
                    <version>0.7.3</version>
                    <exclusions>
                        <exclusion>
                            <groupId>org.slf4j</groupId>
                            <artifactId>slf4j-log4j12</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <!--WebMagic扩展-->
                <dependency>
                    <groupId>us.codecraft</groupId>
                    <artifactId>webmagic-extension</artifactId>
                    <version>0.7.3</version>
                </dependency>
                <!--WebMagic对布隆过滤器的支持-->
                <dependency>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                    <version>16.0</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、对应数据库的表创建Entity、dao

      • package cn.sgwks.crawlerjob.entity;
        
        import javax.persistence.*;
        
        @Entity
        @Table(name = "job_info")
        public class Job {
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Column(name="id")
            private Long id;
            @Column(name="company_name")
            private String companyName;
            @Column(name="company_addr")
            private String companyAddr;
            @Column(name="company_info")
            private String companyInfo;
            @Column(name="job_name")
            private String jobName;
            @Column(name="job_addr")
            private String jobAddr;
            @Column(name="job_info")
            private String jobInfo;
            @Column(name="salary_min")
            private Integer salaryMin;
            @Column(name="salary_max")
            private Integer salaryMax;
            @Column(name="url")
            private String url;
            @Column(name="time")
            private String time;
        
            public Long getId() {
                return id;
            }
        
            public void setId(Long id) {
                this.id = id;
            }
        
            public String getCompanyName() {
                return companyName;
            }
        
            public void setCompanyName(String companyName) {
                this.companyName = companyName;
            }
        
            public String getCompanyAddr() {
                return companyAddr;
            }
        
            public void setCompanyAddr(String companyAddr) {
                this.companyAddr = companyAddr;
            }
        
            public String getCompanyInfo() {
                return companyInfo;
            }
        
            public void setCompanyInfo(String companyInfo) {
                this.companyInfo = companyInfo;
            }
        
            public String getJobName() {
                return jobName;
            }
        
            public void setJobName(String jobName) {
                this.jobName = jobName;
            }
        
            public String getJobAddr() {
                return jobAddr;
            }
        
            public void setJobAddr(String jobAddr) {
                this.jobAddr = jobAddr;
            }
        
            public String getJobInfo() {
                return jobInfo;
            }
        
            public void setJobInfo(String jobInfo) {
                this.jobInfo = jobInfo;
            }
        
            public Integer getSalaryMin() {
                return salaryMin;
            }
        
            public void setSalaryMin(Integer salaryMin) {
                this.salaryMin = salaryMin;
            }
        
            public Integer getSalaryMax() {
                return salaryMax;
            }
        
            public void setSalaryMax(Integer salaryMax) {
                this.salaryMax = salaryMax;
            }
        
            public String getUrl() {
                return url;
            }
        
            public void setUrl(String url) {
                this.url = url;
            }
        
            public String getTime() {
                return time;
            }
        
            public void setTime(String time) {
                this.time = time;
            }
        }
        
      • package cn.sgwks.crawlerjob.dao;
        
        import cn.sgwks.crawlerjob.entity.Job;
        import org.springframework.data.jpa.repository.JpaRepository;
        
        public interface JobDao extends JpaRepository<Job,Long> {
        }
        
        

      ​ 5、创建一个引导类。

      • package cn.sgwks.crawlerjob;
        
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        
        @SpringBootApplication
        public class JobApplication {
            public static void main(String[] args) {
                SpringApplication.run(JobApplication.class, args);
            }
        }
        
        package cn.sgwks.crawlerjob.controller;
        
        import cn.sgwks.crawlerjob.component.JobSpider;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RestController;
        
        @RestController
        public class JobController {
            @Autowired
            private JobSpider jobSpider;
        
            @RequestMapping("/start")
            public String startCrawler() {
                jobSpider.doCrawler();
                return "OK";
            }
        }
        

      ​ 6、实现爬虫的业务逻辑

  4. 爬虫的业务逻辑
    需要自定义的组件:

    • PageProcess

      • 工具类 MathSalary

      • package cn.sgwks.crawlerjob.utils;
        
        public class MathSalary {
        
        
            /**
             * 获取薪水范围
             *
             * @param salaryStr
             * @return
             */
            public static Integer[] getSalary(String salaryStr) {
                //声明存放薪水范围的数组
                Integer[] salary = new Integer[2];
        
                //"500/天"
                //0.8-1.2万/月
                //5-8千/月
                //5-6万/年
                String date = salaryStr.substring(salaryStr.length() - 1, salaryStr.length());
                //如果是按天,则直接乘以240进行计算
                if (!"月".equals(date) && !"年".equals(date)) {
                    salaryStr = salaryStr.substring(0, salaryStr.length() - 2);
                    salary[0] = salary[1] = str2Num(salaryStr, 240);
                    return salary;
                }
        
                String unit = salaryStr.substring(salaryStr.length() - 3, salaryStr.length() - 2);
                String[] salarys = salaryStr.substring(0, salaryStr.length() - 3).split("-");
        
        
                salary[0] = mathSalary(date, unit, salarys[0]);
                salary[1] = mathSalary(date, unit, salarys[1]);
        
                return salary;
        
        
            }
        
            //根据条件计算薪水
            private static Integer mathSalary(String date, String unit, String salaryStr) {
                Integer salary = 0;
        
                //判断单位是否是万
                if ("万".equals(unit)) {
                    //如果是万,薪水乘以10000
                    salary = str2Num(salaryStr, 10000);
                } else {
                    //否则乘以1000
                    salary = str2Num(salaryStr, 1000);
                }
        
                //判断时间是否是月
                if ("月".equals(date)) {
                    //如果是月,薪水乘以12
                    salary = str2Num(salary.toString(), 12);
                }
        
                return salary;
            }
        
        
            private static int str2Num(String salaryStr, int num) {
                try {
                    // 把字符串转为小数,必须用Number接受,否则会有精度丢失的问题
                    Number result = Float.parseFloat(salaryStr) * num;
                    return result.intValue();
                } catch (Exception e) {
                }
                return 0;
            }
        }
        

      ​ 1)判断是列表页面还是详情页面
      ​ 2)如果是列表页面,解析职位详情url列表
      ​ 3)把列表添加到访问队列中。
      ​ 4)解析下一页的url,添加到访问队列中。
      ​ 5)如果是详情页面
      ​ 6)解析职位详情信息
      ​ 7)封装到Job对象中。
      ​ 8)把job对象传递给pipeline。

      • package cn.sgwks.crawlerjob.component;
        
        import cn.sgwks.crawlerjob.entity.Job;
        import cn.sgwks.crawlerjob.utils.MathSalary;
        import org.jsoup.nodes.Document;
        import org.springframework.stereotype.Component;
        import us.codecraft.webmagic.Page;
        import us.codecraft.webmagic.Site;
        import us.codecraft.webmagic.processor.PageProcessor;
        import us.codecraft.webmagic.selector.Html;
        import us.codecraft.webmagic.selector.Selectable;
        
        import java.util.List;
        import java.util.stream.Collectors;
        import java.util.stream.Stream;
        
        @Component
        public class JobPageProcessor implements PageProcessor {
            @Override
            public void process(Page page) {
                Html html = page.getHtml();
                //1)判断是列表页面还是详情页面
                List<Selectable> nodes = html.css("#resultList div.el p.t1").nodes();
                //如果得到的集合数量大于0就说明是列表页面
                if (nodes.size() > 0) {
                    //2)如果是列表页面,解析职位详情url列表
                    List<String> urlList = html.css("#resultList div.el p.t1").links().all();
                    //3)把列表添加到访问队列中。
                    page.addTargetRequests(urlList);
                    //4)解析下一页的url,添加到访问队列中。
                    String nextPageUrl = html.css("#rtNext", "href").get();
                    page.addTargetRequest(nextPageUrl);
                    //跳过pipeline处理
                    page.setSkip(true);
                } else {
                    parseJobInfo(page);
                    //5)如果是详情页面
                    //6)解析职位详情信息
                    //7)封装到Job对象中。
                    //8)把job对象传递给pipeline。
                }
            }
        
            @Override
            public Site getSite() {
                //或者 return new Site();
                return Site.me();
            }
        
            /**
             * 职位详情解析业务逻辑
             *
             * @param page
             */
            private void parseJobInfo(Page page) {
                //取html对象
                Html html = page.getHtml();
                //公司名称
                String compName = html.css("div.tHeader.tHjob p.cname a.catn", "text").get();
                //公司联系方式
                String compAddr = html.css(".tCompany_main > div:nth-child(2) > div:nth-child(2) > p:nth-child(1)", "text").get();
                //公司信息
                String compInfo = html.css("div.tBorderTop_box div.tmsg.inbox", "text").get();
                //职位名称
                String jobName = html.css("h1", "text").get();
                //工作地点
                // 深圳-南山区  |  3-4年经验  |  大专  |  招若干人  |  03-06发布 要分割
                String jobLine = html.css("div.tHeader.tHjob div.in p.msg.ltype", "title").get();
                //要转义
                String[] elems = jobLine.split("\\|");
                String jobAddr = "";
                //如果得到的数组长度大有0,取第一个
                if (elems.length > 0) {
                    jobAddr = elems[0];
                }
        
                //职位信息,这里也可以利用 html的getDocument()来获取指定的标签也可
                Document document = html.getDocument();
                String jobInfo = document.select("div.tCompany_main div.tBorderTop_box div.bmsg.job_msg.inbox").text();
        
                //薪资范围,最小
        
                String salaryStr = html.css(".cn > strong:nth-child(2)", "text").get();
                System.out.println(salaryStr);
                //如果未获取到薪资
                Integer[] salary = MathSalary.getSalary(salaryStr);
                int salaryMin = salary[0];
                //薪资范围,最大
                int salaryMax = salary[1];
                //招聘信息详情页url
                String jobUrl = page.getUrl().get();
                //职位最近发布时间
                List<String> timeList = Stream.of(elems)
                        .filter(e -> e.contains("发布"))
                        .collect(Collectors.toList());
                String time = "";
                if (timeList.size() > 0) {
                    time = timeList.get(0);
                }
                //把职位信息封装到Job对象中
                Job job = new Job();
                job.setCompanyName(compName);
                job.setCompanyAddr(compAddr);
                job.setCompanyInfo(compInfo);
                job.setJobName(jobName);
                job.setJobAddr(jobAddr);
                job.setJobInfo(jobInfo);
                job.setSalaryMin(salaryMin);
                job.setSalaryMax(salaryMax);
                job.setUrl(jobUrl);
                job.setTime(time);
                //把job对象传递给pipeline
                page.putField("job", job);
            }
        }
        
    • Pipeline:把数据保存到数据库

      • package cn.sgwks.crawlerjob.component;
        
        import cn.sgwks.crawlerjob.dao.JobDao;
        import cn.sgwks.crawlerjob.entity.Job;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Component;
        import org.springframework.transaction.annotation.Transactional;
        import us.codecraft.webmagic.ResultItems;
        import us.codecraft.webmagic.Task;
        import us.codecraft.webmagic.pipeline.Pipeline;
        
        @Component
        public class JobPipelie implements Pipeline {
            @Autowired
            private JobDao jobDao;
        
            @Override
            //开启事务管理
            @Transactional
            public void process(ResultItems resultItems, Task task) {
                Job job = resultItems.get("job");
                //把job保存到数据库
                jobDao.save(job);
            }
        }
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值