文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

教你如何用Java简单爬取WebMagic

2024-04-02 19:55

关注

一、Java爬虫——WebMagic

 1.1 WebMagic总体架构图

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 {

    
    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 {
    
    public static final String PAGE_CSS_CONTENT = "div.content";

    
    public static final String END_CONTENT = "<div id=\"adInArticle\"></div>";

    
    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;

    
    public static final String REX_IMG_SRC = "src\\s*=\\s*\"?(.*?)(\"|>|\\s+)";

    
    public static final String REX_IMG_SUFFIX = "[\\w]+[\\.](jpeg|jpg|png)";

    
    public static final String XPATH_IMG = "/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内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯