首页 > 技术文章 > WebMagic简介和使用

Jason-Xiang 2018-11-26 16:44 原文

概览

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

WebMagic项目代码分为核心和扩展两部分。

  • 核心部分(webmagic-core)是一个精简的、模块化的爬虫实现,而扩展部分则包括一些便利的、实用性的功能。WebMagic的架构设计参照了Scrapy,目标是尽量的模块化,并体现爬虫的功能特点。这部分提供非常简单、灵活的API,在基本不改变开发模式的情况下,编写一个爬虫。
  • 扩展部分(webmagic-extension)提供一些便捷的功能,例如注解模式编写爬虫等。同时内置了一些常用的组件,便于爬虫开发。

 

总体架构

WebMagic的结构分为DownloaderPageProcessorSchedulerPipeline四大组件,并由Spider将它们彼此组织起来。

WebMagic总体架构图如下:

image

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

用于数据流转的对象

  1. Request:Request是对URL地址的一层封装,一个Request对应一个URL地址。它是PageProcessor与Downloader交互的载体,也是PageProcessor控制Downloader唯一方式。除了URL本身外,它还包含一个Key-Value结构的字段extra。你可以在extra中保存一些特殊的属性,然后在其他地方读取,以完成不同的功能。例如附加上一个页面的一些信息等。
  2. Page:Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。
  3. ResultItems:ResultItems相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。它的API与Map很类似,值得注意的是它有一个字段skip,若设置为true,则不应被Pipeline处理。

 

实例

Maven坐标

<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>

爬取安居客房价信息实例

需求是获取所有小区的房价,先看一下页面截图:

代码:

public class AnjukeProcessor implements PageProcessor {
    //抓取网站的相关配置,包括编码、抓取间隔、重试次数等
    private Site site = Site.me().setRetryTimes(3).setSleepTime(100);
    private static int count = 0;
    private static List<String> urlList = new ArrayList<>();

    @Override
    public void process(Page page) {
        //从页面发现后续的url地址来抓取
        page.addTargetRequests(
                page.getHtml().xpath("//div[@class='page-content']/div[@class='multi-page']/a/@href").all());

        //判断链接是否符合"https://qd.anjuke.com/community/p任意个数字"格式
        if (page.getUrl().regex("https://qd.anjuke.com/community/p[0-9]+").match()) {
            //定义如何抽取页面信息,并保存下来
            List<Selectable> selectableList = page.getHtml().xpath("//div[@class='list-content']/div[@class='li-itemmod']").nodes();
            List<HousePrice> list = new ArrayList<>();
            for(Selectable selectable : selectableList){
                String name = selectable.xpath("//div[@class='li-info']/h3/a/text()").toString();
                String price = selectable.xpath("//div[@class='li-side']/p[1]/strong/text()").toString();
                HousePrice housePrice = new HousePrice();
                housePrice.setName(name.trim());
                housePrice.setPriceStr(price.trim());
                list.add(housePrice);
            }

            page.putField("housePriceList",list);
            urlList.add(page.getUrl().toString());
            count++;
        }
    }

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

    public static void main(String[] args) {
        Spider.create(new AnjukeProcessor())
                .addUrl("https://qd.anjuke.com/community/") //从https://qd.anjuke.com/community/开始爬取
                .addPipeline(new HousePricePipeline())  //使用自定义的Pipeline
                .thread(5)
                .run();
        System.out.println("----------抓取了"+count+"条记录");
    }
}
public class HousePricePipeline implements Pipeline {
    @Override
    public void process(ResultItems resultItems, Task task) {
        System.out.println("----------get page: " + resultItems.getRequest().getUrl());
        List<HousePrice> list = resultItems.get("housePriceList");
        System.out.println("----------list size:" + list.size());
    }
}

 

注意事项

在0.7.3版本中,爬取只支持TLS1.2https站点的时候会报错:

javax.net.ssl.SSLException: Received fatal alert: protocol_version

解决办法:https://github.com/code4craft/webmagic/issues/701

 

相关文档

官方文档

XPath 语法

 

推荐阅读