手把手Java爬虫教学 - 4. 项目1 - htmlunit 页面爬取数据

一、功能详解

我们先来规划一下程序。这里我是想每隔一段时间,自动去网站抓取一波数据。

在抓取的过程中可能会抓取到重复的博客,这里我有两种想法:

1. 如果碰到相同博客,更新博客;如果不相同,就去新增;

2. 如果碰到一个重复的,就认为后面的都是重复的,直接停止任务;

经过琢磨,第一种方式绝对不可取,因为我们抓取的上限是 200 页,这要是每次都抓 200 也数据,那不得疯了;所以我们使用第二种;

废话不多说,直接上代码~

二、代码实现

这里我们用到了定时任务,因为 Spring 自己就带了这个东西,所以我也就没有引用 jdk 或者是 Quartz。当然,这里你会那种定时任务就用那种。

1. 启动对定时任务的支持

我们在启动类上增加注解:@EnableScheduling,让 Spring 启动对计划任务的支持。

2. 创建定时任务

类名:CrawlCnblogTask

@Component
public class CrawlCnblogTask {

	private Logger logger = LoggerFactory.getLogger(CrawlCnblogTask.class);
	
	@Scheduled(cron = "0 0/20 * * * ?")
	public void task() {
		
	}
}

这里我们弄一个 task 任务,然后通过注解:@Scheduled 设置间隔时间,我这里设置的是每 20 分钟去定时循环一次,要是看不懂 cron,可以去找一下我的 SpringBoot 相关博客学习一下。

3. 配置要抓取的网页

这里我们直接将我们要爬的页面 url 配置到 yml 文件中

...之前的 yml 配置文件

# 要爬行的页面
crawl:
  cnblogs: https://xxx/xxx/x

因为我们一个 task 任务只爬一个页面,所以我这里直接用的是网页的域名作为配置名。

然后这里的域名是不对的,如果需要知道具体的域名,自己去 clone 代码(gitee:https://gitee.com/soul-sys/lemon1234_scraper

4. 修改 CrawlCnblogTask 类

我们将刚刚配置的 crawl.cnblogs 配置到类里面

@Value("${crawl.cnblogs}")
private String taskUrl;

5. 域名可用性检测

当然,这个域名虽然配置进去了,但是我们为了安全起见,增加一个小功能,就是对 url 检测,看当前 url 能否可以请求的通。

public static boolean checkUrl(String url) {
		CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build();
		
		HttpGet httpGet = new HttpGet(url);
		
		try {
			CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(httpGet);
			
			if(closeableHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				return true;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				closeableHttpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return false;
	}

很简单哈,如果能请求的通,就返回 true,请求不通,就返回 false。

6. 创建 htmlunit 工具类

从现在开始我们就要用到 htmlunit 这个东西了,我个人建议大家去看一下它官网上 Get Start,连接地址:HtmlUnit – Getting Started with HtmlUnit

我们根据它官网的提示,htmlunit 想要去爬页面数据,首先是需要用到 WebClient,所以这里我们弄个简单的工具类,用来管理它。

public class HtmlunitUtil {

	public static WebClient getWebClient() {
		WebClient webClient = new WebClient(BrowserVersion.CHROME);
		
		webClient.getOptions().setCssEnabled(false);
		webClient.getOptions().setJavaScriptEnabled(false);
		
		webClient.waitForBackgroundJavaScript(2000L);
		
		webClient.getOptions().setTimeout(5000);
		
		webClient.getOptions().setUseInsecureSSL(true);
		
		return webClient;
	}
	
	public static void closeWebClient(WebClient webClient) {
		if(webClient != null) {
			webClient.close();
		}
	}
}

当然,上面除去实例化 WebClient 之外,还给 WebClient 设置了很多属性,具体是什么意思,可以去看 API:HtmlUnit 2.60.0 API

7. 将 WebClient 接入到 task 中

CrawlCnblogTask 完整代码如下:

@Component
public class CrawlCnblogTask {

	private Logger logger = LoggerFactory.getLogger(CrawlCnblogTask.class);
	
	@Value("${crawl.cnblogs}")
	private String taskUrl;
	
	@Scheduled(cron = "0 0/20 * * * ?")
	public void task() {
		if(StrUtil.isEmpty(taskUrl)) {
			taskUrl = "https://xxx/xxx/x";
		}
		
		logger.info("========== {} 爬取 cnblog 开始 ======================================== ", DateUtil.now());
		
		try {
			crawlData(taskUrl);
		} catch (Exception e) {
			logger.info("{} 爬取 cnblog 出现异常,错误原因:{}", DateUtil.now(), e.getMessage());
			e.printStackTrace();
		}
	}

	public void crawlData(String taskUrl) throws Exception {
		if(!UrlUtil.checkUrl(taskUrl + "1")) {
			logger.info("{} 爬取 cnblog 失败,错误原因:连接地址错误,无法请求:{}", DateUtil.now(), taskUrl);
			throw new RuntimeException("连接地址错误,无法请求");
		}
		
		WebClient webClient = HtmlunitUtil.getWebClient();
		
		for(int i = 1; i <= 1; i++) {
			HtmlPage page = webClient.getPage(taskUrl + i);
			System.out.println(page.asXml());
		}
		
		HtmlunitUtil.closeWebClient(webClient);
	}
}

这里有几个注意点要说的:

  • taskUrl 是错误的,没有 https://xxx 这种网站,想要获取正确的地址,去 clone 我的项目,项目的 yml 配置文件里有
  • crawlData 方法中为啥 taskUrl 要拼接一个数字 1?这里我们其实在上一个博客就说了,他们的请求是一个 Restful 请求,1 就是代表第一页,2 就是第二页,以此类推
  • 下面 for 循环只循环一次,因为我们只是用来测试,后期我们需要将条件改成 i <= 200

8. 测试

测试的话比较简单,学过 maven 的都会知道 maven 是有专门的测试类的,而且 SpringBoot 也是可以支持测试,所以直接上代码

@SpringBootTest
class Lemon1234ScraperApplicationTests {
	
	@Autowired
	private CrawlCnblogTask syncCnblogTask;

	@Test
	void contextLoads() {
		syncCnblogTask.task();
	}

}

来看看运行结果

可以看到,整个 html 页面就直接拿下了,下一讲我们来说对这段 html 的解析。


这一讲就讲到这里,有问题可以联系我:QQ 2100363119。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

他 他 = new 他()

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值