WebMagic爬虫

本文详细介绍了WebMagic爬虫框架的架构、组件和核心功能,包括Downloader、PageProcessor、Scheduler和Pipeline。通过实例展示了如何创建一个简单的爬虫项目,利用Jsoup和Xsoup解析HTML。此外,还提供了配置Site、定制Pipeline的代码示例,帮助读者快速上手WebMagic。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

WebMagic是一款简单灵活的爬虫框架。基于它你可以很容易的编写一个爬虫。

简介

WebMagic项目代码分为核心和扩展两部分。核心部分(webmagic-core)是一个精简的、模块化的爬虫实现,而扩展部分则包括一些便利的、实用性的功能。WebMagic的架构设计参照了Scrapy,目标是尽量的模块化,并体现爬虫的功能特点。
在这里插入图片描述
WebMagic总体架构图如下:
在这里插入图片描述

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。

主要部分

WebMagic主要包括两个包,这两个包经过广泛实用,已经比较成熟:

webmagic-core

webmagic-core是WebMagic核心部分,只包含爬虫基本模块和基本抽取器。WebMagic-core的目标是成为网页爬虫的一个教科书般的实现。

webmagic-extension

webmagic-extension是WebMagic的主要扩展模块,提供一些更方便的编写爬虫的工具。包括注解格式定义爬虫、JSON、分布式等支持。

Maven依赖

WebMagic基于Maven进行构建,推荐使用Maven来安装WebMagic。在你自己的项目(已有项目或者新建一个)中添加以下依赖即可:

        <!--web magic爬虫-->
        <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>
第一个爬虫项目

这里我以领导留言板的案例举例
在这里插入图片描述
网站页面如上,代码如下

Spider

Spider是爬虫启动的入口。在启动爬虫之前,我们需要使用一个PageProcessor创建一个Spider对象,然后使用run()进行启动。同时Spider的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来进行设置。

package com.zcf.spider.crawler;

import com.zcf.common.config.HttpConfig;
import com.zcf.spider.webmagic.DataPipeline;
import com.zcf.spider.webmagic.LiuYanBanProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.model.HttpRequestBody;
import us.codecraft.webmagic.utils.HttpConstant;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description TODO
 * @Author zcf
 * @Date 2020/5/20
 **/
@Component
public class LiuYanBanCrawler {
    private static final Logger LOG = LoggerFactory.getLogger(LiuYanBanCrawler.class);

    @Autowired
    private LiuYanBanProcessor liuYanBanProcessor;
    @Autowired
    private DataPipeline dataPipeline;

    /**
     * @Scheduled(cron = "0 0 0/2 * * ? ")          //  每2个小时执行一次
     * @Scheduled(cron = "0 0/1 * * * ?")           //  每1分钟执行一次
     * @Scheduled(cron = "0/30 * * * * ?")          //  每30秒执行一次
     */
    @Async("taskExecutor")
    @Scheduled(cron = "0 0 0/2 * * ? ")
    public void governmentLiuYanBan(){
        LOG.info("执行开始时间:{}",new Date());

        int fid = 561;
        Map<String,Object> map = new HashMap<>();
        map.put("fid",fid);
        map.put("lastItem",0);
        String referer_url = "http://liuyan.people.com.cn/threads/list?fid="+fid;

        Request request = getRequest(map, referer_url);
        try {
            Thread.sleep(1000*10); // 休眠10秒

            Spider.create(liuYanBanProcessor)
                    .addRequest(request)
                    .thread(10)
                    .addPipeline(dataPipeline)
                    .run();

            LOG.info("执行结束时间:{}",new Date());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    private Request getRequest(Map<String, Object> map, String referer_url){
        Request request = new Request();
        request.setMethod(HttpConstant.Method.POST);
        request.addHeader("Referer",referer_url);
        request.addHeader("Origin","http://liuyan.people.com.cn");
        request.addHeader("User-Agent",HttpConfig.USER_AGENT);
        request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        //构造map用于第一条请求的请求体
        request.setRequestBody(HttpRequestBody.form(map,"utf-8"));
        request.setUrl("http://liuyan.people.com.cn/threads/queryThreadsList");
        return request;
    }
}
Site

Site对站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理等,都可以通过设置Site对象来进行配置。

package com.zcf.spider.webmagic;

import com.alibaba.fastjson.JSONObject;
import com.zcf.common.config.HttpConfig;
import com.zcf.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.model.HttpRequestBody;
import us.codecraft.webmagic.processor.PageProcessor;

import java.util.*;

/**
 * @Description TODO
 * @Author zcf
 * @Date 2020/5/19
 **/
@Component
public class LiuYanBanProcessor implements PageProcessor {
    private static final Logger LOG = LoggerFactory.getLogger(LiuYanBanProcessor.class);

    @Override
    public void process(Page page) {
        List<Map<String,Object>> infoList = new ArrayList<>();
        // 获取返回结果
        JSONObject result  = JSONObject.parseObject(page.getJson().toString());
        Boolean flag = (Boolean) result.get("success");
        if(flag) {
            List<Map<String,Object>> responseData = (List) result.get("responseData");
            for (Map<String, Object> map : responseData) {
                Map<String, Object> info = new HashMap<>();
                Integer threadsCheckTime = (Integer) map.get("threadsCheckTime"); //时间戳
                String subject = (String) map.get("subject"); //标题
                Integer tid = (Integer) map.get("tid"); //文章ID
                String url = "http://liuyan.people.com.cn/threads/content?tid=" + tid;  //文章链接
                info.put("eventDate",threadsCheckTime);
                info.put("subject",subject);
                info.put("id",tid);
                info.put("url",url);
                LOG.info("时间戳的信息:{}",threadsCheckTime);
                LOG.info("标题的信息:{}",subject);
                LOG.info("文章ID的信息:{}",tid);
                LOG.info("文章链接的信息:{}",url);
                LOG.info("--------------------------------------------------------");
                infoList.add(info);
            }
            
            Map<String,Object> map = new HashMap<>();
            map.put("fid",responseData.get(responseData.size()-1).get("fid"));
            map.put("lastItem",responseData.get(responseData.size()-1).get("tid"));
            Integer threadsCheckTime = (Integer) responseData.get(responseData.size() - 1).get("threadsCheckTime");
            Date date1 = DateUtil.timestampToDateBySecond(Long.valueOf(threadsCheckTime));
            Date date2 = DateUtil.formatDateToDate(new Date());
            if(DateUtil.compareToDate(date1, date2)){ 
                Request request = page.getRequest();
                request.setRequestBody(HttpRequestBody.form(map,"utf-8"));
                page.addTargetRequest(request);
                LOG.info("开始下一页");
            }

        }
        page.putField("data",infoList);
    }

    //配置爬虫信息
    private Site site = Site.me()
            .setUserAgent(HttpConfig.USER_AGENT)
            .setCharset("utf8")
            .setTimeOut(10 * 1000)
            .setRetryTimes(3)
            .setRetrySleepTime(3000);

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

WebMagic用于保存结果的组件叫做Pipeline。

package com.zcf.spider.webmagic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @Description TODO
 * @Author zcf
 * @Date 2020/5/19
 **/
@Component
public class DataPipeline implements Pipeline {
    private static final Logger LOG = LoggerFactory.getLogger(DataPipeline.class);

    @Override
    public void process(ResultItems resultItems, Task task) {
        List<Map<String,Object>> data = resultItems.get("data");
        LOG.info("总数据一共有{}条",data.size());
        // 后续数据的存储到数据库
        for (Map<String, Object> datum : data) {
            Set set = datum.keySet();
            LOG.info("--------------------------------------------------------");
            for (Object o : set){
                LOG.info("数据参数:{}的信息:{}",o,datum.get(o));
            }
        }
    }
}

运行一下,查看一下效果
在这里插入图片描述
在这里我想获取的数据都已经获取到了,当然每个页面的解析都不一样。
Jsoup是一个简单的HTML解析器,同时它支持使用CSS选择器的方式查找元素。
Xsoup是基于Jsoup开发的一款XPath解析器。
具体我们也可以参考官网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值