事情背景
最近帮朋友做一个小项目,里面涉及到中国高校信息的下拉选。就去网上搜索一下相关信息,总结了一下,还是把这些信息控制在自己的手里比较好,就尝试用Jsoup去爬取中国高校信息。
准备工作
1.爬取网址:校园信息库
2.创建一个springboot项目,引入Jsoup的依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
3.数据库表
开始撸代码
因为我把城市信息和高校信息分别存在两张表,所以爬取高校信息分成两步。
1.爬取城市信息。
上代码:
Document doc = Jsoup.connect(url).get();
//取出所有的城市,并编号
cityMap = new CollegeSpider().cityInfo(doc,cityMap);
//保存城市信息
citySave(cityMap);
/**
* 爬取城市信息
* @param doc
* @param cityMap
* @return
*/
public Map<String,Integer> cityInfo(Document doc, Map<String,Integer> cityMap){
//获取城市列表元素
Elements cityList = doc.getElementsByClass("ch-select ch-hide").get(0).children();
//跳过第一行
boolean isFirst = true;
for(Node city : cityList){
if(isFirst){
isFirst = false;
continue;
}
Integer cityNum = ((Element) city).attr("value")==null? 10000 : Integer.parseInt(((Element) city).attr("value"));
String cityName = city.childNodes().get(0).outerHtml();
//组装map
cityMap.put(cityName.trim(),cityNum);
}
return cityMap;
}
/**
* 保存城市信息
* @param cityMap
*/
private void citySave(Map<String, Integer> cityMap) {
//map迭代器
Iterator<Map.Entry<String, Integer>> entries = cityMap.entrySet().iterator();
//存库列表
List<CityEntity> citys = new ArrayList<>();
while (entries.hasNext()) {
Map.Entry<String, Integer> entry = entries.next();
citys.add(new CityEntity(entry.getValue(),entry.getKey()));
}
//先删除所有
cityRepository.deleteAll();
//存库
cityRepository.saveAll(citys);
}
2.爬取高校信息
上代码:
//爬取数据(高校信息表格)
Integer start = 0;
while (true){
String paramUrl = url + "?start="+start;
Document docs = Jsoup.connect(paramUrl).get();
Elements collegeList = docs.getElementsByClass("ch-table");
//拿到需要表格的页面元素集合
String html = collegeList.get(0).children().get(0).children().get(1).children().get(0).childNodes().get(0).outerHtml();
if("很抱歉,没有找到您要搜索的数据!".equals(html)){
break;
}else {
Elements collegeElements = collegeList.get(0).children().get(0).children();
colleges = new CollegeSpider().collegeInfo(collegeElements,colleges,cityMap);
}
start += 20;
//休眠一秒
Thread.sleep(1000);
}
//先删除再保存高校数据
collegeRepository.deleteAll();
collegeRepository.saveAll(colleges);
/**
* 爬取高校信息
* @return
*/
public List<CollegeEntity> collegeInfo(Elements collegeElements, List<CollegeEntity> collegList, Map<String,Integer> cityMap){
//遍历表格每一行
boolean isFirst = true;
for(Element element : collegeElements){
//跳过表格第一行
if(isFirst){
isFirst = false;
continue;
}
//从第二行开始遍历
Elements elements = element.getAllElements();
//记列数
int column = 0;
//遍历每一列
CollegeEntity college = new CollegeEntity();
for(Element elem : elements){
if(null == elem.children() || elem.children().size() ==0){
//1.学校名
if(column == 0){
college.setName(elem.childNodes().get(0).outerHtml().trim());
}else if(column == 1){
college.setCityId(cityMap.get(elem.childNodes().get(0).outerHtml().trim()));
}else if(column == 4){//本科
college.setRecord(elem.childNodes().get(0).outerHtml());
}else if(column == 5){//985|211
if(elem.childNodes().size() != 0){
college.setFeatures(elem.childNodes().get(0).outerHtml());
}
}else if(column == 6){//|211
if(StringUtils.isBlank(college.getFeatures())){
college.setFeatures(elem.childNodes().get(0).outerHtml());
}else {
college.setFeatures(college.getFeatures() +"|"+elem.childNodes().get(0).outerHtml());
}
}else if(column == 7){//是否是研究生院校
if(elem.tag().getName().contains("i")){
college.setInstitute(true);
}else {
college.setInstitute(false);
}
}
column ++;
}
}
//保存高校列表
collegList.add(college);
log.info(college.toString());
}
return collegList;
}
运行结果:
至此,我的目的已经达到,接下来写一个高校下拉选接口即可。
总结:
1.Jsoup适合静态网页信息的抓取。
2.难点在于找到想要的信息的具体节点。