访问我的博客

前言

年前闲着无聊,研究了一阵子爬虫技术,接触到爬虫框架 WebMagic,感觉很好用。
在之后的工作中,接手了新站与第三方接口对接的工作,主要的工作是去抓取对方接口的内容;初始的时候,之前负责该工作的同事,是手动使用多线程去抓取,在应用的过程当中暴露了不少问题。比如对于接口内容超级多的时候,虽然使用了多线程,但是抓取的效率很低,而且也没有实现增量抓取,每次都需要去全量抓取,跑一次基本需要好几天-.-;小说是连载的情况下,这种问题是亟需解决的。

趁着熟悉了新兵器 WebMagic, 果断在项目中进行引入,解决以上问题。功能上线后,替换了原有的多线程抓取,目前已经十分稳定, 基本上配置好任务之后,就无需再人工干预了。

以下,正文是基于我学习 WebMagic 时练手项目,功能和在公司开发的差不多,只不过我本地开发的是去抓取盗版网站的内容。

项目预览

  1. 菜单管理
  2. 爬虫任务管理

  3. 实现了爬虫的状态监控,以及可视化启停

初入手兵器-基本使用

  1. 爬虫套路分析
    先看官方文档的总体架构图

    大部分模块WebMagic已经提供了默认实现。
    一般来说,对于编写一个爬虫,PageProcessor是需要编写的部分,而Spider则是创建和控制爬虫的入口。

    得益于 WebMagic 框架的良好封装,对于框架的使用者来说,所需要编写的代码几乎只有爬虫的逻辑代码,而对于怎么爬,维护任务队列的事情,WebMagic 都可以替我们做好。开始我们的爬虫之旅吧!

  2. 引入依赖
    本文中所使用到的项目是基于 Maven 的 SSM 项目,在 pom.xml 中引入 WebMagic 的依赖。
    xml <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. 基本类图
    先将对应的处理类进行抽象出来,方便统一处理。

  4. 每个爬虫都有其对应的配置信息

    Site 是 抓取网站的相关配置,包括编码、抓取间隔、重试次数

  5. 对应的实现类重写 process 方法,在方法中实现对应的爬虫逻辑处理

  6. 启动爬虫
  7. 爬虫的使用就简单带过,具体可以将本文与官方文档结合使用,官方文档的示例只是基于 main 方法。

爬虫监控

  1. 扩展源码
    为了实现项目预览的效果,实现爬虫的状态监控,需要对爬虫进行扩展。因为官网提供的方式功能不足以达到在页面展示的效果。

    添加监控非常简单,获取一个 SpiderMonitor 的单例 SpiderMonitor.instance(),并将你想要监控的 Spider 注册进去即可。你可以注册多个 Spider 到 SpiderMonitor 中。

    查看 SpiderMonitor 源代码后,如果调用的是 获取一个 SpiderMonitor 的单例 SpiderMonitor 的 注册方法,发现 WebMagic 将每只爬虫的状态对象 SpiderStatusMXBean 全部添加到一个 List 集合当中去,这样就难以区分具体是哪一只爬虫的状态,所以我们需要对 SpiderMonitor 进行扩展。

    将 SpiderMonitor 中的
    private List<SpiderStatusMXBean> spiderStatuses = new ArrayList<SpiderStatusMXBean>();
    修改为 Map 集合,key 选择 Spider 的 UUID 作为唯一区分爬虫的标记。

    @Experimental
    public class MySpiderMonitor {private static MySpiderMonitor INSTANCE = new MySpiderMonitor();private AtomicBoolean started = new AtomicBoolean(false);private Logger logger = LoggerFactory.getLogger(getClass());private MBeanServer mbeanServer;private String jmxServerName;private Map<String,MySpiderStatus> spiderStatuses = new HashMap<String,MySpiderStatus>();protected MySpiderMonitor() {jmxServerName = "WebMagic";mbeanServer = ManagementFactory.getPlatformMBeanServer();}public Map<String,MySpiderStatus> getSpiderStatuses(){return spiderStatuses;}/*** Register spider for monitor.** @param spiders spiders* @return this*/public synchronized MySpiderMonitor register(Spider... spiders) throws JMException {for (Spider spider : spiders) {MyMonitorSpiderListener monitorSpiderListener = new MyMonitorSpiderListener();if (spider.getSpiderListeners() == null) {List<SpiderListener> spiderListeners = new ArrayList<SpiderListener>();spiderListeners.add(monitorSpiderListener);spider.setSpiderListeners(spiderListeners);} else {spider.getSpiderListeners().add(monitorSpiderListener);}MySpiderStatus spiderStatusMBean = getSpiderStatusMBean(spider, monitorSpiderListener);registerMBean(spiderStatusMBean);spiderStatuses.put(spider.getUUID(),spiderStatusMBean);}return this;}protected MySpiderStatus getSpiderStatusMBean(Spider spider, MyMonitorSpiderListener monitorSpiderListener) {return new MySpiderStatus(spider, monitorSpiderListener);}public static MySpiderMonitor instance() {return INSTANCE;}public class MyMonitorSpiderListener implements SpiderListener {private final AtomicInteger successCount = new AtomicInteger(0);private final AtomicInteger errorCount = new AtomicInteger(0);private List<String> errorUrls = Collections.synchronizedList(new ArrayList<String>());@Overridepublic void onSuccess(Request request) {successCount.incrementAndGet();}@Overridepublic void onError(Request request) {errorUrls.add(request.getUrl());errorCount.incrementAndGet();}public AtomicInteger getSuccessCount() {return successCount;}public AtomicInteger getErrorCount() {return errorCount;}public List<String> getErrorUrls() {return errorUrls;}}protected void registerMBean(SpiderStatusMXBean spiderStatus) throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {ObjectName objName = new ObjectName(jmxServerName + ":name=" + spiderStatus.getName());if(mbeanServer.isRegistered(objName)==false){mbeanServer.registerMBean(spiderStatus, objName);}}}

    需要注意的是,SpiderMonitor 中使用的 SpiderStatus 也需要进行一同扩展。

    public class MySpiderStatus implements SpiderStatusMXBean {protected final Spider spider;protected Logger logger = LoggerFactory.getLogger(getClass());protected final MySpiderMonitor.MyMonitorSpiderListener monitorSpiderListener;public MySpiderStatus(Spider spider, MySpiderMonitor.MyMonitorSpiderListener monitorSpiderListener) {this.spider = spider;this.monitorSpiderListener = monitorSpiderListener;}public Spider getSpider(){return this.spider;}public String getName() {return spider.getUUID();}public int getLeftPageCount() {if (spider.getScheduler() instanceof MonitorableScheduler) {return ((MonitorableScheduler) spider.getScheduler()).getLeftRequestsCount(spider);}logger.warn("Get leftPageCount fail, try to use a Scheduler implement MonitorableScheduler for monitor count!");return -1;}public int getTotalPageCount() {if (spider.getScheduler() instanceof MonitorableScheduler) {return ((MonitorableScheduler) spider.getScheduler()).getTotalRequestsCount(spider);}logger.warn("Get totalPageCount fail, try to use a Scheduler implement MonitorableScheduler for monitor count!");return -1;}@Overridepublic int getSuccessPageCount() {return monitorSpiderListener.getSuccessCount().get();}@Overridepublic int getErrorPageCount() {return monitorSpiderListener.getErrorCount().get();}public List<String> getErrorPages() {return monitorSpiderListener.getErrorUrls();}@Overridepublic String getStatus() {return spider.getStatus().name();}@Overridepublic int getThread() {return spider.getThreadAlive();}public void start() {spider.start();}public void stop() {spider.stop();}@Overridepublic Date getStartTime() {return spider.getStartTime();}@Overridepublic int getPagePerSecond() {int runSeconds = (int) (System.currentTimeMillis() - getStartTime().getTime()) / 1000;return getSuccessPageCount() / runSeconds;}}
  2. 重写爬虫启动处代码
    ```java
    @Service
    public class WebMagicService {

     @Resourceprivate ApplicationContext context;@Resourceprivate TaskService taskService;public void run(TaskDTO taskDTO, boolean runAsync) throws Exception {MySpiderMonitor spiderMonitor = MySpiderMonitor.instance();String ruleJson = taskDTO.getTaskRuleJson();WebMagicConfig config = JSONObject.parseObject(ruleJson, WebMagicConfig.class);SpiderConfig spiderConfig = config.getSpider();AbstractPageProcess pageProcess = context.getBean(spiderConfig.getProcesser(), AbstractPageProcess.class);pageProcess.init(config);pageProcess.setUuid(taskDTO.getSpiderUUID());Spider spider = Spider.create(pageProcess).thread(spiderConfig.getThread());spider.setUUID(taskDTO.getSpiderUUID());List<String> pipelines = spiderConfig.getPipeline();for (String pipeline : pipelines) {Pipeline bean = context.getBean(pipeline, Pipeline.class);if (bean != null) {spider.addPipeline(bean);}}// 设置Downloader// 设置Scheduler// 注册爬虫spiderMonitor.register(spider);spider.addUrl(spiderConfig.getStartUrl());if (runAsync) {spider.runAsync();} else {spider.run();}}/*** 爬虫状态监控* @return*/public List<TaskDTO> runTaskList() {MySpiderMonitor spiderMonitor = MySpiderMonitor.instance();Map<String, MySpiderStatus> spiderStatuses = spiderMonitor.getSpiderStatuses();List<TaskDTO> taskDTOList = taskService.findAll();for (TaskDTO taskDTO : taskDTOList) {MySpiderStatus spiderStatus = spiderStatuses.get(taskDTO.getSpiderUUID());if (spiderStatus == null) {taskDTO.setRunState(Spider.Status.Stopped.name());} else {taskDTO.setRunState(spiderStatus.getStatus());}}return taskDTOList;}public TaskDTO stop(TaskDTO taskDTO) {MySpiderMonitor spiderMonitor = MySpiderMonitor.instance();Map<String, MySpiderStatus> spiderStatuses = spiderMonitor.getSpiderStatuses();MySpiderStatus spiderStatus = spiderStatuses.get(taskDTO.getSpiderUUID());if (spiderStatus != null) {spiderStatus.stop();spiderStatus.getSpider().close();}return taskDTO;}

    }
    ```
    创建爬虫时,将爬虫注册到 MySpiderMonitor 中,之后通过 getSpiderStatuses 方法即可获取所有爬虫的状态了。

资源下载

  • 点我下载源码

转载于:https://www.cnblogs.com/vcmq/p/9484404.html

WebMagic之爬虫监控相关推荐

  1. python拿什么做可视化界面好-用python打造可视化爬虫监控系统,酷炫的图形化界面...

    原标题:用python打造可视化爬虫监控系统,酷炫的图形化界面 本文并不是讲解爬虫的相关技术实现的,而是从实用性的角度,将抓取并存入 MongoDB 的数据 用 InfluxDB 进行处理,而后又通过 ...

  2. WebMagic 实现爬虫入门教程

    本示例实现某电影网站最新片源名称列表及详情页下载地址的抓取. webmagic是一个开源的Java垂直爬虫框架,目标是简化爬虫的开发流程,让开发者专注于逻辑功能的开发. WebMagic 特点: 完全 ...

  3. python中turtle画酷炫图案-用python打造可视化爬虫监控系统,酷炫的图形化界面

    本文并不是讲解爬虫的相关技术实现的,而是从实用性的角度,将抓取并存入 MongoDB 的数据 用 InfluxDB 进行处理,而后又通过 Grafana 将爬虫抓取数据情况通过酷炫的图形化界面展示出来 ...

  4. python做炫酷的界面_用python打造可视化爬虫监控系统,酷炫的图形化界面

    安装 InfluxDB 修改配置文件/usr/local/etc/influxdb.conf,如果原文件中没有对应配置项,需自己添加. 3. 安装配置 Grafana 安装 Grafana 并修改 G ...

  5. Python爬虫监控(邮件和钉钉)

    文章目录 一.监控的意义 二.Python邮件监控(SMTP) 1. SMTP简介 2. 邮件监控五个步骤 3. 邮件监控发送(代码) 二.钉钉监控 一.监控的意义 爬虫脚本是基于网站的结构去写的,但 ...

  6. WebMagic Java 爬虫的简单应用

    前段时间做旅游本体的知识库,我和老师反应说景点之间关系太少了,导致整个图很稀疏.. "你去wiki上抓一批数据吧",就这样被自己坑了. 由于一直在用java做项目,ZWQ师兄推荐的 ...

  7. python爬虫监控豆瓣发帖关键字,并发送邮件提醒

    项目场景: 豆瓣的某小组,经常会有非常优惠的购物活动,但是僧多肉少,晚一些就没机会了.所以我需要时刻监控该界面,一旦出现关键词(开车),就自动向我邮箱发送信件. 前期准备: 我自认为,程序员做什么事情 ...

  8. 探秘WebMagic:爬虫神器

    一.介绍 WebMagic是一款基于Java的开源网络爬虫框架,能够快速.灵活.高效地实现网络数据的爬取和抽取.WebMagic支持多线程.分布式.自动重试等特性,而且使用起来也非常方便. 二.优点 ...

  9. 爬虫监控1000家企业新闻动态

    开源一个项目 Github地址 简介: 此项目可监控近千家中国企业的官方网站的新闻动态,如有更新,系统能在 5 分钟之内通过邮件发送更新的标题和链接. 更新的信息流也可通过浏览器查看.监控的公司和站点 ...

最新文章

  1. synchronized关键字实现原理
  2. 【LeetCode】3月29日打卡-Day14-BFS
  3. webpack使用文档
  4. LeetCode 93. Restore IP Addresses
  5. RTOS osDelay和taskDelay关系
  6. Ajax学习笔记-客户端模板引擎-9
  7. android ppsspp 存档位置,小鸡模拟器游戏存档在哪个文件夹
  8. PDF怎么转CAD?分享两种转换方法
  9. Web前端大作业—— 饮食餐饮网站 咖啡网站pc端带轮播(5个页面)HTML+CSS+JavaScript 学生美食网页设计作品 学生餐饮文化网页模板
  10. hadoop上运行java版本_Hadoop--3.JDK1.7安装配置
  11. 新玺配资:热点轮动明显 均衡配置策略
  12. OpenGL超级宝典的例子Triangle
  13. 谷歌相机android4,谷歌相机10.0版本 7.4.200.316937482 安卓版
  14. 加州伯克利计算机科学录取,2020加州大学伯克利分校统计学录取案例。
  15. 【对讲机的那点事】手把手教你安装公网4G对讲机读写频的驱动及写码?
  16. 国庆不加班攻略丨7款效率神器速领(内附福利)
  17. mysql的收银软件_java swing mysql实现的超市收银进销存系统项目源码附带视频指导运行教程...
  18. Jprofile连接远程机器
  19. 网络聊天室_TCP客户端
  20. 从Unity导出FBX到网页,实现一个模型多个动作动画

热门文章

  1. 69道Java Spring 面试笔试题
  2. InnoDB 存储引擎体系架构
  3. java-Transient关键字、Volatile关键字介绍和序列化、反序列化机制、单例类序列化
  4. SQL数据库基础知识总结(三)---对象
  5. Redis发布与订阅(pub/sub)
  6. Java IO最详解
  7. OpenCV之imgproc 模块. 图像处理(4)直方图均衡化 直方图计算 直方图对比 反向投影 模板匹配
  8. 斯坦福大学深度学习与自然语言处理第三讲:高级的词向量表示
  9. 寻找最优参数解:最速下降法,牛顿下降法,阻尼牛顿法,拟牛顿法
  10. Day 15:Meteor —— 从零开始创建一个 Web 应用