本系列文章生成的镜像站已上传到 Gitee Pages 托管平台,可通过如下地址查看完成后的效果:https://jhlxge.gitee.io/coolshell-mirror/
2024年6月5日更新 :Gitee Pages服务已停止提供服务,可通过本机托管的如下地址访问:https://coolshell-mirror.work.zhuzhilong.com/
在前面的数据准备章节咱们已经在 magic-boot 基础工程上整合了 jsoup,而 jsoup 正是 java 领域数据爬取及 html 解析的神器,对于 html 的处理如同 JS 中的 jQuery 一般神一样的存在,而且一些 API 跟 jQuery 也比较接近,使用起来不得不说那叫一个丝滑。废话少说,咱们开始 show me the code。
01采集列表
通过分析酷壳首页的内容列表,我们发现一共有74页,并且页面URL规则是:https://coolshell.cn/page/${页码},经过验证该规则也适用于第一页,那我们就先把所有列表页的html采集过来,存储到 crawler_list 表,爬取代码如下:
import cn.hutool.core.util.StrUtil
import org.jsoup.Jsoup
import cn.hutool.core.date.DateUtil
import cn.hutool.core.thread.ThreadUtil
import log
var START_PAGE = 1
var END_PAGE = 74
var BASE_URL = 'https://coolshell.cn/'
var currentPage = START_PAGE
var timer = DateUtil.timer(); // 定义计时器,用于记录时间
var MAX_RETRY_COUNT = 100
var currentRetryCount = 0;
while(currentPage <= END_PAGE) {
var pageURL = currentPage === 1 ? BASE_URL : BASE_URL + "page/" + currentPage
try {
var doc = Jsoup.connect(pageURL).timeout(30000).userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36").get();
var html = doc.outerHtml();
log.info(pageURL)
if (StrUtil.isNotBlank(html)) {
db['MDC'].table('crawler_list').insert({
siteId:1,
pageUrl: pageURL,
pageHtml: html,
successFlag:'Y'
})
}
currentPage++
currentRetryCount = 0
} catch(e) {
// 暂停1秒,避免请求间隔太短,导致 Matomo 负载过高拿不到数据
ThreadUtil.sleep(1000);
currentRetryCount++;
if (currentRetryCount == MAX_RETRY_COUNT) {
exit 0, `爬取第${currentPage}页数据失败,页面URL:${pageURL}`;
}
}
}
log.info(`Cost Time: ${timer.intervalPretty()}.`);
所有列表页数据爬取完后,crawler_list表的数据如下:
分析列表页获取文章详情页的URL列表
得到列表数据后我们需要分析列表html,得到每页中博客页面的URL,下面是代码:
import cn.hutool.core.util.StrUtil
import org.jsoup.Jsoup
import cn.hutool.core.date.DateUtil
import cn.hutool.json.JSONUtil
import log
var timer = DateUtil.timer(); // 定义计时器,用于记录时间
var pageList = db['MDC'].select('select id,page_html from crawler_list')
for (pageItem in pageList) {
var pageHtml = pageItem.pageHtml
var doc = Jsoup.parse(pageHtml)
var links = doc.select('.entry-title a')
log.info(links.toString())
var linkArr = []
for (linkItem in links) {
var linkURL = linkItem.attr('href')
var linkTitle = linkItem.text()
linkArr.push({
linkURL,
linkTitle
})
db['MDC'].table('crawler_article').insert({
siteId:1,
pageUrl: linkURL,
articleTitle:linkTitle
})
}
var updateMap = {
id: pageItem.id,
targetLinks: JSONUtil.toJsonStr(linkArr)
}
db['MDC'].table('crawler_list').primary('id').update(updateMap)
}
log.info(`Cost Time: ${timer.intervalPretty()}.`);
爬取文章详情页 html
分析完成后,在 crawler_article 表会得到所有文章详情页的URL,然后我们遍历 crawler_article 表的记录,逐个URL爬取回每篇博客内容的 html,以下是爬取代码:
import cn.hutool.core.util.StrUtil
import org.jsoup.Jsoup
import cn.hutool.core.date.DateUtil
import cn.hutool.json.JSONUtil
import cn.hutool.core.thread.ThreadUtil
import log
var timer = DateUtil.timer(); // 定义计时器,用于记录时间
var pageList = db['MDC'].select('select id,page_url from crawler_article where page_html is null')
var MAX_RETRY_COUNT = 100
for (item in pageList) {
var currentRetryCount = 0;
var needCrawler = true
var pageURL = item.pageUrl
// 由于爬取过程中经常会出现超时未响应的情况,这里使用while增加了重试机制
while(needCrawler) {
try {
var doc = Jsoup.connect(pageURL).timeout(30000).userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36").get();
var html = doc.outerHtml();
if(StrUtil.isNotBlank(html)) {
var updateMap = {
id: item.id,
pageHtml: html,
crawlerTime:DateUtil.now()
}
db['MDC'].table('crawler_article').primary('id').update(updateMap)
}
needCrawler = false
ThreadUtil.sleep(2000);
} catch(e) {
log.error(e.getMessage())
currentRetryCount++
log.info(`采集 ${pageURL} 失败,已重试 ${currentRetryCount} 次。`)
if (currentRetryCount == MAX_RETRY_COUNT) {
exit 0, '重试次数过多';
}
}
}
}
log.info(`Cost Time: ${timer.intervalPretty()}.`);
需要说明一下,由于coolshell.cn 服务器在国外,爬取过程有些不稳定,所以我加入了重试机制。
至此,我们需要的博客内容已经爬取完毕。接下来的章节,我将分析爬取的html,得到我们需要的博客内容数据。
评论 (0)