WebMagic学习笔记

本文介绍了WebMagic的四大组件:Downloader、PageProcessor、Scheduler和Pipeline,展示了如何实现PageProcessor抓取和解析网页数据,并通过SpringDataPipeline将数据存入数据库。实例代码详细演示了如何配置和使用WebMagic进行网页抓取和数据处理。
摘要由CSDN通过智能技术生成

1、WebMagic的四大组件

  1. Downloader
    Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了Apache HttpClient作为下载工具。

  2. PageProcessor
    PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。

  3. Scheduler
    Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。除非项目有一些特殊的分布式需求,否则无需自己定制Scheduler。

  4. Pipeline
    Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案。Pipeline定义了结果保存的方式,如果你要保存到指定数据库,则需要编写对应的Pipeline。对于一类需求一般只需编写一个Pipeline。

2、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>

3、WebMagic的使用

  1. 第一步,需要先实现PageProcessor接口,并实现process与getSite方法,process方法主要用来选择页面的数据并放入page对象中。具体案例如下:
    JobProcessor类,用于抓取并解析网页内容获取需要的数据
@Component
public class JobProcessor implements PageProcessor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /** 自定以的数据处理类,该类继承了Pipeline接口,并实现了其process方法 */
    @Autowired
    private SpringDataPipeline springDataPipeline;

    /** 需要抓取的url列表页 */
    private String url = "https://search.51job.com/list/000000,000000,0000,32%252C01,9,99," +
            "Java,2,1.html?lang=c&stype=&postchannel=0000&workyear=99&cotype=99&degreefrom=99" +
            "&jobterm=99&companysize=99&providesalary=99&lonlat=0%2C0&radius=-1&ord_field=0" +
            "&confirmdate=9&fromType=&dibiaoid=0&address=&line=&specialarea=00&from=&welfare=";


    /** 设置请求的页面解析的编码,请求的超时时间,每台服务的请求时间与重试次数 */
    private Site site = Site.me()
            .setCharset("gbk")
            .setTimeOut(10000)
            .setRetrySleepTime(3000)
            .setRetryTimes(3);

    @Override
    public void process(Page page) {

        //解析页面,获取招聘信息详情页的url地址
        List<Selectable> nodes = page.getHtml().css("div#resultList div.el").nodes();
        //判断获取到的集合是否weikong,如果为空表示这个招聘详情页,如果不为空则表示这是列表页
        if (nodes == null || nodes.size() == 0) {
            // 如果为空,则表示这是招聘详情页,解析页面获取招聘详情信息保存数据
            this.saveJobInfo(page);
        } else {
            // 如果不为空,则表示这是列表页解析出详情页的URL地址,放到任务队列中
            for (Selectable selectable : nodes) {
                // 获取URL地址
                String jobInfoUrl = selectable.links().toString();
                // 将获取到的URL地址放到任务队列中
                page.addTargetRequest(jobInfoUrl);
            }

            // 获取下一页的url
            String nextPageUrl = page.getHtml().css("div.p_in li.bk").nodes().get(1).links().toString();
            // 把url放到任务队列中
            page.addTargetRequest(nextPageUrl);
        }
    }

    /**
     * 功能描述:saveJobInfo,解析页面获取招聘详情信息,保存数据 <br>
     * @param page
     * @return void
     *
     * @Author jiangkuobo
     * @Date 2020-05-19 00:21
     * @Version 1.0.0
     */
    private void saveJobInfo(Page page) {
        // 创建招聘详情对象
        JobInfo jobInfo = new JobInfo();
        // 解析页面
        Html html = page.getHtml();

        // 获取数据封装到对象中
        jobInfo.setCompanyName(html.css("div.cn p.cname a", "text").toString());
        String companyAddr = Jsoup.parse(html.css("div.bmsg").nodes().get(1).toString()).text();
        jobInfo.setCompanyAddr(companyAddr.substring(0, companyAddr.length()-3));
        jobInfo.setCompanyInfo(Jsoup.parse(html.css("div.tmsg").toString()).text());
        jobInfo.setJobName(html.css("div.cn h1", "text").toString());
        String[] jobAndTime = html.css("div.cn p.ltype", "text").toString().split("    ");
        jobInfo.setJobAddr(jobAndTime[0]);
        jobInfo.setJobInfo(Jsoup.parse(html.css("div.job_msg").toString()).text());
        jobInfo.setUrl(page.getUrl().toString());
        String text = html.css("div.cn strong", "text").toString();
        Integer[] salary = SalatyUtils.getSalary(text);
        jobInfo.setSalaryMin(salary[0]);
        jobInfo.setSalaryMax(salary[1]);
        String time = null;
        for (int i=0; i< jobAndTime.length; i++) {
             String s = jobAndTime[i];
            if (s.contains("发布")) {
                time = s.substring(0, s.length()-2);
            }
        }
        jobInfo.setTime(time);

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

    @Override
    public Site getSite() {
        return site;
    }

    /**
     * 功能描述:initialDelay任务启动后等待多久执行,fixedDelay任务执行的间隔时间 <br>
     * @param
     * @return void
     *
     * @Author jiangkuobo
     * @Date 2020-05-19 00:21
     * @Version 1.0.0
     */
    @Scheduled(initialDelay = 1000, fixedDelay = 100000)
    public void doProcess() {
        logger.info("============ 任务开始执行 ============");
        Spider.create(new JobProcessor())
                .addUrl(url)
                .setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(100000)))
                .thread(10)
                .addPipeline(this.springDataPipeline)
                .run();
        logger.info("============ 任务执行完成 ============");
    }
}

SpringDataPipeline类,用于数据处理后放入数据库中

@Component
public class SpringDataPipeline implements Pipeline {

    @Autowired
    private JobInfoService jobInfoService;

    @Override
    public void process(ResultItems resultItems, Task task) {
        // 获取封装好的招聘信息对象
        JobInfo jobInfo = resultItems.get("jobInfo");

        // 判断数据是否为空,如果不为空则将数据保存到数据库中
        if (jobInfo != null) {
            this.jobInfoService.save(jobInfo);
        }
    }
}

SalatyUtils类,JobProcessor中调用的方法用于处理网页中的数据

public class SalatyUtils {

    public static Integer[] getSalary(String salaryString) {
        Integer[] salaryArr = new Integer[2];
        String itemSalaty = salaryString.substring(0, salaryString.length()-3);
        String itemMath = salaryString.substring(salaryString.length()-3, salaryString.length());
        String[] salary = itemSalaty.split("-");
        if (itemMath.contains("千")) {
            salaryArr[0] = new BigDecimal(salary[0]).multiply(new BigDecimal(1000)).intValue();
            salaryArr[1] = new BigDecimal(salary[1]).multiply(new BigDecimal(1000)).intValue();
        } else {
            if (itemMath.contains("月")) {
                salaryArr[0] = new BigDecimal(salary[0]).multiply(new BigDecimal(10000)).intValue();
                salaryArr[1] = new BigDecimal(salary[1]).multiply(new BigDecimal(10000)).intValue();
            } else {
                salaryArr[0] = new BigDecimal(salary[0]).multiply(new BigDecimal(10000/12)).intValue();
                salaryArr[1] = new BigDecimal(salary[1]).multiply(new BigDecimal(10000/12)).intValue();
            }
        }
        return salaryArr;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值