使用正則表達式採集整站小說數據

背景

開源小說漫畫系統小說精品屋已經誕生了1年時間了,其間不少同窗諮詢過我數據抓取的原理,我這裏抽出空餘時間詳細說明一下小說爬蟲模塊的設計與實現。html

爬蟲模塊設計與實現(多爬蟲源配置)

   1. 建立application-crawl.yml配置文件,配置不一樣網站的正則表達式規則。git

#爬取的網站名稱類型 1:筆趣島 ,2:筆趣塔, 3:頂點,4:百書齋,5:筆趣閣,6: 筆趣窩,默認百書齋  更多網站解析中,敬請期待
biquta:
  crawlsource:
    index-url: https://m.biquta.la
    list-page-url: https://m.biquta.la/class/{0}/{1}.html
    book-url-pattern: href="/(\d+_\d+)/"
    score-pattern: <div\s+class="score">(\d+\.\d+)分</div>
    book-name-pattern: <p class="title">([^/]+)</p>
    author-pattern: 做者:([^/]+)<
    status-pattern: 狀態:([^/]+)</li>
    cat-pattern: 類別:([^/]+)</li>
    update-time-pattern: 更新:(\d+-\d+-\d+\s\d+:\d+:\d+)</a>
    pic-pattern: <img src="([^>]+)"\s+onerror="this.src=
    intro-pattern: class="review">([^<]+)</p>
    catalog-url-pattern: <a\s+href="(/du/\d+_\d+/)">查看完整目錄</a>
    catalog-pattern: <a\s+style=""\s+href="(/\d+_\d+/\d+\.html)">([^/]+)</a>
biqudao:
  crawlsource:
    index-url: https://m.biqudao.net
    list-page-url: https://m.biqudao.net/bqgeclass/{0}/{1}.html
    book-url-pattern: href="/(bqge\d+)/"
    score-pattern: <div\s+class="score">(\d+\.\d+)分</div>
    book-name-pattern: <p class="title">([^/]+)</p>
    author-pattern: <li class="author">做者:([^/]+)</li>
    status-pattern: 狀態:([^/]+)</li>
    cat-pattern: 類別:([^/]+)</li>
    update-time-pattern: 更新:(\d+-\d+-\d+\s\d+:\d+:\d+)</a>
    pic-pattern: <img src="([^>]+)"\s+onerror="this.src=
    intro-pattern: class="review">([^<]+)</p>
    catalog-url-pattern: <a\s+href="(/bqge\d+/all\.html)">查看完整目錄</a>
    catalog-pattern: <a[^/]+style[^/]+href="(/bqge\d+/\d+\.html)">([^/]+)</a>

dingdian:
  crawlsource:
    index-url: http://m.xdingdiann.com
    list-page-url: http://m.xdingdiann.com/sort/{0}/{1}.html
    book-url-pattern: href="/(ddk\d+)/"
    score-pattern: <div\s+class="score">(\d+\.\d+)分</div>
    book-name-pattern: <p class="title">([^/]+)</p>
    author-pattern: 做者:([^/]+)<
    status-pattern: 狀態:([^/]+)</li>
    cat-pattern: 類別:([^/]+)</li>
    update-time-pattern: 更新:(\d+-\d+-\d+\s\d+:\d+:\d+)</a>
    pic-pattern: <img src="([^>]+)"\s+onerror="this.src=
    intro-pattern: class="review">([^/]+)</p>
    catalog-url-pattern: <a\s+href="(/ddk\d+/all.html)">查看完整目錄</a>
    catalog-pattern: <a\s+style=""\s+href="(/ddk\d+/\d+\.html)">([^/]+)</a>

baishuzhai:
  crawlsource:
    index-url: https://m.baishuzhai.com
    list-page-url: https://m.baishuzhai.com/sort/{0}/{1}.html
    book-url-pattern: href="/(ibook/\d+/\d+)/"
    score-pattern: <div\s+class="score">(\d+\.\d+)分</div>
    book-name-pattern: <p class="title">([^/]+)</p>
    author-pattern: 做者:([^/]+)<
    status-pattern: 狀態:([^/]+)</li>
    cat-pattern: 類別:([^/]+)</li>
    update-time-pattern: 更新:(\d+-\d+-\d+)</li>
    pic-pattern: <img src="([^>]+)"\s+onerror="this.src=
    intro-pattern: class="review">([^/]+)</p>
    catalog-url-pattern: <a\s+href="(/ibook/\d+/\d+/all\.html)">查看完整目錄</a>
    catalog-pattern: <a\s+style=""\s+href="(/ibook/\d+/\d+/\d+\.html)">([^/]+)</a>

biquge:
  crawlsource:
    index-url: http://m.biquge.info
    list-page-url: http://m.biquge.info/paihangbang_lastupdate/{0}.html
    book-url-pattern: href="/(\d+_\d+)/"
    score-pattern: <i>(\d+)</i>
    book-name-pattern: <a\s+href="/(\d+_\d+)/">([^<]+)</a>
    author-pattern: 做者:([^<]+)<
    status-pattern: <p>狀態:([^<]+)</p>
    cat-pattern: <a\s+href="/list/\d+_\d+\.html">([^<]+)</a>
    update-time-pattern: <p>更新:(\d+-\d+-\d+T\d+:\d+:\d+)</p>
    pic-pattern: <div\s+class="block_img2">\s*<img\s+src="([^"]+)"
    intro-pattern: class="review">([^/]+)</p>
    catalog-url-pattern: <a\s+href="(/ddk\d+/all.html)">查看完整目錄</a>
    catalog-pattern: <dd>\s*<a\s+href="(\d+\.html)"\s+title="([^"]+)">([^<]+)</a>\s*</dd>
複製代碼

   2. 建立基礎的爬蟲源抽象類,用於接收網站通用配置參數和定義基礎的解析入庫函數。github

/**
 * 爬蟲源
 * @author 11797
 */
@Data
public abstract class BaseCrawlSource {

    /**
     * 採集的小說最低評分配置
     * */
    @Value("${books.lowestScore}")
    private Float lowestScore;

    /**
     * 解析數據
     * */
    public abstract void parse();

    /**
     * 更新書籍
     * */
    public abstract void update();
}
複製代碼

   3. 建立不一樣類型網站(Html,Json)的爬蟲源抽象類,繼承BaseCrawlSource,用於接收網站規則配置參數。web

/**
 * html爬蟲源
 * @author 11797
 */
@Data
public abstract class BaseHtmlCrawlSource extends BaseCrawlSource{

    /**
     * 首頁url
     * */
    private String indexUrl;

    /**
     * 列表頁url
     * */
    private String listPageUrl;

    /**
     * 書籍url Pattern
     * */
    private String bookUrlPattern;

    /**
     * 評分 Pattern
     * */
    private String scorePattern;

    /**
     * 書名 Pattern
     * */
    private String bookNamePattern;

    /**
     * 做者 Pattern
     * */
    private String authorPattern;

    /**
     * 狀態 Pattern
     * */
    private String statusPattern;

    /**
     * 類別 Pattern
     * */
    private String catPattern;

    /**
     * 更新時間 Pattern
     * */
    private String updateTimePattern;

    /**
     * 封面 Pattern
     * */
    private String picPattern;

    /**
     * 簡介 Pattern
     * */
    private String introPattern;

    /**
     * 完整目錄頁url Pattern
     * */
    private String catalogUrlPattern;

    /**
     * 目錄 Pattern
     * */
    private String catalogPattern;

}
複製代碼

   4. 建立多個不一樣網站的爬蟲源配置類,這裏以百書齋爬蟲源爲例。正則表達式

/**
 * 百書齋爬蟲源配置類
 * @author 11797
 */
@Slf4j
@Configuration
public class CrawlBaishuzhaiConfig {

    /**
     * 必須加此@Primary註解,否則報錯,表示有多個爬蟲源配置類被加載時默認加載此配置類
     * 下一個爬蟲源配置類則不須要添加
     * */
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "baishuzhai.crawlsource")
    @ConditionalOnProperty(prefix = "crawl.website",name = "type",havingValue = "4")
    public BaseHtmlCrawlSource dingdianCrawlSource() {
        return new BiquCrawlSource();
    }

}
複製代碼

   5. 經過配置開啓默認爬蟲源。markdown

#爬取的網站名稱類型 1:筆趣島 ,2:筆趣塔,3:頂點小說 ,4:百書齋,6: 筆趣窩 更多網站解析中,敬請期待
crawl:
  website:
    type: 4
複製代碼

   6. 定義具體的爬蟲源類BiquCrawlSource,繼承抽象類BaseHtmlCrawlSource,實現源網站解析方法parse()和數據更新方法update()。解析方法解析小說基礎屬性,生成解析日誌,更新方法經過解析日誌採集小說全部數據進行新書入庫和老書更新,代碼過多,只貼出部分解析代碼。app

//解析第一頁小說的數據
                //小說頁URI正則匹配
                Pattern bookUriPatten = compile(getBookUrlPattern());
                Matcher bookUriMatcher = bookUriPatten.matcher(bookListHtml);
                boolean bookUriFind = bookUriMatcher.find();

                //小說評分正則匹配
                Pattern scorePatten = compile(getScorePattern());
                Matcher scoreMatch = scorePatten.matcher(bookListHtml);
                boolean scoreFind = scoreMatch.find();

                //小說名正則匹配
                Pattern bookNamePatten = compile(getBookNamePattern());
                Matcher bookNameMatch = bookNamePatten.matcher(bookListHtml);
                boolean bookNameFind = bookNameMatch.find();

                while (bookUriFind && scoreFind && bookNameFind) {
                    try {
                        //小說基礎信息可以匹配到
                        Float score = Float.parseFloat(scoreMatch.group(1));
                        if (score < getLowestScore()) {
                            //只採集指定評分以上的小說
                            continue;
                        }

                        //獲取小說基礎信息,生成採集日誌
                        String bookUri = bookUriMatcher.group(1);
                        String bookUrl = getIndexUrl() + "/" + bookUri + "/";
                        String bookName = bookNameMatch.group(1);
                        bookService.addBookParseLog(bookUrl, bookName, score, (byte) 10);

                    } catch (Exception e) {
                        //小說解析出現異常,不作處理,繼續下一本小說的解析
                        log.error(e.getMessage(), e);
                    } finally {
                        //跳到下一本小說的解析
                        bookUriMatcher.find();
                        bookUriFind = bookUriMatcher.find();
                        scoreFind = scoreMatch.find();
                        bookNameFind = bookNameMatch.find();
                    }
                }
複製代碼

   7. 監聽程序啓動,注入爬蟲源,循環執行源網站解析方法parse()和數據更新方法update() 。函數

設計原理

爲了實時(和源站小說最新章節更新時間保持一致)的採集小說最新更新章節,程序須要循環不斷的獲取源站最新更新小說分頁列表信息,若是一本小說數據所有采集完再採集下一本,而一本小說全部數據採集完是秒級甚至是分鐘級的,這樣採集完一頁是須要必定時間的,而源站最新更新列表是不斷刷新的,這樣就會由於採集到的分頁數據不連貫而致使小說採集遺漏,故小說解析(毫秒級,不會致使採集到的分頁數據不連貫)與小說更新操做(秒級或分鐘級)分開來作。oop

項目倉庫地址

github.com/201206030/f…網站