SpringBoot项目通过WebCollector + @Scheduled实现定时爬取网站任务
一、编写网站爬取程序
1、继承BreadthCrawler类
继承BreadthCrawler类
2、根据自己的需求写构造方法
如:我写的这个无参构造方法,其中super的第一个参数是爬取过程中产生的文件夹路径(文件夹中的文件主要是维护爬取过程中的URL等信息),第二个参数是指是否根据设置的正则表达式自动探取新的URL。添加正则表达式的方法addRegex(String urlRegex),参数为一个 url 正则表达式,可以用于过滤不必抓取的链接,如 .js .jpg .css … 等,也可用于抓取指定某些规则的链接,如:
addRegex("https://www.bilibili.com/video/BV1Kt411g7Sq?p=/[0-9]{2}[^/]");
public ProcurementCrawler() {
super("./crawPath", false);
/**设置爬取的网站,其中后面的参数指定该URL的类型
*如:爬取一个网站的所有公告,最开始爬取的便是公告列表的第一页
*第一页中有公告详情URL,就需要将详情URL添加到爬取任务中
*就可以通过后面的那个字符串参数来分辨爬取的URL是公告列表还是公告详情页
*/
addSeed(send + noticePage,"noticeList");
//设置线程数
setThreads(2);
}
3、覆写visit方法
visit中有两个参数(Page,CrawlDatums)
Page是爬取过程中,内存中保存网页爬取信息的一个容器,Page只在内存中存 放,用于保存一些网页信息,方便用户进行自定义网页解析之类的操作
CrawlDatums是存放爬取任务的集合,底层实现是LinkedList
@Override
public void visit(Page page, CrawlDatums crawlDatums) {
//区分爬取的URL是公告列表还是公告详情页
if(page.matchType("noticeList")){
/**
*获取公告列表中的公告详情页的URL,其中select方法中的字符串代表的是
*代码所示的意思是抓取class为info的div下的ul的li的a标签
*/
Elements elements = page.select("div.info>ul>li>a");
if(elements != null){
for(Element element : elements){
String href = element.attributes().get("href");
//将详情页URL添加到crawlDatums中
crawlDatums.add(href,"noticeDetails");
}
//当本页还有数据就将下一页列表的URL添加进去
crawlDatums.add(send + ++noticePage,"noticeList");
}
}else if(page.matchType("noticeDetails")) {
//如果是详情页就将数据存入数据库中
try {
Notice notice = new Notice();
notice.setId(UUID.randomUUID().toString());
//获取公告发布时间,若公告时间比上次爬取的时间早,return结束该URL爬取
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String uploadDate = page.select("div.cont-info>p").first().text().split(":")[1];
if(lastDate != null) {
if (sdf.parse(uploadDate).before(lastDate)) {
return;
}
}
notice.setUploadDate(sdf.parse(uploadDate));
String title = page.select("div.cont-info>h1").first().text();
notice.setTitle(title);
String url = page.url();
notice.setUrl(url);
String content = page.select("div.cont-info>div.tableBox").first().text();
notice.setContent(content);
noticeCrawlerDao.insert(notice);
} catch (ParseException e) {
log.error("", e);
}
}
}
4、写main方法测试爬取程序
//这里的异常其实需要处理,我是为了省事
public static void main(String[] args) throws Exception {
ProcurementCrawler crawler = new ProcurementCrawler();
/**
*lastDate是我在爬取程序中定义的一个属性并写了set方法
*protected static Date lastDate = null;
*/
crawler.setLastDate(null);
/**
*20代表爬取深度
*1爬取深度代表爬取完第一个爬取URL本身和你添加在CrawlDatums的爬取任务
*下一个爬取深度就是爬取你上一深度中添加在CrawlDatums中的URL
*类似树的层次遍历
*/
crawler.start(20);
}
二、编写定时任务
可以写在@Service或@Component注解的类中
1、在类上添加@EnableScheduling注解
这个注解主要作用是向SchedulingConfiguration注册
2、定时任务启动爬取程序
//在每天凌晨两点钟定时爬取公告数据
@Scheduled(cron = "0 0 2 * * ?")
public void noticeCrawler() throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//从数据库中查上次爬取的时间
String timeStr = dao.getLastDate();
Date lastDate = dateFormat.parse(timeStr.substring(0,18) + "1");
crawler.setLastDate(lastDate);
try {
crawler.start(30);
} catch (Exception e) {
log.error("",e);
}
}
3、@Scheduled定时任务时间设置
主要讲述3种常用的时间设置方式
(1)cron
一个cron表达式有至少6个(也可能7个)由空格分隔的时间元素,项目中大多写在配置文件中,通过@Scheduled(cron="${key}")来获取
依次是:[秒] [分] [小时] [日] [月] [周] [年](可省略年)
*表示所有值
?表示不指定值
,表示多值,如在秒上1,30表示1秒和30秒的时候都触发
-表示区间,如在时上10-12表示10、11、12都会触发
/表示递增,如在时上0/12表示晚上零点和中午十二点触发
(2)fixedDelay
例:@Scheduled(fixedDelay = 5000)
表示上一次执行完成后多久再次执行,单位是毫秒
每个两小时执行一次
@Scheduled(fixedDelay = 2 * 60 * 60 * 1000)
(3)fixedDelayString
与fixedDelay意思相同,只是使用字符串的形式,唯一不同的是支持占位符
如:@Scheduled(fixedDelayString="${time.cron}")
所以大多数使用(1)、(3)两种设置方式
总结
内容并不难主要是再visit方法的覆写上难
更多WebCollector教程链接