0

老狗啃爬虫-小爬虫初长成之PageProcessor

 2 years ago
source link: http://www.veiking.cn/blog/1046-page.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

WebMagic是一个简单灵活的Java爬虫框架。其简单的API,容易上手,模块化的结构,便于轻松扩展;同时也功能完备,且提供多线程和分布式支持。基于WebMagic,我们可以快速开发出一个高效、易维护的爬虫。WebMagic框架主要由Downloader、PageProcessor、Scheduler、Pipeline四大组件组成

WebMagic初探

  WebMagic是一个简单灵活的Java爬虫框架。其简单的API,容易上手,模块化的结构,便于轻松扩展;同时也功能完备,且提供多线程和分布式支持。基于WebMagic,我们可以快速开发出一个高效、易维护的爬虫
  WebMagic框架主要由DownloaderPageProcessorSchedulerPipeline四大组件组成,并由Spider提供组织联系与功能整合,其大概的工作流程如下图所示:

  其中,Downloader负责从互联网上下载页面,以便后续处理;PageProcessor负责解析页面,抽取有用信息,以及发现新的链接等Scheduler负责管理待抓取的URL,以及一些去重的工作Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。
  在实现爬虫的过程中,我们主要的工作是针对不同网站不同页面,来实现我们自己的PageProcessor;然后再通过对Pipeline的定制,来实现我们最终的数据抓取、处理、保存工作。
  Spider是WebMagic整个工作流程的核心中枢,负责将这些工作穿连起来。我们下面就针对PageProcessor进行开发,实现一个简单的测试案例。

一个简单的小爬虫

  我们从『爱古风』网站找了一个页面,内容是介绍先秦爱国大学者屈原的,我们准备用程序抓取它页面上的标题、人物头像地址、人物简介这三个内容:

  首先,我们在包路径cn.veiking下新建个叫processor的新包,然后创建名为SpiderLarvaProcessor的java文件,我们来尝试实现WebMagic的PageProcessor,代码如下:
package cn.veiking.processor;

import org.springframework.stereotype.Component;
import cn.veiking.base.common.logs.SimLogger;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.processor.PageProcessor;

/**
* @author    :Veiking
* @version    :2020年12月1日
* 说明        :蜘蛛幼儿,初写测试
*/
@Component
public class SpiderLarvaProcessor implements PageProcessor {
    SimLogger logger = new SimLogger(this.getClass());
    @Override
    public Site getSite() {
        Site site = Site.me().setRetryTimes(5).setSleepTime(1000).setTimeOut(10000);
        return site;
    }
    // 重写process,获取title、authorImg、content 三数据
    @Override
    public void process(Page page) {
        String title = page.getHtml().xpath("//*[@id=\"content\"]/div/div[1]/div[2]/div[1]/h1/a/text()").get();
        String authorImg = page.getHtml().xpath("//*[@id=\"content\"]/div/div[1]/div[2]/div[1]/a/img/@src").toString();
        String content = page.getHtml().xpath("//*[@id=\"icontentContent\"]/text()").toString();

        logger.info("AigufengCelebrityProcessor getAigufengCelebrity[title={}, authorImg={}, content={},]", title, authorImg, content);
    }
}

  我们需要留意,在这个重写的方法process里,我们用了三个变量,title、authorImg、content来承接我们从页面上抓取到的对应的值,并用日志的形式打印了出来。
  其中,注意这个xpath()方法,Xpath是一种用于xml、html等脚本文件定位元素信息的解释语言,我们在目标页面上抓取我们想要的信息,借助的就是这种技术运用,随后我们单另拿出来详细的说一说。
  这个是主要的功能代码,接下来我们看看怎么将他运行起来。
  一般来说,程序写到上面这个程度,我们需要做简单测试的时候,只需写一个main方法即可,但这里不准备这么做,因为之后一系列代码,很多地方我们都想用spring注解的方式进行,我们还准备将整个程序注册成springboot的微服务节点,以供管理中枢调用;设想最后这些实验代码,适当的做些修改,我们就可以直接用于实际应用。所以我们尽可能的模拟spring容器实际运行的方式,这里我们指定测试方式,都将基于junit这个单元测试框架进行。
  于是,我们就在src/test/java的文件路径下,创建与src/main/java对应的包,然后在包路径cn.veiking.processor下,创建我们的测试入口SpiderLarvaTest,代码如下:

package cn.veiking.processor;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import cn.veiking.StartTest;
import cn.veiking.base.common.logs.SimLogger;
import cn.veiking.processor.SpiderLarvaProcessor;
import us.codecraft.webmagic.Spider;

/**
* @author    :Veiking
* @version    :2020年12月1日
* 说明        :SpiderLarvaProcessor 测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartTest.class)
public class SpiderLarvaTest {
    SimLogger logger = new SimLogger(this.getClass());

    @Autowired
    private SpiderLarvaProcessor spiderLarvaProcessor;

    private static final String url = "http://www.aigufeng.com/celebrity/article-1000056/page.html";

    @Test
    public void testSpider() {
        long startTime, endTime;
        logger.info("SpiderLarvaTest testSpider [start={}] ", "开始爬取数据");
        startTime = System.currentTimeMillis();
        Spider.create(spiderLarvaProcessor).addUrl(url).thread(3).run();
        endTime = System.currentTimeMillis();
        logger.info("SpiderLarvaTest testSpider [end={}] ", "爬取结束,耗时约" + ((endTime - startTime) / 1000) + "秒");
    }
}

  注意这里,如果我们想顺利的启动运行,测试时就必须得创建所需的启动类,并在启动文件类这里做一个扫描动作

package cn.veiking;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

/**
* @author    :Veiking
* @version    :2020年11月30日
* 说明        :测试启动入口
*/
@SpringBootApplication
//开启通用注解扫描

@MapperScan(value = {"cn.veiking.biz.dao"})
@ComponentScan(value = {"cn.veiking.processor"})
public class StartTest {
    public static void main(String[] args) {
        SpringApplication.run(StartTest.class, args);
    }
}

  特别注意:@ComponentScan(value = {“cn.veiking.processor”})
  这行代码即表示,在程序启动的时候,程序会扫描这个包路径,将包内的类注册到spring容器内,这样我们就可以通过@Autowired标签,直接将类实例化使用。
  关于spring框架的注解式开发,这里要额外啰嗦一下,注解式开发摈弃了spring原来高度依赖的xml配置形式,理论上的优劣咱不扯,但在实际开发过程中,操作更为灵活,代码统一性更好。其实早些年写个类实现个功能,就去xml做同步的添加修改,也是挺让人崩溃的,这样说吧,注解式是历史的必然选择,哈哈哈。

  好了,测试入口准备好了,我们测试用例右键Run As(运行)->Junit Test,执行之后…报错:

  Caused by: java.lang.ClassNotFoundException: org.junit.platform.launcher.core.LauncherFactory
  大事不妙,明显缺包,无法支持测试,根据关键信息词:junit.platform.launcher,我们赶紧去搜一个,加上:
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <scope>test</scope>
    </dependency>

  然后更新(maven->update)项目,再尝试运行,OK,Junit框显示条绿色通过,我们再看Console打印的日志信息:

  我们看到,已经打印出来预期的信息了,页面抓取完成,测试OK!
  以上,就是一个简单爬虫的实现过程。

  最后要留意的是,我们获取页面信息的时候,用的这个xpath()方法,这个路径参数,可能会让一些新手比较蒙,这个是xpath语言,是用来在 XML、HTML文档中定位查找信息用的。
  我们在获取这个xpath路径的时候,需要在浏览器里打开调试(按F12键),找到我们需要的数据块处,右键如下:

  这样我们就可以获取这个xpath路径。
  通过上面的代码我们可以看到,PageProcessor的主要工作就是对页面HTML进行数据的解析提取,关于解析,我们就要知道一些开发爬虫关于HTML解析必需的基础知识,接下来我们整理梳理下这些东西。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK