SpringBoot(29) 整合WebMagic實現爬取和解析CSDN文章數據

一、前言

  1. WebMagic:一款簡單靈活的爬蟲框架,基於它我們可以非常容易的編寫一個爬蟲。
  2. 官網文檔地址:http://webmagic.io/docs/zh/

在這裏插入圖片描述

下面小編將通過爬取+解析自己的csdn文章數據來演示一個簡單的爬蟲案例demo

二、SpringBoot 整合 WebMagic

1、pom.xml中引入相關依賴

<!-- WebMagic:爬蟲 -->
<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>

2、定義全局常用變量-博主博客地址

public class Constants {

    /**
     * csdn博主博客地址
     */
    public static final String CSDN_URL = "https://blog.csdn.net/qq_38225558/article/list/1";

}

3、CSDN博客文章信息實體類

@Data
@AllArgsConstructor
public class Csdn {

    /**
     * id
     */
    private int id;
    /**
     * 文章標題
     */
    private String title;
    /**
     * 文章發佈時間
     */
    private String time;
    /**
     * 文章所屬分類
     */
    private String category;
    /**
     * 文章內容
     */
    private String content;

}

4、編寫一個簡單的爬蟲

實現PageProcessor

@Slf4j
public class SamplePageProcessor implements PageProcessor {

    /**
     * 記錄總分頁列表url數
     */
    private static List<String> urlList = new ArrayList<>();

    /**
     * 文章詳情信息
     */
    private static List<Csdn> articleDetailInfoList = new ArrayList<>();

    /**
     * 【部分一】:抓取網站的相關配置,包括編碼、抓取間隔、重試次數等
     */
    private Site site = Site.me().
    // 重試次數
        setRetryTimes(3).
        // 抓取間隔
        setSleepTime(1000).
        // 超時時間
        setTimeOut(100 * 1000);

    /**
     * process是定製爬蟲邏輯的核心接口,在這裏編寫抽取邏輯
     *
     * @param page:
     *            頁面數據
     * @return: void
     * @author : zhengqing
     * @date : 2020/7/1 16:43
     */
    @Override
    public void process(Page page) {
        // 【部分二】:定義如何抽取頁面信息,並保存下來
        Html html = page.getHtml();

        // 根據url判斷該頁面屬於列表頁還是文章詳情頁面
        String url = page.getUrl().toString();
        log.info("頁面url地址:【{}】", url);
        if (url.contains("article/details")) {
            // 詳情頁面處理邏輯...
            // 文章id
            int articleId = Integer.parseInt(url.substring(url.lastIndexOf('/') + 1));
            // 文章標題
            String articleTitle = html.xpath("//h1[@class='title-article']//text()").toString();
            // 文章發佈時間
            String articleTime = html.xpath("//div[@class='bar-content']//span[@class='time']//text()").toString();
            // 文章所屬分類
            String articleCategory = html.xpath("//a[@class='tag-link']//text()").toString();
            // 文章內容
            String articleContent = html.xpath("//article[@class='baidu_pl']").toString();
            log.info("文章id:【{}】  文章標題:【{}】 文章所屬分類:【{}】", articleId, articleTitle, articleCategory);
            Csdn csdn = new Csdn(articleId, articleTitle, articleTime, articleCategory, articleContent);
            if (articleDetailInfoList.contains(csdn)) {
                return;
            }
            articleDetailInfoList.add(csdn);
        } else if (url.contains("article/list")) {
            // 列表頁面處理邏輯...
            if (urlList.contains(url)) {
                return;
            }
            urlList.add(url);
            List<Selectable> articleList = html
                .xpath("//div[@class='article-list']//div[@class='article-item-box csdn-tracking-statistics']").nodes();
            if (CollectionUtils.isEmpty(articleList)) {
                // 這裏移除最後一條錯誤元素
                urlList.remove(urlList.get(urlList.size() - 1));
                log.info("總列表數:【{}】  總文章數:【{}】", urlList, articleDetailInfoList);
                return;
            }
            // 開始解析每一篇文章數據【文章標題,發送時間,文章詳情url地址】
            articleList.forEach(article -> {
                // 文章標題
                String articleTitle = article.$("a", "text").toString();
                // 文章詳情url地址
                String articleUrl = article.links().toString();
                // 文章發佈時間
                String articleTime =
                    article.xpath("//div[@class='info-box d-flex align-content-center']//span[@class='date']/text()")
                        .toString();

                log.info("文章標題:【{}】 文章地址:【{}】 文章發佈時間:【{}】", articleTitle, articleUrl, articleTime);

                // 進入文章內部獲取文章詳情內容
                page.addTargetRequests(article.links().all());
            });

            // 【部分三】:從頁面發現後續的url地址來抓取 (這裏因csdn暫時拿不了頁面尾部的分頁數,因此手動模擬了一下數據...)
            int nextPage = Integer.parseInt(url.substring(url.lastIndexOf('/') + 1)) + 1;
            String newUrl = "https://blog.csdn.net/qq_38225558/article/list/" + nextPage;
            page.addTargetRequest(newUrl);
        } else {
            // Other ...
            log.error("該頁面url【{}】無法解析...", url);
        }
    }

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

    public static void main(String[] args) {
        Spider.create(new SamplePageProcessor())
            // 從指定的url地址開始抓
            .addUrl(Constants.CSDN_URL)
            // 開啓5個線程抓取
            .thread(5)
            // 啓動爬蟲
            .run();
    }

}

5、定時器定時爬取數據

這部分看自己需求,不是必要…

① 啓動類開啓定時任務

在這裏插入圖片描述

② 編寫定時任務

@Slf4j
@Component
public class AppScheduledJobs {

    /**
     * 每10秒執行一次
     *
     * @return: void
     * @author : zhengqing
     * @date : 2020/7/1 11:44
     */
    @Scheduled(cron = "*/10 * * * * ?")
    public void cralwer() {
        log.info("<<<<<< Start: 【{}】 >>>>>>", LocalDateTime.now());
        Spider.create(new SamplePageProcessor())
            // .setDownloader(new HttpClientDownloader())
            // 從指定的url地址開始抓
            .addUrl(Constants.CSDN_URL)
            // 開啓5個線程抓取
            .thread(5)
            // 啓動爬蟲
            .run();
    }

}

三、運行項目測試

可以看到我們解析獲取到的文章標題,文章內容,文章發佈時間等一系列信息…
在這裏插入圖片描述


本文案例demo源碼

https://gitee.com/zhengqingya/java-workspace

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章