博客搬家系列(五)-爬取開源中國博客

博客搬家系列(五)-爬取開源中國博客

一.前情回顧 

 博客搬家系列(一)-簡介: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一樣,同樣,我們找一個文章比較多的主頁爲例分析源碼,如https://my.oschina.net/u/222608

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:

 

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