老狗啃爬虫-小爬虫初长成之PageProcessor
摘要:
WebMagic是一个简单灵活的Java爬虫框架。其简单的API,容易上手,模块化的结构,便于轻松扩展;同时也功能完备,且提供多线程和分布式支持。基于WebMagic,我们可以快速开发出一个高效、易维护的爬虫。WebMagic框架主要由Downloader、PageProcessor、Scheduler、Pipeline四大组件组成
WebMagic初探
WebMagic是一个简单灵活的Java爬虫框架。其简单的API,容易上手,模块化的结构,便于轻松扩展;同时也功能完备,且提供多线程和分布式支持。基于WebMagic,我们可以快速开发出一个高效、易维护的爬虫。
WebMagic框架主要由Downloader、PageProcessor、Scheduler、Pipeline四大组件组成,并由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解析必需的基础知识,接下来我们整理梳理下这些东西。