博客搬家系列(三)-爬取博客园博客
博客搬家系列(一)-简介: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/83619538
博客搬家系列(五)-爬取开源中国博客:https://blog.csdn.net/rico_zhou/article/details/83619561
博客搬家系列(六)-爬取今日头条文章: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
爬取博客园的博客思路跟CSDN一样,且下载图片那一步更为简单,任何header都不需要设置,同样,我们以ricozhou的主页为例分析源码https://www.cnblogs.com/ricozhou/
我们可以看到文章列表如下,依然是很简洁的url,我们找一个博主文章较多的看看,方便分析规律,如https://www.cnblogs.com/xdp-gacl/
当我们点击下一页的时候,url如下
显然最后的2是页数,这样我们就找到了页面url规律,同样右击查看源码,分析找到文章都位于哪个标签内
观察发现,文章url均位于class为postTitle的标签内,代码如下:
/**
* @date Oct 17, 2018 12:30:46 PM
* @Desc
* @param blogMove
* @param oneUrl
* @return
* @throws IOException
* @throws MalformedURLException
* @throws FailingHttpStatusCodeException
*/
public void getCnBlogArticleUrlList(Blogmove blogMove, String oneUrl, List 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());
Elements pageMsg = doc.select("div.postTitle");
Element linkNode;
for (Element e : pageMsg) {
linkNode = e.select("a.postTitle2").first();
if (linkNode == null) {
continue;
}
if (urlList.size() < blogMove.getMoveNum()) {
urlList.add(linkNode.attr("href"));
} else {
break;
}
}
return;
}
获取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 getCnBlogArticleMsg(Blogmove blogMove, String url, List 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 = BlogMoveCnBlogUtils.getCnBlogArticleTitle(doc);
// 是否重复去掉
if (blogMove.getMoveRemoveRepeat() == 0) {
// 判断是否重复
if (BlogMoveCommonUtils.articleRepeat(bList, title)) {
return null;
}
}
blogcontent.setTitle(title);
// 获取作者
blogcontent.setAuthor(BlogMoveCnBlogUtils.getCnBlogArticleAuthor(doc));
// 获取时间
if (blogMove.getMoveUseOriginalTime() == 0) {
blogcontent.setGtmCreate(BlogMoveCnBlogUtils.getCnBlogArticleTime(doc));
} else {
blogcontent.setGtmCreate(new Date());
}
blogcontent.setGtmModified(new Date());
// 获取类型
blogcontent.setType(BlogMoveCnBlogUtils.getCnBlogArticleType(doc));
// 获取正文
blogcontent.setContent(BlogMoveCnBlogUtils.getCnBlogArticleContent(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 getCnBlogArticleTitle(Document doc) {
// 标题
Element pageMsg2 = doc.select("div#post_detail").first().select("h1.postTitle").first().select("a").first();
return pageMsg2.ownText();
}
/**
* @date Oct 17, 2018 1:10:28 PM
* @Desc 获取作者
* @param doc
* @return
*/
public static String getCnBlogArticleAuthor(Document doc) {
Element pageMsg2 = doc.select("div.postDesc").first().select("a").first();
return pageMsg2.ownText();
}
/**
* @date Oct 17, 2018 1:10:33 PM
* @Desc 获取时间
* @param doc
* @return
*/
public static Date getCnBlogArticleTime(Document doc) {
Element pageMsg2 = doc.select("div.postDesc").first().select("span#post-date").first();
String date = pageMsg2.ownText().trim();
// 这地方时间格式变化太多暂时不实现
Date d = DateUtils.formatStringDate(date, DateUtils.YYYY_MM_DD_HH_MM_SS4);
// 注意有些格式不正确
return d == null ? new Date() : d;
}
/**
* @date Oct 17, 2018 1:10:37 PM
* @Desc 获取类型
* @param doc
* @return
*/
public static String getCnBlogArticleType(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 getCnBlogArticleContent(Document doc, Blogmove blogMove, Blogcontent blogcontent) {
Element pageMsg2 = doc.select("div#post_detail").first().select("div#cnblogs_post_body").first();
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 imgList = BlogMoveCommonUtils.getArticleImgList(content);
// 下载并返回重新生成的imgurllist
List newImgList = BlogMoveCommonUtils.getArticleNewImgList(blogMove, imgList, blogFileName);
// 拼接文章所有链接
images = BlogMoveCommonUtils.getArticleImages(newImgList);
blogcontent.setImages(images);
// 替换所有链接按顺序
content = getCnBlogNewArticleContent(content, imgList, newImgList);
}
return content;
}
代码共用,不再多放。还是一样的步骤,获取正文源码html匹配img,下载img,替换img链接,返回替换后的HTML
本人网站效果图:
欢迎交流学习!
完整源码请见github:https://github.com/ricozhou/blogmove