教你如何用Java简单爬取WebMagic

一、Java爬虫——WebMagic

1.1 WebMagic总体架构图

1.2 WebMagic核心组件

1.2.1 Downloader

该组件负责从互联网上下载页面。WebMagic默认使用Apache HttpClient作为下载工具。

1.2.2 PageProcessor

该组件负责解析页面,根据我们的业务进行抽取信息。WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析Xpath的工具Xsoup。

1.2.3 Scheduler

该组件负责管理待抓取的URL,以及去重的工作。WebMagic默认使用JDK内存队列管理URL,通过集合进行去重。

支持使用Redis进行分布式管理。

1.2.4 Pipeline

该组件负责抽取结果的处理,包括计算、持久化到文件、数据库等等。

1.2.5 数据流转对象

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,底层使用了LinkedHashMap进行存储,它保存PageProcessor处理的结果,供Pipeline使用。它的API与Map很类似,值得注意的是它有一个字段skip,若设置为true,则不被Pipeline处理,跳过。

1.2.6 Spider——WebMagic核心引擎

Spider是WebMagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性是可以自由设置的,通过设置这个属性可以实现不同的功能。Spider也是WebMagic操作的入口,它封装了爬虫的创建、启动、停止、多线程等功能。

1.3 练习Demo

需求是爬取一篇厦门限行文章,文章来源:http://xm.bendibao.com/traffic/2018116/54311.shtm,具体需求如下:

1.删除文章中超链接

2.文章中的图片下载至本地

3.删除文章末尾:温馨提示...

1.3.1 定制Downloader

为预防页面失效,定制一个Downloader,当链接地址不存在时,打印日志。

public class MyHttpClientDownloader extends HttpClientDownloader {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 提示页面获取状态码
     */
    @Override
    protected Page handleResponse(Request request, String charset, HttpResponse httpResponse, Task task) throws IOException {
        Page page = super.handleResponse(request, charset, httpResponse, task);
        if(httpResponse.getStatusLine().getStatusCode()!= ConstantsField.PAGE_STATUS_200){
            page.setDownloadSuccess(false);
            logger.warn("页面获取状态码错误,正在重试!");
        }
        return page;
    }
}

1.3.2 定制PageProcessor

该页面处理器实现了对页面的抽取,符合上面的需求。

将处理完成的数据添加进入:Page对象,并设置键分别为:imgListcontent

public class XmPageProcessor implements PageProcessor {

    /**
     * 抓取网站的相关配置,包括编码、抓取间隔、重试次数等
     */
    private Site site = Site.me().setCycleRetryTimes(3).setSleepTime(1000);

    /**
     * 核心:编写抽取逻辑
     */
    @Override
    public void process(Page page) {
        // 抽取页面文本数据
        Selectable selectable = page.getHtml().css(ConstantsField.PAGE_CSS_CONTENT);

        //处理图片
        List<String> pImgList = selectable.xpath(ConstantsField.XPATH_IMG).all();
        List<String> imgUrl = new ArrayList<>();
        if(pImgList.size()>0){
            Pattern compile = Pattern.compile(ConstantsField.REX_IMG_SRC);
            for (String img : pImgList) {
                Matcher matcher = compile.matcher(img);
                while (matcher.find()){
                    imgUrl.add(matcher.group(1));
                }
            }
        }
        if(imgUrl.size()>0){
            page.putField("imgList",imgUrl);
        }else {
            page.putField("imgList",null);
        }

        //对内容转换为StringBuilder
        String content = selectable.toString();
        StringBuilder stringBuilder = new StringBuilder(content);

        //处理超链接
        StringBuilder newString = dealLink(stringBuilder);

        //处理末尾
        int startIndex = newString.indexOf(ConstantsField.END_CONTENT);
        if(startIndex>0) {
            newString.delete(startIndex, stringBuilder.length());
            newString.append("</div>");
        }

        page.putField("content",newString.toString());
    }

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

    /**
     * 处理超链接
     */
    private static StringBuilder dealLink(StringBuilder stringBuilder){
        StringBuilder newString = new StringBuilder(stringBuilder);
        int aIndex = newString.indexOf("<a href");
        while (aIndex != -1){
            int pStart = newString.lastIndexOf("<p>", aIndex);
            int pEnd = (newString.indexOf("</p>", aIndex) + 4);
            newString.delete(pStart,pEnd);
            aIndex = newString.indexOf("<a href");
        }
        return newString;
    }
}

1.3.3 定制Pipeline

Pipeline是处理结果的地方,这里我们对结果进行存储文件的处理。网站文本存储为:stm格式,图片文本存储为其网站源文件的格式。

public class MyFilePipeline extends FilePersistentBase implements Pipeline {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private StringBuilder filepath;

    private MyFilePipeline() {
        this.setPath(ConstantsField.DEFAULT_SAVE_LOCATION);
    }

    public MyFilePipeline(String path) {
        if(path!=null){
            this.setPath(path);
        }else {
            new MyFilePipeline();
        }
        filepath = new StringBuilder().append(this.path).
                append(PATH_SEPERATOR).append(ConstantsField.FILE_NAME)
                .append(ConstantsField.FILE_POSTFIX);

    }

    @Override
    public void process(ResultItems resultItems, Task task) {
        //文件内容覆盖
        try(PrintWriter printWriter = new PrintWriter(new FileWriter(getFile(filepath.toString()),false))) {
            printWriter.write(resultItems.get("content").toString());
            logger.info("文件生成成功,存储地址为:"+filepath);

            //下载图片
            List<String> imgList = resultItems.get("imgList");
            if(imgList!=null&&imgList.size()>0){
                boolean dowload = DownloadImgUtils.download(imgList, this.getPath());
                if(dowload){
                    logger.info("图片下载成功,存储地址为:" + this.getPath());
                }
            }

        } catch (IOException e) {
            logger.error("输出文件出错:" + e.getCause().toString());
        }
    }
}

这里实现了网页存储为stm格式与图片存储,图片存储使用了如下工具类DownloadImgUtils

public class DownloadImgUtils {

    /**
     * 下载图片
     * @param imgList 图片列表
     * @param savePath 保存地址
     * @return 成功返回true
     */
    public static boolean download(List<String> imgList, String savePath) throws IOException {
        URL url;
        DataInputStream dataInputStream = null;
        FileOutputStream fileOutputStream = null;
        File file;
        try {
            for (String imgUrl : imgList) {
                //截取文件名
                Pattern pat=Pattern.compile(ConstantsField.REX_IMG_SUFFIX);
                Matcher mc=pat.matcher(imgUrl);
                while(mc.find()) {
                    String fileName= mc.group();
                    file = new File(savePath + fileName);
                    file.createNewFile();
                    fileOutputStream = new FileOutputStream(savePath + fileName);
                }

                url = new URL(imgUrl);
                dataInputStream = new DataInputStream(url.openStream());

                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int length;
                while ((length = dataInputStream.read(buffer))>0){
                    outputStream.write(buffer,0,length);
                }
                fileOutputStream.write(outputStream.toByteArray());
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            dataInputStream.close();
            fileOutputStream.close();
        }

        return false;
    }
}

1.3.4 启动类

public class WebMagicApplication {

    private String url;
    private String saveUrl;

    /**
     * 无参构造使用默认值
     */
    public WebMagicApplication() {
        this.url = ConstantsField.XM_BDB_URL;
        this.saveUrl = ConstantsField.DEFAULT_SAVE_LOCATION;
    }

    public WebMagicApplication(String url, String saveUrl) {
        this.url = url;
        this.saveUrl = saveUrl;
    }

    public void start(){
        Spider.create(new XmPageProcessor()).addUrl(this.url).addPipeline(new MyFilePipeline(this.saveUrl)).setDownloader(new MyHttpClientDownloader()).run();
    }

    public static void main(String[] args) {
        WebMagicApplication webMagicApplication = new WebMagicApplication("http://xm.bendibao.com/traffic/2018116/5431122.shtm","C:\\");
        webMagicApplication.start();
    }
}

这里启动类可以使用带参构造或无参构造,无参构造默认使用URL与存储地址为ConstantsField类中的XM_BDB_URL属性和DEFAULT_SAVE_LOCATION

练习中的ConstantsField具体如下:

public final class ConstantsField {
    /**
     * 爬取的CSS的文本
     */
    public static final String PAGE_CSS_CONTENT = "div.content";

    /**
     * 结束文本起始位置
     */
    public static final String END_CONTENT = "<div id=\"adInArticle\"></div>";

    /**
     * 厦门本地宝URL
     */
    public static final String XM_BDB_URL = "http://xm.bendibao.com/traffic/2018116/54311.shtm";

    /**
     * 默认文件保存地址
     */
    public static final String DEFAULT_SAVE_LOCATION = "C:\\";

    /**
     * 文件名
     */
    public static final String FILE_NAME = "2021厦门限行最新消息(持续更新)";

    /**
     * 文件后缀
     */
    public static final String FILE_POSTFIX = ".stm";

    /**
     * 页面访问状态码
     */
    public static final int PAGE_STATUS_200 = 200;

    /**
     * 正则匹配src
     */
    public static final String REX_IMG_SRC = "src\\s*=\\s*\"?(.*?)(\"|>|\\s+)";

    /**
     * 正则匹配文件后缀
     */
    public static final String REX_IMG_SUFFIX = "[\\w]+[\\.](jpeg|jpg|png)";

    /**
     * 处理图片XPATH
     */
    public static final String XPATH_IMG = "//*[@id=\"bo\"]/*/img";
}

1.3.5 源码地址

练习Demo源码地址: https://gitee.com/Xiaoxinnolabi/web-magic/settings
WebMagic中文文档: http://webmagic.io/docs/zh/
WebMagic源码地址: https://github.com/code4craft/webmagic/

到此这篇关于教你如何用Java简单爬取WebMagic的文章就介绍到这了,更多相关Java爬取WebMagic内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Java两种方式简单实现:爬取网页并且保存

    对于网络,我一直处于好奇的态度.以前一直想着写个爬虫,但是一拖再拖,懒得实现,感觉这是一个很麻烦的事情,出现个小错误,就要调试很多时间,太浪费时间. 后来一想,既然早早给自己下了保证,就先实现它吧,从简单开始,慢慢增加功能,有时间就实现一个,并且随时优化代码. 下面是我简单实现爬取指定网页,并且保存的简单实现,其实有几种方式可以实现,这里慢慢添加该功能的几种实现方式. UrlConnection爬取实现 package html; import java.io.BufferedReader; i

  • java代理实现爬取代理IP的示例

    仅仅使用了一个java文件,运行main方法即可,需要依赖的jar包是com.alibaba.fastjson(版本1.2.28)和Jsoup(版本1.10.2) 如果用了pom,那么就是以下两个: <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.28</version> </depe

  • java爬取豆瓣电影示例解析

    为什么我们要爬取数据 在大数据时代,我们要获取更多数据,就要进行数据的挖掘.分析.筛选,比如当我们做一个项目的时候,需要大量真实的数据的时候,就需要去某些网站进行爬取,有些网站的数据爬取后保存到数据库还不能够直接使用,需要进行清洗.过滤后才能使用,我们知道有些数据是非常真贵的. 分析豆瓣电影网站 我们使用Chrome浏览器去访问豆瓣的网站如 https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=re

  • java+selenium爬取图片签名的方法

    本文实例为大家分享了java+selenium爬取图片签名的具体实现方法,供大家参考,具体内容如下 学习记录: 1.注意 对应的版本非常重要,使用selenium得下载与游览器版本相对应的插件,有火狐和谷歌我用的谷歌,贴下谷歌driver的插件 查看谷歌版本: 2.插件存放路径 3.获取签名图片存放路径 4.Controller代码如下 @ResponseBody @RequestMapping(value = "signatureGenerationv") public String

  • Java实现爬取往期所有双色球开奖结果功能示例

    本文实例讲述了Java实现爬取往期所有双色球开奖结果功能.分享给大家供大家参考,具体如下: 梦想还是要有的,万一实现了呢?我相信经常买双色球的朋友和我都会有一个疑问,就是往期双色球的开奖结果是什么?我钟意的这一注双色球在往期是否开过一等奖,如果开过的话,基本上可以放弃这一注了,因为历史上应该没有出现过两期双色球开奖完全一致的吧?那么往期的开奖结果是什么呢?我自己用Java写了一个简易的类,爬取所有双色球开奖结果,本来想开发安卓版本的,由于UI等需要时间准备,有缘再开发吧. import java

  • Java基于WebMagic爬取某豆瓣电影评论的实现

    目的 搭建爬虫平台,爬取某豆瓣电影的评论信息. 准备 webmagic是一个开源的Java垂直爬虫框架,目标是简化爬虫的开发流程,让开发者专注于逻辑功能的开发.webmagic的核心非常简单,但是覆盖爬虫的整个流程,也是很好的学习爬虫开发的材料. 下载WebMagic源码,或Maven导入,或Jar包方式导入.  码云地址:https://gitee.com/flashsword20/webmagic 试运行 搭建好后打开项目, 在 us.codecraft.webmagic.processor

  • 教你如何用Java简单爬取WebMagic

    一.Java爬虫--WebMagic 1.1 WebMagic总体架构图 1.2 WebMagic核心组件 1.2.1 Downloader 该组件负责从互联网上下载页面.WebMagic默认使用Apache HttpClient作为下载工具. 1.2.2 PageProcessor 该组件负责解析页面,根据我们的业务进行抽取信息.WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析Xpath的工具Xsoup. 1.2.3 Scheduler 该组件负责管理待抓取的URL,以及

  • 教你如何使用Python快速爬取需要的数据

    一.基础第三方库使用 1.基本使用方法 """例""" from urllib import request response = request.urlopen(r'http://bbs.pinggu.org/') #返回状态 200证明访问成功 print("返回状态码: "+str(response.status)) #读取页面信息转换文本并进行解码,如果本身是UTF-8就不要,具体看页面格式 #搜索"char

  • Java简单从文件读取和输出的实例

    用Scanner输入,用PrintStream输出 功能:从in.txt读入,输出到out.txt 代码: 和下面的一样 package ioTest; import java.io.*; import java.util.Scanner; public class TestMain { public static void main(String[] args) { try { Scanner sc=new Scanner(new File("in.txt")); PrintStre

  • Java实现爬取百度图片的方法分析

    本文实例讲述了Java实现爬取百度图片的方法.分享给大家供大家参考,具体如下: 在以往用java来处理解析HTML文档或者片段时,我们通常会采用htmlparser(http://htmlparser.sourceforge.net/)这个开源类库.现在我们有了JSOUP,以后的处理HTML的内容只需要使用JSOUP就已经足够了,JSOUP有更快的更新,更方便的API等. jsoup 是一款 Java 的HTML 解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,

  • 详解python定时简单爬取网页新闻存入数据库并发送邮件

    本人小白一枚,简单记录下学校作业项目,代码十分简单,主要是对各个库的理解,希望能给别的初学者一点启发. 一.项目要求 1.程序可以从北京工业大学首页上爬取新闻内容:http://www.bjut.edu.cn 2.程序可以将爬取下来的数据写入本地MySQL数据库中. 3.程序可以将爬取下来的数据发送到邮箱. 4.程序可以定时执行. 二.项目分析 1.爬虫部分利用requests库爬取html文本,再利用bs4中的BeaultifulSoup库来解析html文本,提取需要的内容. 2.使用pymy

  • 教你如何用Java替换Word中带有${}的内容

    一.概述 1.因为有些需求,需要把word文档里面的特定数据,设置成可变的:所以需要某种方式,把可变量用标签(如${变量名})替换,通过后端赋值此变量名,重新生成的Word就能根据后端设置的内容变化. 2.替换方法:准备一份word模板文档,如:word_mode.doc(或 word_mode.docx) 文件,把可变内容,用标签${变量名}替换(如图1姓名:${name}) 3.转成可读模板:全部设置完变量标签后,对此word文档进行另存为xml格式的文档(图2),保存后的文件名:word_

  • 教你如何用Java根据日期生成流水号

    前言 生成流水号,在企业中可以说是比较常见的需求,尤其是订单类业务. 一般来说,需要保证流水号的唯一性. 如果没有长度和字符的限制,那么直接使用UUID生成一个唯一字符串即可,也可以直接使用数据库表中的主键,主键就是唯一的. 那么,如果限制了流水号必须多少位,这种怎么生成呢? 可以采用"前缀+日期+数字"的方式(ps:此方式是需要用到缓存的) 前缀:为了更好的标识这个流水号是属于哪种类型: 日期:为了防止重复: 数字:为了表示当前的流水所处序号. 需求:生成一个17位数的唯一流水号,&

  • 教你用python3根据关键词爬取百度百科的内容

    前言 关于python版本,我一开始看很多资料说python2比较好,因为很多库还不支持3,但是使用到现在为止觉得还是pythin3比较好用,因为编码什么的问题,觉得2还是没有3方便.而且在网上找到的2中的一些资料稍微改一下也还是可以用. 好了,开始说爬百度百科的事. 这里设定的需求是爬取北京地区n个景点的全部信息,n个景点的名称是在文件中给出的.没有用到api,只是单纯的爬网页信息. 1.根据关键字获取url 由于只需要爬取信息,而且不涉及交互,可以使用简单的方法而不需要模拟浏览器. 可以直接

  • 手把手教你用Node.js爬虫爬取网站数据的方法

    开始之前请先确保自己安装了Node.js环境,还没有安装的的童鞋请看一下安装教程...... https://www.jb51.net/article/113677.htm https://www.jb51.net/article/57687.htm 直接开始吧 1.在项目文件夹安装两个必须的依赖包 npm install superagent --save-dev SuperAgent(官网是这样解释的) -----SuperAgent is light-weight progressive

随机推荐