部落格搬家系列(五)-爬取開源中國部落格
部落格搬家系列(五)-爬取開源中國部落格
一.前情回顧
部落格搬家系列(一)-簡介:https://blog.csdn.net/rico_zhou/article/details/83619152
部落格搬家系列(二)-爬取CSDN部落格:https://blog.csdn.net/rico_zhou/article/details/83619509
部落格搬家系列(三)-爬取部落格園部落格:https://blog.csdn.net/rico_zhou/article/details/83619525
部落格搬家系列(四)-爬取簡書文章:https://blog.csdn.net/rico_zhou/article/details/83619538
部落格搬家系列(六)-爬取今日頭條文章:https://blog.csdn.net/rico_zhou/article/details/83619564
部落格搬家系列(七)-本地WORD文件轉HTML:https://blog.csdn.net/rico_zhou/article/details/83619573
部落格搬家系列(八)-總結:https://blog.csdn.net/rico_zhou/article/details/83619599
二.開幹(獲取文章URL集合)
爬取開源中國的部落格思路跟CSDN一樣,同樣,我們找一個文章比較多的主頁為例分析原始碼,如
u後面的字串即為博主id,同簡書相像,經我們下拉發現,列表載入方式也是下拉自動載入,即滾動條到達一定程度時則js去請求後臺,那麼我們按下F12或者右擊審查元素,點選network檢視一下詳情
此時我們緩慢滾動滑鼠讓其繼續載入文章列表,我們發現多了一條請求widgets/_space_index_newest_blog?catalogId=0&q=&p=2&type=ajax
經測試發現p=2即表示頁數,將url輸入瀏覽器,發現頁面沒有了樣式,但是沒關係,文章的url還都在,我們只需要文章url
同樣,右擊檢視原始碼,尋找url,並程式碼獲取url
程式碼如下:
/**
* @date Oct 17, 2018 12:30:46 PM
* @Desc
* @param blogMove
* @param oneUrl
* @return
* @throws IOException
* @throws MalformedURLException
* @throws FailingHttpStatusCodeException
*/
public void getOsChinaArticleUrlList(Blogmove blogMove, String oneUrl, List<String> urlList)
throws FailingHttpStatusCodeException, MalformedURLException, IOException {
// 模擬瀏覽器操作
// 建立WebClient
WebClient webClient = new WebClient(BrowserVersion.CHROME);
// 關閉css程式碼功能
webClient.getOptions().setThrowExceptionOnScriptError(false);
webClient.getOptions().setCssEnabled(false);
// 如若有可能找不到檔案js則加上這句程式碼
webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
// 獲取第一級網頁html
HtmlPage page = webClient.getPage(oneUrl);
// System.out.println(page.asXml());
Document doc = Jsoup.parse(page.asXml());
Element pageMsg22 = doc.select("div.list-container.space-list-container").first();
if (pageMsg22 == null) {
return;
}
Elements pageMsg = pageMsg22.select("div.content");
Element linkNode;
for (Element e : pageMsg) {
linkNode = e.select("a.header").first();
if (linkNode == null) {
continue;
}
if (urlList.size() < blogMove.getMoveNum()) {
urlList.add(linkNode.attr("href"));
} else {
break;
}
}
return;
}
url集合獲取如下
三.開幹(獲取文章具體資訊)
接下來根據具體的url去獲取文章詳情,同樣,我們還是開啟一篇博文,以使用爬蟲框架htmlunit整合springboot出現的一個不相容問題為例,使用Chrome開啟,我們可以看到一些基本資訊,如文章的型別為原創,標題,時間,作者,閱讀數,文章文字資訊,圖片資訊等
這裡也需要特別注意一下的就是時間的獲取,同簡書文章時間顯示並不是唯一,他會將時間進行一些改變顯示,這裡需要注意一下,將獲取的時間反向解析一下,這裡不再過多講述。
同樣,右擊檢視原始碼找到對應的元素,然後獲取內容
程式碼如下:
/**
* @date Oct 17, 2018 12:46:52 PM
* @Desc 獲取詳細資訊
* @param blogMove
* @param url
* @return
* @throws IOException
* @throws MalformedURLException
* @throws FailingHttpStatusCodeException
*/
public Blogcontent getOsChinaArticleMsg(Blogmove blogMove, String url, List<Blogcontent> bList)
throws FailingHttpStatusCodeException, MalformedURLException, IOException {
Blogcontent blogcontent = new Blogcontent();
blogcontent.setArticleSource(blogMove.getMoveWebsiteId());
// 模擬瀏覽器操作
// 建立WebClient
WebClient webClient = new WebClient(BrowserVersion.CHROME);
// 關閉css程式碼功能
webClient.getOptions().setThrowExceptionOnScriptError(false);
webClient.getOptions().setCssEnabled(false);
// 如若有可能找不到檔案js則加上這句程式碼
webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
// 獲取第一級網頁html
HtmlPage page = webClient.getPage(url);
Document doc = Jsoup.parse(page.asXml());
// 獲取標題
String title = BlogMoveOsChinaUtils.getOsChinaArticleTitle(doc);
// 是否重複去掉
if (blogMove.getMoveRemoveRepeat() == 0) {
// 判斷是否重複
if (BlogMoveCommonUtils.articleRepeat(bList, title)) {
return null;
}
}
blogcontent.setTitle(title);
// 獲取作者
blogcontent.setAuthor(BlogMoveOsChinaUtils.getOsChinaArticleAuthor(doc));
// 獲取時間
if (blogMove.getMoveUseOriginalTime() == 0) {
blogcontent.setGtmCreate(BlogMoveOsChinaUtils.getOsChinaArticleTime(doc));
} else {
blogcontent.setGtmCreate(new Date());
}
blogcontent.setGtmModified(new Date());
// 獲取型別
blogcontent.setType(BlogMoveOsChinaUtils.getOsChinaArticleType(doc));
// 獲取正文
blogcontent.setContent(BlogMoveOsChinaUtils.getOsChinaArticleContent(doc, blogMove, blogcontent));
// 設定其他
blogcontent.setStatus(blogMove.getMoveBlogStatus());
blogcontent.setBlogColumnName(blogMove.getMoveColumn());
// 特殊處理
blogcontent.setArticleEditor(blogMove.getMoveArticleEditor());
blogcontent.setShowId(DateUtils.format(new Date(), DateUtils.YYYYMMDDHHMMSSSSS));
blogcontent.setAllowComment(0);
blogcontent.setAllowPing(0);
blogcontent.setAllowDownload(0);
blogcontent.setShowIntroduction(1);
blogcontent.setIntroduction("");
blogcontent.setPrivateArticle(1);
return blogcontent;
}
詳細資訊
/**
* @date Oct 17, 2018 1:10:19 PM
* @Desc 獲取標題
* @param doc
* @return
*/
public static String getOsChinaArticleTitle(Document doc) {
// 標題
Element pageMsg2 = doc.select("div.article-detail").first().select("h1.header").first();
return pageMsg2.ownText();
}
/**
* @date Oct 17, 2018 1:10:28 PM
* @Desc 獲取作者
* @param doc
* @return
*/
public static String getOsChinaArticleAuthor(Document doc) {
Element pageMsg2 = doc.select("div.article-detail").first().select("a.__user").first().select("span").first();
return pageMsg2.html();
}
/**
* @date Oct 17, 2018 1:10:33 PM
* @Desc 獲取時間
* @param doc
* @return
*/
public static Date getOsChinaArticleTime(Document doc) {
Element pageMsg2 = doc.select("div.article-detail").first().select("div.item").first();
String date = pageMsg2.ownText().trim();
if (date.startsWith("釋出於")) {
date = date.substring(date.indexOf("釋出於") + 3).trim();
}
if (date.indexOf(CommonSymbolicConstant.FORWARD_SLASH) < 4) {
date = DateUtils.format(new Date(), DateUtils.YYYY) + CommonSymbolicConstant.FORWARD_SLASH + date;
}
// 這地方時間格式變化太多暫時不實現
Date d = DateUtils.formatStringDate(date, DateUtils.YYYY_MM_DD_HH_MM_SS3);
// 注意有些格式不正確
return d == null ? new Date() : d;
}
/**
* @date Oct 17, 2018 1:10:37 PM
* @Desc 獲取型別
* @param doc
* @return
*/
public static String getOsChinaArticleType(Document doc) {
Element pageMsg2 = doc.select("div.article-detail").first().select("h1.header").first().select("div.horizontal")
.first();
if ("原".equals(pageMsg2.html())) {
return "原創";
} else if ("轉".equals(pageMsg2.html())) {
return "轉載";
} else if ("譯".equals(pageMsg2.html())) {
return "翻譯";
}
return "原創";
}
正文
/**
* @date Oct 17, 2018 1:10:41 PM
* @Desc 獲取正文
* @param doc
* @param object
* @param blogcontent
* @return
*/
public static String getOsChinaArticleContent(Document doc, Blogmove blogMove, Blogcontent blogcontent) {
Element pageMsg2 = doc.select("div.content#articleContent").first();
// 為了圖片顯示正常去掉一個元素
pageMsg2.select("div.ad-wrap").remove();
String content = pageMsg2.toString();
String images;
// 注意是否需要替換圖片
if (blogMove.getMoveSaveImg() == 0) {
// 儲存圖片到本地
// 先獲取所有圖片連線,再按照每個連結下載圖片,最後替換原有連結
// 先建立一個資料夾
// 先建立一個臨時資料夾
String blogFileName = String.valueOf(UUID.randomUUID());
FileUtils.createFolder(FilePathConfig.getUploadBlogPath() + File.separator + blogFileName);
blogcontent.setBlogFileName(blogFileName);
// 匹配出所有連結
List<String> imgList = BlogMoveCommonUtils.getArticleImgList(content);
// 下載並返回重新生成的imgurllist
List<String> newImgList = getOsChinaArticleNewImgList(blogMove, imgList, blogFileName);
// 拼接文章所有連結
images = BlogMoveCommonUtils.getArticleImages(newImgList);
blogcontent.setImages(images);
// 替換所有連結按順序
content = getOsChinaNewArticleContent(content, imgList, newImgList);
}
return content;
}
這裡需要注意的一點是,當我爬取文章後,發現圖片的排版和文字重合了,並且只要有圖片就會重合,大概是開源中國的部落格樣式不同導致,只需去掉標籤div.ad-wrap即可
// 為了圖片顯示正常去掉一個元素
pageMsg2.select("div.ad-wrap").remove();
最後獲取的正文
本人網站效果圖:
歡迎交流學習!
完整原始碼請見github: