spring boot 整合爬虫框架webmagic,并将数据存储到数据库

spring boot 整合爬虫框架webmagic,并将数据存储到数据库

文末附测试业务代码链接,供学习使用

webmagic是一个开源的Java垂直爬虫框架,目标是简化爬虫的开发流程,让开发者专注于逻辑功能的开发。webmagic主要由Downloader(下载器)、PageProcesser(解析器)、Schedule(调度器)和Pipeline(管道)四部分组成。
webmagic采用完全模块化的设计,功能覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、自定义UA/cookie等功能。
这里关于Webmagic不做过多介绍,想了解更多可去官网查阅:http://webmagic.io/docs/zh/

操作数据源:
https://www.baikemy.com/disease/list/0/0?diseaseContentType=A

项目核心依赖

<!-- 爬虫相关代码 -->
<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>

获取数据示例
获取所有的疾病 及 详情信息,示例信息如下图所示
疾病数据列表
疾病详情信息
项目结构业务
项目架构图
下面将分步骤解析

步骤一:
定义 DiseaseProcessor.java 获取所有的 疾病详情链接

@Component
public class DiseaseProcessor implements PageProcessor {
    private Logger LOGGER = LoggerFactory.getLogger(DiseaseProcessor.class);
    // 抓取网站的相关配置,包括编码、抓取间隔、重试次数、超时时间等
    private static Site site = Site.me().setSleepTime(3000).setTimeOut(10000).setRetryTimes(0);
    private static DiseaseDetailService diseaseDetailService;

    @Override
    // process 是定制爬虫逻辑的核心接口,在这里编写抽取逻辑
    public void process(Page page) {
        Html html = page.getHtml();
        
        // 抽取页面 疾病详情链接
        List<String> links = html.xpath("//*[@id=\"theLeft\"]/div[2]/div/div[2]/ul/li/a").links().all();
        for (String link : links) {
            // 单体链接操作
            diseaseDetailService.executeDiseaseDetailProcessor(link);
        }
    }

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

    // 注入 DiseaseDetailService bean对象,否则单体链接操作会报错
    @Autowired
    public void setDiseaseDetailService(DiseaseDetailService diseaseDetailService) {
        DiseaseProcessor.diseaseDetailService = diseaseDetailService;
    }
}

html.xpath("//*[@id=“theLeft”]/div[2]/div/div[2]/ul/li/a").links().all(); 路径解析
路径解析
关于 xpath 中元素获取的方法有很多种,也可以通过下main的方式直接获取(简便)
便捷操作
上图 可以根据需要做出适当修改。
在 DiseaseDetailServiceImpl 中定义一个方法,通过 Spider 去执行 DiseaseProcessor 处理器

// Spider 启动爬虫,指定规则
public void executeDiseaseWm() {
    Spider.create(new DiseaseProcessor())
            .addUrl(WebMagicConstant.DISEASE_LIST_URL) // 要爬取的url路径
            .thread(10) // 定义线程数
            .run(); // 启动
}

步骤二:
根据步骤一所得的疾病详情去获取我们所需的数据
定义 疾病详情 处理器

@Component
public class DiseaseDetailProcessor implements PageProcessor {
    // 抓取网站的相关配置
    private static Site site = Site.me().setSleepTime(300).setTimeOut(10000).setRetryTimes(0);

    @Override
    public void process(Page page) {
        Html html = page.getHtml();
        // 定义初始化数据容器
        Map<String, Object> map = new ConcurrentHashMap<>();
        // 疾病名称抓取 text()代表获取标签内的内容
        String sickName = html.xpath("/html/body/div[2]/div[2]/div[1]/div[1]/div[1]/text()").toString();
        map.put("sickName",sickName);
        // 获取当前要抓取数据的url
        map.put("detailUrl",page.getUrl().toString());

        int count = 1;
        while (true) {
            String d1 = "//*[@id=\"specialityVersion\"]/div[" + count + "]/h1/text()";
            String d2 = "//*[@id=\"specialityVersion\"]/div[" + count + "]/p/text()";
            String key = html.xpath(d1).toString();
            String value = html.xpath(d2).toString();
            
            if (StringUtils.isEmpty(key) && StringUtils.isEmpty(value)) {
                break;
            }
            map.put(DiseaseCatalogueEnum.getName(key),value);
            count++;
        }
        // 将抽取页面信息,保存下来
        page.putField("diseaseDetail",map);
    }

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

步骤三:
通过步骤二我们已经获得想要的数据,现在就是将数据存储到数据库中
在 DiseaseDetailServiceImpl 中定义一个方法,通过 Spider 去执行 DiseaseDetailProcessor 处理器去获取数据并存储到page中; 详情如下

    public void executeDiseaseDetailProcessor(String url) {
	// Spider 启动爬虫,指定规则
	Spider.create(new DiseaseDetailProcessor()) // 定义疾病详情处理器,抓取疾病详情数据
	        .addUrl(url)  // 抓取路径
	        .thread(10)   // 定义线程数量 
	        .addPipeline(new DiseaseDetailPipeline()) // 定义管道 进行数据存储操作处理
	        .run();      // 项目启动
    }

在 上述代码块中有一个 addPipeline() 方法,在这里我们可以定义操作数据库的相关业务操作,示例代码如下

@Component
public class DiseaseDetailPipeline implements Pipeline {
    private Logger LOGGER = LoggerFactory.getLogger(DiseaseDetailPipeline.class);
    private static DiseaseDetailService diseaseDetailService;

    @Override
    public void process(ResultItems resultItems, Task task) {
        // 获取 DiseaseDetailProcessor 执行器中 page.putField("diseaseDetail",map) 存储的数据
        Map<String, Object> map = resultItems.get("diseaseDetail");
        // 调用远程服务 存储数据
        diseaseDetailService.saveMapData(map);
    }
    // bean 对象注册
    @Autowired
    public void setDiseaseDetailService(DiseaseDetailService diseaseDetailService) {
        DiseaseDetailPipeline.diseaseDetailService = diseaseDetailService;
    }
}

数据库存储业务代码如下

public void saveMapData(Map<String, Object> map) {
    LOGGER.info("爬虫 获取的封装信息为 {}", map);
    DiseaseDetail detail = diseaseDetailMapper.selectByUrl(map.get("detailUrl").toString());
    if (ObjectUtil.isEmpty(detail)) {
        // 数据转换
        DiseaseDetail diseaseDetail = JSONUtil.toBean(JSONUtil.toJsonStr(map), DiseaseDetail.class);
        LOGGER.info("map 转换成 DiseaseDetail 的数据为 {}", diseaseDetail);

        diseaseDetailMapper.insert(diseaseDetail);
    }
}

注意:
note 01:
在存储数据库的过程中,需要注意的是在 @Component 定义的配置文件中,需要获取 @Service 定义的 bean 对象,若通过在对象中直接通过 @Autowired 会出现获取不到 bean 对象(为 null)。
上述代码也作出了相关的处理操作。可以参考上述代码进行测试。
关于 @Component 下 @Autowired 注解为空 可以通过下面方法获取

// 方法 一
@Component
public class Test{
    private static TestService testService;
    // 其他业务代码
    ... ... 
    
    @Autowired
    public void setTestService (TestService testService) {
        Test.testService= testService;
    }
}

// 方法 二 
@Component
public class TimingTaskController{
    public static TimingTaskController timingTaskController;
    @Autowired
    private OrderService orderService;
    
    @PostConstruct
    public void init() {
        timingTaskController = this;
        timingTaskController.orderService = this.orderService;
    }
    //调用的时候
    timingTaskController.orderService.*****************
}

关注公众号回复 “ WebMagic ” 即可获取相关Demo,Demo包含多个页面的爬取数据,提供学习参考
在这里插入图片描述

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值