为了账号安全,请及时绑定邮箱和手机立即绑定

【注意】恕我直言,我想教你抓取慕课的文章!

标签:
Java

现在的技术博客(社区)越来越多,比如:imooc、spring4All、csdn或者iteye等,有很多朋友可能在这些网站上都发表过博文,当有一天我们想自己搞一个博客网站时就会发现好多东西已经写过了,我们不可能再重新写一遍,况且多个平台上都有自己发表的文章,也不可能挨个去各个平台ctrl c + ctrl v,so,今天“小码”就教各位如何快速、便捷的做博客迁移! Let's go!

第一步,技术选型

今天分享的功能使用了开源的爬虫框架:WebMagic

WebMagic是一个国产的、简单灵活的Java爬虫框架。它有以下特点:

 1. 简单的API,可快速上手
 2. 模块化的结构,可轻松扩展
 3. 提供多线程和分布式支持

该框架的具体用法以及其帮助文档,请参考官网介绍,本文不做赘述。

第二步、实战

本文以本人在慕课网的文章为例进行数据抓取(抓的话就抓自己的就好了,抓别人的不是不可以,但要标注上原作者哈)。

一般对爬虫来说,都需要对Html文本进行解析,然后根据特定的抽取规则获取相应的内容。因此实战的第一步就是分析html结构,整理文章相关(标题、作作者、发布时间和文章内容等等)的抽取规则。

以https://www.imooc.com/article/41600该文章为例
图片描述
如图所示,需要抽取的一共为5部分:

1. 文章标题

2. 文章作者

3. 文章发布日期

4. 文章正文内容

5. 待抽取的其他文章列表

f12查看页面结构
图片描述
慕课的前端小伙伴还是很给力的,代码很整齐,因此抽取起来相对就简单的多!喏,整理相关规则如下:

标题://span[@class=js-title]/html()

作者://div[@class=name_con]/p[@class=name]/a[@class=nick]/html()

发布日期://div[@class='dc-profile']/div[@class='l']/span[@class='spacer']/text()

文章正文://div[@class=detail-content]/html()

待抽取的其他文章链接:/article/[0-9]{1,10}

接下来就要考虑账号登录的问题了,这一点,webMagic已经替我们留了接口了,在我们声明Site时,可以手动指定相关的Request参数,比如UA、Header以及Cookies,这几样我们都需要手动指定。具体的参数可以在浏览器中查看
图片描述

因为webMacgic不支持批量添加Cookie,所以我们需要将自己登陆后的Cookie逐条复制到程序中,可能有朋友会说,在Request Headers里的Cookie一大堆,一条条复制起来太难了!
图片描述

就知道会有人这么问,办法已经给你们想好了,喏~~!

图片描述

这个里面,一条条的,够方便了吧~~

接下来我可要上代码了~~!

首先添加依赖

<dependency>
    <groupId>us.codecraft</groupId>
    <artifactId>webmagic-core</artifactId>
    <version>0.7.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>us.codecraft</groupId>
    <artifactId>webmagic-extension</artifactId>
    <version>0.7.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.7.2</version>
</dependency>

然后编写自己的Processor并实现PageProcessor接口,然后重写里面的process和getSite方法即可。

getSite(设置网站信息)

@Override
public Site getSite() {
    return Site.me()
            .setCharset("UTF-8")
            .setDomain("www.imooc.com")
            .setSleepTime(1000)
            .setRetryTimes(2)

            //添加抓包获取的cookie信息
            .addCookie(".www.imooc.com", "cookie-key", "cookie-value")
            // .. 一堆cookie
            //添加请求头,有些网站会根据请求头判断该请求是由浏览器发起还是由爬虫发起的
            .setUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36")
            .addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
            .addHeader("Accept-Encoding", "gzip, deflate, br")
            .addHeader("Accept-Language", "zh-CN,zh;q=0.9")
            .addHeader("Connection", "keep-alive")
            .addHeader("Host", "www.imooc.com")
            .addHeader("Referer", "https://www.imooc.com");

}

process(抽取页面内容)

@Override
public void process(Page page) {
    Html pageHtml = page.getHtml();
    String title = pageHtml.xpath("//span[@class=js-title]/html()").get();
    String content = pageHtml.xpath("//div[@class=detail-content]/html()").get();
    if (isNotEmpty(title)) {
        String releaseDate = pageHtml.xpath("//div[@class='dc-profile']/div[@class='l']/span[@class='spacer']/text()").get();
        String author = pageHtml.xpath("//div[@class=name_con]/p[@class=name]/a[@class=nick]/html()").get();
        // 计数器
        page.putField("index", articleCount.incrementAndGet());
        page.putField("title", title);
        page.putField("releaseDate", releaseDate);
        page.putField("author", author);
        page.putField("content", content);
        page.putField("source", page.getRequest().getUrl());
    }
    page.addTargetRequests(page.getHtml().links().regex("/article/[0-9]{1,10}").all());
}

run(自定义方法,爬虫的启动函数)

public static void run(ImoocModel model) {
    System.out.println(">>  共" + model.getTotalPage() + "页数据...");
    List<String> urls = new LinkedList<>();
    for (int i = 1; i <= model.getTotalPage(); i++) {
        urls.add(String.format("https://www.imooc.com/u/%s/articles?page=%s", model.getUid(), i));
    }
    Spider spider = Spider.create(new ImoocSpiderProcessor())
            .addUrl(urls.toArray(new String[urls.size()]))
            .addPipeline((resultItems, task) -> {
                Map<String, Object> map = resultItems.getAll();
                String index = String.valueOf(map.get("index"));
                String title = String.valueOf(map.get("title"));
                if (!isNotEmpty(title)) {
                    return;
                }
                String source = String.valueOf(map.get("source"));
                String releaseDate = String.valueOf(map.get("releaseDate"));
                String author = String.valueOf(map.get("author"));
                System.out.println(String.format(">> 正在抓取 -- [%s] %s -- %s -- %s -- %s", index, source, title, releaseDate, author));
            })
            .thread(1);
    // 启动爬虫
    spider.run();
}

ImoocModel(参数类,很简单,就两个属性)

public class ImoocModel {
    private String uid;
    private Integer totalPage;

    public String getUid() {
        return uid;
    }

    public ImoocModel addUid(String uid) {
        this.uid = uid;
        return this;
    }

    public Integer getTotalPage() {
        return totalPage;
    }

    public ImoocModel addTotalPage(Integer totalPage) {
        this.totalPage = totalPage;
        return this;
    }
}

用户Id,和用户文章总页数,其中用户Id通过下面方式获取

图片描述

运行测试

图片描述

如图,文章已成功被抓取,剩下的,无非就是要么保存到文件中,要么持久化到数据库里。

第三步、总结

其实,本篇内容涉及到的技术并不难,主要重难点无非就一个:如何编写提取html内容的规则。规则一旦确定了,剩下的无非就是粘贴复制就能完成的代码而已。

本文同步发表到我的个人公众号:码一码,欢迎关注

还等什么,赶紧去试试啊~~~~
图片描述

源码近期我会整理好提交到我的开源项目上,敬请期待(PS:不光会有慕课的爬虫,还会有比如csdn、iteye等等的爬虫哦)!

客官请留步,在下告辞!!!

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消