開源小說漫畫系統小說精品屋已經誕生了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