简单Java小爬虫--获取博主信息

为什么会写这篇博客,是因为我最近在写博客,对自己的博客浏览量比较好奇,总是会登上来看一下,正好我对爬虫感兴趣,所以就想着写一个爬虫来直接获取一下信息。

基本思路

这个爬虫的思路很简单,就是通过发起 HTTP 请求获取博主的我的博客的信息,来获取Ta的一些基本信息。我主要获取下面这幅图中,圆圈内的信息。
点击我的博客,就能看到这个页面,最上面是需要爬取的链接。

在这里插入图片描述

使用的jar包及遇到的问题

使用的jar包

我使用了 HttpClient 和 Jsoup 这两个工具,采用 maven 创建工程,下面是jar包的坐标。

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
	<dependency>
	    <groupId>org.apache.httpcomponents</groupId>
	    <artifactId>httpclient</artifactId>
	    <version>4.5.6</version>
	</dependency>
	
	<dependency>
     	<groupId>org.jsoup</groupId>
     	<artifactId>jsoup</artifactId>
     	<version>1.11.3</version>
     </dependency>

遇到的问题

我发现直接发起请求会报一个警告日志:Invalid cookie header。但是我不知道是为什么(但是它不影响使用),根据网络上的方法,需要设置一个东西即可。
在这里插入图片描述
解决方法

//下面这两句,是因为总是报一个 Invalid cookie header,
//然后我在网上找到的解决方法。(去掉的话,不影响使用)。
RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
get.setConfig(defaultConfig);

代码实现

Spider 类

功能:获取数据,解析数据。

主要有两个方法:getRawData() 和 getInfo()。
getRawData 是获取相应链接的 html 页面数据,然后由 getInfo 负责解析数据,从中获取相应的信息。

package com.spider;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class InfoSpider {
	private String url;
	private CloseableHttpClient httpClient;
	
	public InfoSpider(String url) {
		this.url = url;
		this.httpClient = HttpClients.createDefault();
	}
	
	/**
	 * 从url中获取原始的网页数据
	 * @throws IOException 
	 * @throws ClientProtocolException 
	 * */
	public String getRawData() throws ClientProtocolException, IOException {
		HttpGet get = new HttpGet(url);
		//下面这两句,是因为总是报一个 Invalid cookie header,然后我在网上找到的解决方法。(去掉的话,不影响使用)。
		RequestConfig defaultConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
		get.setConfig(defaultConfig);
		//因为是初学,而且我这里只是请求一次数据即可,这里就简单设置一下 UA
		get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36");
		HttpEntity entity = null;
		String data = null;
		try (CloseableHttpResponse response = httpClient.execute(get)) {
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) {
				entity = response.getEntity();
				if (entity != null) {
					data = EntityUtils.toString(entity, "UTF-8");
				}
			}
		}
		return data;
	}
	
	/**
	 * 从原始的网页数据中获取个人相关信息。
	 * @throws IOException 
	 * */
	public Bloger getInfo(String data) throws IOException {
		Bloger bloger = null;
		Document doc = Jsoup.parse(data, "UtF-8");
		
		//获取描述用户信息的 div
		Element element = doc.getElementById("asideProfile");
		
		//获取用户url和昵称
		Element infoA = doc.select("a#uid").first();
		String homePageUrl = infoA.attr("href");
		String nickName = infoA.text();
	
		//获取用户的描述信息,如积分、等级、排名之类的信息。
		Elements descriptions1 = element.getElementsByAttributeValue("class", "text-center");
		Elements descriptions2 = element.select("div.grade-box.clearfix > dl");
		
		//如果不想看下面这个流的操作,可以直接输出 文本信息 (但是,这里无法获取等级信息)
		System.out.println(descriptions1.text());
		System.out.println(descriptions2.text());
		System.out.println("========================================");
		
		//使用流,将这些描述信息转成 map 集合
		Map<String, String> desMap = Stream.of(descriptions1, descriptions2)
						.flatMap(des->des.stream())
						.collect(Collectors.toMap(description->{
							int position = -1;
							String key = description.getElementsByTag("dt").first().text();
							position = key.lastIndexOf(":");
							return -1 == position ? key : key.substring(0, position);
						}, 
						description->{
							Element level = description
									.getElementsByAttributeValue("href", "https://blog.csdn.net/home/help.html#level")
									.first();
							if (level != null) {
								return level.attr("title").split(",")[0];
							} else {
								return description.getElementsByTag("dd").first().text();
							}
						}));
		
		
		//获取所有的勋章信息
		Elements medals = element.select("div.icon-badge");
		List<String> medalList = medals.stream()
			.map(medal->medal.attr("title"))
			.collect(Collectors.toList());
		
		//为 Bloger 对象赋值,并返回该对象
		bloger = new Bloger();
		bloger.setUrl(homePageUrl);
		bloger.setNickName(nickName);
		bloger.setDesMap(desMap);
		bloger.setMedalList(medalList);
		return bloger;
	}
}

这里说明一下:

//使用流,将这些描述信息转成 map 集合
Map<String, String> desMap = Stream.of(descriptions1, descriptions2)
				.flatMap(des->des.stream())
				.collect(Collectors.toMap(description->{
					int position = -1;
					String key = description.getElementsByTag("dt").first().text();
					position = key.lastIndexOf(":");
					return -1 == position ? key : key.substring(0, position);
				}, 
				description->{
					Element level = description
							.getElementsByAttributeValue("href", "https://blog.csdn.net/home/help.html#level")
							.first();
					if (level != null) {
						return level.attr("title").split(",")[0];
					} else {
						return description.getElementsByTag("dd").first().text();
					}
				}));

这个方法是将 d1 标签中的数据取出来,d1 标签含有一个 dt 和 dd 标签,将dt表示的数据作为 key, dd标签的数据作为 value。然后由于有一部分标签中的 key 带有 “:”,所以我会判断一下,把 “:” 去掉。value 也是同样的,对于等级它不是直接的文本数据,所以我也处理了一下。虽然这个方法比较麻烦,但是也能用(偷懒一下。哈哈)。如果你不想使用的话,可以直接使用我上面的语句将文本打印出来。

在这里插入图片描述



Bloger 类

package com.spider;

import java.util.List;
import java.util.Map;

public class Bloger {
	private String url;    //主页
	private String nickName; //昵称
	private Map<String, String> desMap;  //描述
	private List<String> medalList;  //勋章
	public String getUrl() {
		return url;
	}
	public void setUrl(String url) {
		this.url = url;
	}
	public String getNickName() {
		return nickName;
	}
	public void setNickName(String nickName) {
		this.nickName = nickName;
	}
	public Map<String, String> getDesMap() {
		return desMap;
	}
	public void setDesMap(Map<String, String> desMap) {
		this.desMap = desMap;
	}
	public List<String> getMedalList() {
		return medalList;
	}
	public void setMedalList(List<String> medalList) {
		this.medalList = medalList;
	}
	
	@Override
	public String toString() {
		return "Bloger : \n url=" + url + ",\n nickName=" + nickName + ",\n desMap=" + desMap + ",\n medalList=" + medalList
				+ "]";
	}
}



Main 类

package com.spider;

import java.io.IOException;

import org.apache.http.client.ClientProtocolException;

public class Main {
	public static void main(String[] args) throws ClientProtocolException, IOException {
		String url = "https://blog.csdn.net/qq_40734247";
		InfoSpider spider = new InfoSpider(url);
		String rawData = spider.getRawData();
		Bloger bloger = spider.getInfo(rawData);
		System.out.println(bloger);
	}
}



运行结果

分隔线下面是 Bloger 对象的 toString 方法,上面是直接输出的 html 中的文本信息,因为我中间使用了流来处理 desMap,但是感觉代码失去可读性了,可能是我为了使用一个操作直接处理了吧,反而变麻烦了。不过它也不影响我使用,如果你不想获取到对象,就可以直接使用 text() 方法,获取文本数据,也是非常不错的。
在这里插入图片描述



总结

学习爬虫也不知道去搞什么,就自己来写一些简单的小工具了。反正也是有一点效果吧。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值