WebMagic爬虫框架(爬取前程无忧网站的招聘信息保存到mysql数据库)

@


WebMagic框架包含四个组件,PageProcessorSchedulerDownloaderPipeline
这四大组件对应爬虫生命周期中的处理、管理、下载和持久化等功能。
这四个组件都是Spider中的属性,爬虫框架通过Spider启动和管理。
WebMagic总体架构图
在这里插入图片描述

一,WebMagic的四大组件

PageProcessor 负责解析页面,抽取有用信息,以及发现新的链接。需要自己定义。
Scheduler 负责管理待抓取的URL,以及一些去重的工作。一般无需自己定制Scheduler。
Pipeline 负责抽取结果的处理,包括计算、持久化到文件、数据库等。
Downloader 负责从互联网上下载页面,以便后续处理。一般无需自己实现。

二,用于数据流转的对象

Request 是对URL地址的一层封装,一个Request对应一个URL地址。
Page 代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。
ResultItems 相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。

三,项目开始前的热身(解析页面的方式)

项目maven添加了下面要求的maven坐标之后就可以写下面的测试代码了。
下面的测试很清楚的讲解了解析页面的方式,以及爬虫的执行。

  • page.putField()是把爬取到的数据添加到了ResultItems中,默认在控制台打印出来。
public class JobProcessor implements PageProcessor {
    //解析页面
    public void process(Page page) {
        //解析返回的数据page,并且把解析的结果放在resultItems中

        //css选择器解析
        //page.putField("爬取内容",page.getHtml().css("span.service_txt").all());//all()是返回所有数据,get()是返回第一条数据

        //XPath解析
        //page.putField("xpath方法解析结果",page.getHtml().xpath("//div[@id=J_cate]/ul/li/a").all());

        //正则表达式解析(筛选内容带“装”字的所有信息)
        page.putField("正则",page.getHtml().css("div#J_cate ul li a").regex(".*装.*").all());

        //获取链接
//        page.addTargetRequests(page.getHtml().css("div#shortcut-2014 div.w ul.fl li#ttbar-home a").links().all());
//        page.putField("url",page.getHtml().css("div#shortcut div ul li a span").all());
    }

    private Site site=Site.me()
            .setCharset("utf8") //设置编码
            .setTimeOut(10000)  //设置超时时间 单位是ms毫秒
            .setRetrySleepTime(3000)  //设置重试的时间间隔
            .setSleepTime(3);  //设置重试次数
    public Site getSite() {
        return site;
    }
/*设置request请求方式
Request requests=new Request("http://www.12371.cn/cxsm/gzbs/");
        requests.setMethod(HttpConstant.Method.GET);

        Spider.create(new JobProcessor())
                //.addUrl(url)
                .addRequest(requests)
                .setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000)))
                .thread(5)
                .addPipeline(this.newsData)
                .run();
*/
    //主函数,执行爬虫
    public static void main(String[] args) {
        Spider spider=Spider.create(new JobProcessor()).addUrl("https://www.jd.com")//设置爬取数据的页面
//                .addPipeline(new FilePipeline("D:\\result"))//使用Pipeline保存数据到指定文件夹中,自动生成文件
                .thread(5) //多线程进行爬取,5个多线程,速度更快
                .setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(10000000)));//设置布隆去重过滤器,指定最多对1000万数据进行去重操作
                //默认HashSet去重
                //Scheduler scheduler=spider.getScheduler();
                spider.run();//run()执行爬虫
    }
}

四,SpringBoot项目环境搭建

首先搭建好一个springboot项目,加入mysqlmybatiswebmagic的核心依赖以及扩展依赖,以及添加WebMagic对布隆过滤器的支持的依赖。

  <!--springmvc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>


        <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>
        <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>
        <!--工具包 StringUtils-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>

五,配置文件

resources文件夹新建log4j.properties文件配置日志

log4j.rootLogger=INFO,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

在新建application.properties文件配置数据库

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=1234

六,Let's go WebMagic!

1,启动类

@SpringBootApplication
@MapperScan("com.qianlong.dao")	//扫描mapper文件
@EnableScheduling //开启定时任务,定时抓取数据
@ComponentScan(value = "com.qianlong")//包扫描

public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

2,实体类(存储到数据库表的字段)

public class JobInfo {
    private Integer id;
    private String companyName;
    private String companyAddr;
    private String companyInfo;
    private String jobName;
    private String jobAddr;
    private String salary;
    private String time;
    }

3,爬虫类

package com.qianlong.task;

import com.qianlong.entity.JobInfo;
import org.jsoup.Jsoup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
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.scheduler.BloomFilterDuplicateRemover;
import us.codecraft.webmagic.scheduler.QueueScheduler;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;

import java.util.List;

@Component
public class JobProcessor implements PageProcessor {
    //前程无忧网站的职位列表地址
    private String url="https://search.51job.com/list/170200,000000,0000,00,9,99,%2B,2,1.html?lang=c&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare=";
    @Override
    public void process(Page page) {
    //解析页面,获取招聘信息详情的url地址
        List<Selectable> list = page.getHtml().css("div#resultList div.el").nodes();
        //判断集合是否为空
        if(list.size()==0){
            //如果为空,表示这是招聘详情页,解析页面,获取招聘详情信息,保存数据
            this.saveJobInfo(page);
        }else {
            //如果不为空,表示这是列表页,解析出详情页的url地址,放到任务队列中
            for(Selectable selectable:list){
                String jobInfoUrl = selectable.links().toString();
                //把获取到的详情页的url地址放到任务队列中
                page.addTargetRequest(jobInfoUrl);
            }
            //获取下一页按钮的url
            String bkUrl=page.getHtml().css("div.p_in li.bk").nodes().get(1).links().toString();//get(1)拿到第二个
            //把下一页的url放到任务队列中
            page.addTargetRequest(bkUrl);
        }
    }

    //解析页面,获取招聘详情信息,保存数据
    private void saveJobInfo(Page page) {
        //创建招聘详情对象
        JobInfo jobInfo=new JobInfo();
        //拿到解析的页面
        Html html = page.getHtml();
        //获取数据,封装到对象中
        //两种获取的方法,一种是直接html.css,另一种是使用Jsoup.parse解析html字符串
        jobInfo.setCompanyName(html.css("div.cn p.cname a","text").toString());
        String addrStr = Jsoup.parse(html.css("div.cn p.msg").toString()).text();
        String addr=addrStr.substring(0,addrStr.indexOf("|"));

        jobInfo.setCompanyAddr(addr);
        jobInfo.setCompanyInfo(html.css("div.tmsg","text").toString());
        jobInfo.setUrl(page.getUrl().toString());
        jobInfo.setJobName(Jsoup.parse(html.css("div.cn h1","title").toString()).text());
        jobInfo.setJobAddr(addr);
        jobInfo.setSalary(Jsoup.parse(html.css("div.cn strong").toString()).text());


        //把结果保存起来
        page.putField("jobInfo",jobInfo);
    }

    private Site site=Site.me()
            .setCharset("gbk")//设置编码(页面是什么编码就设置成什么编码格式的)
            .setTimeOut(10*1000)//设置超时时间
            .setRetrySleepTime(3000)//设置重试的间隔时间
            .setRetryTimes(3);//设置重试的次数
    @Override
    public Site getSite() {
        return site;
    }
	//这里注入SaveData
    @Autowired
    private SaveData saveData;
    //initialDelay当任务启动后,等多久执行方法
    //fixedDelay每个多久执行方法
    @Scheduled(initialDelay = 1000,fixedDelay = 100*1000)
    public void process(){
        Spider.create(new JobProcessor())
                .addUrl(url)
                .setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000)))
                .thread(10)
                .addPipeline(this.saveData)//指定把爬取的数据保存到SaveData类的ResultItems中
                .run();
    }
}

4,获取爬到的数据并保存到数据库

前面爬取的数据(封装到了实体类)都保存在了ResultItems 对象中
在这里插入图片描述
这里取出前面保存的数据(实体类),然后把数据存到数据库

package com.qianlong.task;
import com.qianlong.entity.JobInfo;
import com.qianlong.service.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;

@Component
public class SaveData implements Pipeline {
    @Autowired
    Service service;
    @Override
    public void process(ResultItems resultItems, Task task) {
        //获取封装好的招聘详情对象
        JobInfo jobInfo=resultItems.get("jobInfo");
        if(jobInfo!=null){
            //保存数据到数据库中
            service.saveJobInfo(jobInfo);
        }
    }
}

5,dao和service

public interface Dao {
    @Insert(value = "insert into jobinfo(companyName,companyAddr,companyInfo,jobName,jobAddr,salary,url) values(#{companyName},#{companyAddr},#{companyInfo},#{jobName},#{jobAddr},#{salary},#{url});")
    int saveJobInfo(JobInfo jobInfo);
}
public interface Service {
    int saveJobInfo(JobInfo jobInfo);
}
@Service
public class ServiceImpl implements Service {

    @Autowired
    private Dao dao;
    @Override
    public int saveJobInfo(JobInfo jobInfo) {
        int i = dao.saveJobInfo(jobInfo);
        return i;
    }
}

然后运行启动类,控制台出现下图就是爬取成功了
在这里插入图片描述
爬取的数据保存到数据库成功
在这里插入图片描述

七,后话

至于WebMagic的完整使用,还需要涉及到代理服务器,因为有一些网站是禁止爬取的,它会查到你的ip地址并给你禁掉,所以这时就需要代理服务器。以及爬取数据的去重问题,还要借助一些其他的工具平台进行处理整合,所以,有待完善。
在这里插入图片描述
每天进步一点点,有问题留言兄弟盟!

posted @ 2020-03-08 17:17  你樊不樊  阅读(1016)  评论(1编辑  收藏  举报