java爬取页面数据报错401,JAVA自动爬取CSDN用户数据并文章点赞

爬取用户

某天,我发现我的文章被某个用户点赞了。欣喜之下,把那边文章重新校核更改一遍,接着进入这个点赞我的用户看看,结果发现他近期的博客是一些爬虫的实战。我想,我那篇文章,应该是他做的一个小程序批量加载用户信息并给文章点赞。我觉得这是一个有点意思的事,于是用java实现csdn批量爬取用户名并点赞。其中批量爬取用户名的思路借鉴这位大神的这篇博文GO+Selenium批量关注CSDN 1 (如何获取100万用户名, ES去重)。

题外话到此为止,先说说怎么获得csdn的用户名的,他的方法是通过用户的粉丝和关注两个列表,最多可以获得12个用户名,再根据这12个用户名,继续进入每个用户的主页,获得该用户的粉丝和关注列表的用户数据,这样无限循环往复,直到找到很多很多的用户名。

1.通过https://me.csdn.net/用户名获得粉丝和关注列表。

fb1f5875b77f2c763b5d1a153b1685fa.png

2.查看页面源码,用正则匹配

qq_33981796

匹配正则

a class="fans_title" href="https://me.csdn.net/(.+)"

3.获得匹配的用户名,再次进入https://me.csdn.net/用户名页面,重复1,2的操作

代码

public Set users = new HashSet<>();

public void getCSDNUsers(String userName) {

if (users.size() > 30) {

return;

}

Set userList = parseFans(mePage(userName));

if (userList != null && userList.size() > 0) {

synchronized (users) {

users.addAll(userList);

}

userList.stream().forEach(user -> getCSDNUsers(user, token));

}

}

public String mePage(String username) {

String follerUrl = "https://me.csdn.net/" + username;

try {

Map headers = new HashMap<>();

headers.put("cook", "UserName=luo4105; UserInfo=de5e709f7cd84e2d87648d45e6288db0; " +

"UserToken=de5e709f7cd84e2d87648d45e6288db0;");

InputStream inputStream = HttpUtil.doGet(follerUrl, headers);

String response = StreamUtil.inputStreamToString(inputStream, "UTF-8");

return response;

} catch (IOException e) {

e.printStackTrace();

}

return null;

}

public Set parseFans(String pageContext) {

if (pageContext == null || pageContext.length() == 0) {

return new HashSet<>();

}

String fansRegex = "a class=\"fans_title\" href=\"https://me.csdn.net/(.+)\"";

Pattern pattern = Pattern.compile(fansRegex);

Matcher matcher = pattern.matcher(pageContext);

Set fanses = new HashSet<>();

while (matcher.find()) {

fanses.add(matcher.group(1));

}

return fanses;

}

这里只拉30个用户的数据,我也没有想好怎么截止这个任务。附上自己写的httpUtil和StreamUtil的访问网页的方法并输出string的方法。

//httpUtil.doGet方法

public static InputStream doGet(String urlstr, Map headers) throws IOException {

URL url = new URL(urlstr);

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 " +

"(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36");

conn.setRequestProperty("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp," +

"image/apng,*/*;q=0" +

".8");

if (headers != null) {

Set keys = headers.keySet();

keys.stream().forEach(key -> {

conn.setRequestProperty(key, headers.get(key));

});

}

Random random = new Random();

String ip =

(random.nextInt(100) + 100) + "." + (random.nextInt(100) + 100) + "." + (random.nextInt(100) + 100) + "." + (random.nextInt(100) + 100);

conn.setRequestProperty("x-forwarded-for", ip);

InputStream inputStream = conn.getInputStream();

return inputStream;

}

public class StreamUtil {

public static String inputStreamToString(InputStream is, String charset) throws IOException {

byte[] bytes = new byte[1024];

int byteLength = 0;

StringBuffer sb = new StringBuffer();

while ((byteLength = is.read(bytes)) != -1) {

sb.append(new String(bytes, 0, byteLength, charset));

}

return sb.toString();

}

}

博文点赞

分析一下博文点赞的API,随便打开一篇博客,并点赞,查看浏览器请求日志。

cookie: UserName=luo4105; UserToken=xxxxxxxxxxxx;

只要拿到ArticleId,自己的UserToken,就可以拼接请求点赞博客了。

大致思路是这样的,打开https://me.csdn.net/用户名博客主页,解析文章列表便可获得文章id。跟踪登陆API,查看请求参数和响应中的cook的插入,解析出token。有了这些参数,就可以拼点赞请求了。

获得用户下的ArticleId

为了防止一下子把羊毛褥完了,我这里拿的是每个用户下的第一篇aritcleId,思路是这样的。

1.进入该用户的默认博客列表页面

这里通过https://blog.csdn.net/用户名/article/list/1进入博客列表

2.通过正则匹配获得第一篇文章链接中的ArticleId和userName

页面代码

Could not set parameters for mapping错误与mybatis源码追踪

实现代码

public Map getFirstArticleId(Set userNames) {

Map articleIdMap = new HashMap<>();

String blogUrlTemplate = "https://blog.csdn.net/%s/article/list/1";

userNames.stream().forEach(userName -> {

String blogUrl = String.format(blogUrlTemplate, userName);

try {

InputStream inputStream = HttpUtil.doGet(blogUrl);

String content = StreamUtil.inputStreamToString(inputStream, "UTF-8");

String articleIdRegex = "https://blog.csdn.net/" + userName + "/article/details/([0-9]{8})";

Pattern pattern = Pattern.compile(articleIdRegex);

Matcher matcher = pattern.matcher(content);

while (matcher.find()) {

articleIdMap.put(userName, matcher.group(1));

break;

}

} catch (IOException e) {

System.out.println("http get failed");

e.printStackTrace();

}

});

return articleIdMap;

}

模拟登陆获得token

使用浏览器查看csdn登陆请求,分析request和response,找到接口url,请求格式,用户名和密码在哪里传的,token在哪里返回的。

bd9c5fd543dfc63aed5aa22a2f51a3d8.png

61b20a42609c08201f7901031c1032ce.png

通过查看csdn登陆请求可知道

登陆接口url是https://passport.csdn.net/v1/register/pc/login/doLogin,method是POST,content-type是application/json;charset=UTF-8

参数和密码通过一个json字符串上传,json格式是{loginType: "1", pwdOrVerifyCode: "xx", userIdentification: "xxx",...},关键参数是pwdOrVerifyCode(密码)和userIdentification(账号)。

token是通过response的header存入cookie,可通过拿response的header获得登陆token。

实现代码

/**

* 模拟登陆,并从cookie中拿出token和用户名

*

* @param username

* @param password

*/

public UserToken login(String username, String password) {

String url = "https://passport.csdn.net/v1/register/pc/login/doLogin";

Map headers = new HashMap<>();

headers.put("Content-Type", "application/json");

headers.put("accept", "application/json;charset=UTF-8");

JSONObject jsonObject = new JSONObject();

jsonObject.put("loginType", "1");

jsonObject.put("pwdOrVerifyCode", password);

jsonObject.put("userIdentification", username);

UserToken userToken = new UserToken();

try {

Map> repHeaders = HttpUtil.doPostJSON(url, headers,

jsonObject.toJSONString());

List setCookies = repHeaders.get("Set-Cookie");

if (setCookies != null && setCookies.size() > 0) {

setCookies.stream().forEach(cook -> {

if (cook.contains("UserToken=")) {

String userTokenRegex = "UserToken=(.*); Max";

Pattern pattern = Pattern.compile(userTokenRegex);

Matcher matcher = pattern.matcher(cook);

while (matcher.find()) {

userToken.setToken(matcher.group(1));

}

}

if (cook.contains("UserName=")) {

String userTokenRegex = "UserName=(.*); Max";

Pattern pattern = Pattern.compile(userTokenRegex);

Matcher matcher = pattern.matcher(cook);

while (matcher.find()) {

userToken.setUserName(matcher.group(1));

}

}

});

}

} catch (IOException e) {

e.printStackTrace();

}

return userToken;

}

@Data

public static class UserToken {

private String token;

private String userName;

}

以上,就是csdn自动爬取用户并给文章点赞的实现方式和过程。

本文同步分享在 博客“逝兮诚”(CSDN)。

如有侵权,请联系 support@oschina.cn 删除。

本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值