博客搬家系列(四)-爬取简书文章

11 篇文章 0 订阅
10 篇文章 0 订阅

博客搬家系列(四)-爬取简书文章

一.前情回顾 

 博客搬家系列(一)-简介: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/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
 

二.开干(获取文章URL集合)

爬取简书的文章思路跟CSDN一样,且下载图片那一步更为简单,任何header都不需要设置,同样,我们找一个文章比较多的主页为例分析源码,如https://www.jianshu.com/u/b52ff888fd17  u后面的字符串即为博主id,经我们下拉发现,简书的文章列表加载方式是下拉自动加载,即滚动条到达一定程度时则js去请求后台,那么我们按下F12或者右击审查元素,点击network查看一下详情

我们点击XHR(XMLHttpRequest)查看请求如下

暂时没啥有用信息,此时我们缓慢滚动鼠标让其继续加载文章列表,我们发现多了一条请求:?order_by=shared_at&page=2

猜测page=2即表示文章的页数,将url复制到浏览器打开,可以看到正是下一页的文章,此时文章url的规律找到,接下来右击查看源码寻找包含文章url的标签,可以发现,文章的url在class为note-list的ul标签下的子标签,class为content的div内,这就好办了,代码如下:注意url不完整,需要补充拼接一下

/**
	 * @date Oct 17, 2018 12:30:46 PM
	 * @Desc
	 * @param blogMove
	 * @param oneUrl
	 * @return
	 * @throws IOException
	 * @throws MalformedURLException
	 * @throws FailingHttpStatusCodeException
	 */
	public void getJianShuArticleUrlList(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("ul.note-list").first();
		if (pageMsg22 == null) {
			return;
		}
		Elements pageMsg = pageMsg22.select("div.content");
		Element linkNode;
		for (Element e : pageMsg) {
			linkNode = e.select("a.title").first();
			if (linkNode == null) {
				continue;
			}
			if (urlList.size() < blogMove.getMoveNum()) {
				urlList.add(BlogConstant.BLOG_BLOGMOVE_WEBSITE_BASEURL_JIANSHU + 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 getJianShuArticleMsg(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 = BlogMoveJianShuUtils.getJianShuArticleTitle(doc);
		// 是否重复去掉
		if (blogMove.getMoveRemoveRepeat() == 0) {
			// 判断是否重复
			if (BlogMoveCommonUtils.articleRepeat(bList, title)) {
				return null;
			}
		}
		blogcontent.setTitle(title);
		// 获取作者
		blogcontent.setAuthor(BlogMoveJianShuUtils.getJianShuArticleAuthor(doc));
		// 获取时间
		if (blogMove.getMoveUseOriginalTime() == 0) {
			blogcontent.setGtmCreate(BlogMoveJianShuUtils.getJianShuArticleTime(doc));
		} else {
			blogcontent.setGtmCreate(new Date());
		}
		blogcontent.setGtmModified(new Date());
		// 获取类型
		blogcontent.setType(BlogMoveJianShuUtils.getJianShuArticleType(doc));
		// 获取正文
		blogcontent.setContent(BlogMoveJianShuUtils.getJianShuArticleContent(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 getJianShuArticleTitle(Document doc) {
		// 标题
		Element pageMsg2 = doc.select("div.note").first().select("h1.title").first();
		return pageMsg2.html();
	}

	/**
	 * @date Oct 17, 2018 1:10:28 PM
	 * @Desc 获取作者
	 * @param doc
	 * @return
	 */
	public static String getJianShuArticleAuthor(Document doc) {
		Element pageMsg2 = doc.select("div.note").first().select("span.name").first();
		return pageMsg2.text();
	}

	/**
	 * @date Oct 17, 2018 1:10:33 PM
	 * @Desc 获取时间
	 * @param doc
	 * @return
	 */
	public static Date getJianShuArticleTime(Document doc) {
		Element pageMsg2 = doc.select("div.note").first().select("span.publish-time").first();
		String date = pageMsg2.html();
		// 注意有些格式不正确
		return DateUtils.formatStringDate(date, DateUtils.YYYY_MM_DD_HH_MM_SS2);
	}

	/**
	 * @date Oct 17, 2018 1:10:37 PM
	 * @Desc 获取类型
	 * @param doc
	 * @return
	 */
	public static String getJianShuArticleType(Document doc) {
		// Element pageMsg2 =
		// doc.select("div.article-title-box").first().select("span.article-type").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 getJianShuArticleContent(Document doc, Blogmove blogMove, Blogcontent blogcontent) {
		Element pageMsg2 = doc.select("div.note").first().select("div.show-content").first();
		// 为了图片显示正常去掉一个元素
		pageMsg2.select("div.image-container-fill").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.getArticleImgList2(content);
			// 下载并返回重新生成的imgurllist
			List<String> newImgList = BlogMoveCommonUtils.getArticleNewImgList(blogMove, imgList, blogFileName);
			// 拼接文章所有链接
			images = BlogMoveCommonUtils.getArticleImages(newImgList);
			blogcontent.setImages(images);
			// 替换所有链接按顺序
			content = getJianShuNewArticleContent(content, imgList, newImgList);

		}

		return content;
	}

这里的下载图片也需要注意,当我测试文章时,发现有些图片下载了有些则没有,注意观察一下源码发现,img标签中的src并不是同步加载,即当我获取文章正文时,可能图片链接尚未加载到源码中,但是此img标签中有另一个属性可用,即data-original-src,显然这是图片的原路径,那么我们就根据这个路径去下载图片,然后将自己的图片路径更改到src属性中,这样就可全部下载显示了

相关代码更改如下,获取链接

/**
	 * @date Oct 17, 2018 1:10:41 PM
	 * @Desc 获取链接
	 * @param doc
	 * @param object
	 * @param blogcontent
	 * @return
	 */
	public static List<String> getArticleImgList2(String content) {
		// 注意,当爬取简书文章时,图片异步加载,有些并没有完全加载出img,所以只能换方法
		if (content == null) {
			return null;
		}
		List<String> imgList = new ArrayList<String>();
		Document doc = Jsoup.parse(content);
		Elements imgTags = doc.select("img[data-original-src]");
		for (Element element : imgTags) {
			imgList.add("https:" + element.attr("data-original-src"));
		}
		return imgList;
	}

替换img链接代码

/**
	 * @date Oct 22, 2018 3:31:40 PM
	 * @Desc
	 * @param content
	 * @param imgList
	 * @param newImgList
	 * @return
	 */
	private static String getJianShuNewArticleContent(String content, List<String> imgList, List<String> newImgList) {
		Document doc = Jsoup.parse(content);
		Elements imgTags = doc.select("img[data-original-src]");
		if (imgList == null || imgList.size() < 1 || newImgList == null || newImgList.size() < 1 || imgTags == null
				|| "".equals(imgTags)) {
			return content;
		}
		for (int i = 0; i < imgTags.size(); i++) {
			imgTags.get(i).attr("src", newImgList.get(i));
		}
		return doc.body().toString();
	}

最后获取的正文如下:

本人网站效果图:

欢迎交流学习!

完整源码请见github:https://github.com/ricozhou/blogmove​​​​​​​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值