JAVA自动爬取CSDN用户数据并文章点赞

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/luo4105/article/details/87166231

爬取用户

某天,我发现我的文章被某个用户点赞了。欣喜之下,把那边文章重新校核更改一遍,接着进入这个点赞我的用户看看,结果发现他近期的博客是一些爬虫的实战。我想,我那篇文章,应该是他做的一个小程序批量加载用户信息并给文章点赞。我觉得这是一个有点意思的事,于是用java实现csdn批量爬取用户名并点赞。

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

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

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

<a class="fans_title" href="https://me.csdn.net/qq_33981796">qq_33981796</a>

匹配正则

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

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

代码

    public Set<String> users = new HashSet<>();
    
    public void getCSDNUsers(String userName) {
        if (users.size() > 30) {
            return;
        }
        Set<String> 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<String, String> 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<String> 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<String> 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<String, String> 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<String> 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,随便打开一篇博客,并点赞,查看浏览器请求日志。

  1. Request URL: https://blog.csdn.net/u014633966/phoenix/article/digg?ArticleId=86526464
  2. Request Method: GET

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

页面代码

<a href="https://blog.csdn.net/luo4105/article/details/86599896" target="_blank">
        <span class="article-type type-1">
            原        </span>
        Could not set parameters for mapping错误与mybatis源码追踪      </a>

实现代码

    public Map<String, String> getFirstArticleId(Set<String> userNames) {
        Map<String, String> 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在哪里返回的。

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

  1. 登陆接口url是https://passport.csdn.net/v1/register/pc/login/doLogin,method是POST,content-type是application/json;charset=UTF-8
  2. 参数和密码通过一个json字符串上传,json格式是{loginType: "1", pwdOrVerifyCode: "xx", userIdentification: "xxx",...},关键参数是pwdOrVerifyCode(密码)和userIdentification(账号)。
  3. 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<String, String> 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<String, List<String>> repHeaders = HttpUtil.doPostJSON(url, headers,
                    jsonObject.toJSONString());
            List<String> 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自动爬取用户并给文章点赞的实现方式和过程。

整个项目源代码已经上传github,欢迎大家下载使用并改进,https://github.com/programluo/csdn-tool

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券