Java 爬虫遇到须要登陆的网站,该怎么办?

这是 Java 网络爬虫系列博文的第二篇,在上一篇 Java 网络爬虫,就是这么的简单 中,咱们简单的学习了一下如何利用 Java 进行网络爬虫。在这一篇中咱们将简单的聊一聊在网络爬虫时,遇到须要登陆的网站,咱们该怎么办?html

在作爬虫时,遇到须要登录的问题也比较常见,好比写脚本抢票之类的,但凡须要我的信息的都须要登录,对于这类问题主要有两种解决方式:一种方式是手动设置 cookie ,就是先在网站上面登陆,复制登录后的 cookies ,在爬虫程序中手动设置 HTTP 请求中的 Cookie 属性,这种方式适用于采集频次不高、采集周期短,由于 cookie 会失效,若是长期采集的话就须要频繁设置 cookie,这不是一种可行的办法,第二种方式就是使用程序模拟登录,经过模拟登录获取到 cookies,这种方式适用于长期采集该网站,由于每次采集都会先登录,这样就不须要担忧 cookie 过时的问题。java

为了能让你们更好的理解这两种方式的运用,我以获取豆瓣我的主页昵称为例,分别用这两种方式来获取须要登录后才能看到的信息。获取信息以下图所示:git


获取图片中的缺心眼那叫单纯,这个信息显然是须要登录后才能看到的,这就符合咱们的主题啦。接下来分别用上面两种办法来解决这个问题。github

手动设置 cookie 的方式,这种方式比较简单,咱们只须要在豆瓣网上登录,登录成功后就能够获取到带有用户信息的cookie,豆瓣网登陆连接:https://accounts.douban.com/passport/login。以下图所示:

图中的这个 cookie 就携带了用户信息,咱们只须要在请求时携带这个 cookie 就能够查看到须要登录后才能查看到的信息。咱们用 Jsoup 来模拟一下手动设置 cookie 方式,具体代码以下:算法

/**
 * 手动设置 cookies
 * 先从网站上登陆,而后查看 request headers 里面的 cookies
 * @param url
 * @throws IOException
 */
public void setCookies(String url) throws IOException {

    Document document = Jsoup.connect(url)
            // 手动设置cookies
            .header("Cookie", "your cookies")
            .get();
    //
    if (document != null) {
        // 获取豆瓣昵称节点
        Element element = document.select(".info h1").first();
        if (element == null) {
            System.out.println("没有找到 .info h1 标签");
            return;
        }
        // 取出豆瓣节点昵称
        String userName = element.ownText();
        System.out.println("豆瓣个人网名为:" + userName);
    } else {
        System.out.println("出错啦!!!!!");
    }
}

从代码中能够看出跟不须要登录的网站没什么区别,只是多了一个.header("Cookie", "your cookies"),咱们把浏览器中的 cookie 复制到这里便可,编写 main 方法json

public static void main(String[] args) throws Exception {
    // 我的中心url
    String user_info_url = "https://www.douban.com/people/150968577/";
    new CrawleLogin().setCookies(user_info_url);

运行 main 获得结果以下:

能够看出咱们成功获取到了缺心眼那叫单纯,这说明咱们设置的 cookie 是有效的,成功的拿到了须要登录的数据。这种方式是真的比较简单,惟一的不足就是须要频繁的更换 cookie,由于 cookie 会失效,这让你使用起来就不是很爽啦。浏览器

模拟登录方式

模拟登录的方式能够解决手动设置 cookie 方式的不足之处,但同时也引入了比较复杂的问题,如今的验证码形形色色、五花八门,不少都富有挑战性,好比在一堆图片中操做某类图片,这个仍是很是有难度,不是随便就可以编写出来。因此对于使用哪一种方式这个就须要开发者本身去衡量利弊啦。今天咱们用到的豆瓣网,在登录的时候就没有验证码,对于这种没有验证码的仍是比较简单的,关于模拟登录方式最重要的就是找到真正的登录请求、登录须要的参数。 这个咱们就只能取巧了,咱们先在登录界面输入错误的帐号密码,这样页面将不会跳转,因此咱们就可以垂手可得的找到登录请求。我来演示一下豆瓣网登录查找登录连接,咱们在登录界面输入错误的用户名和密码,点击登录后,在 network 查看发起的请求连接,以下图所示:

从 network 中咱们能够查看到豆瓣网的登录连接为https://accounts.douban.com/j/mobile/login/basic,须要的参数有五个,具体参数查看图中的 Form Data,有了这些以后,咱们就可以构造请求模拟登录啦。登录后进行后续操做,接下来咱们就用 Jsoup 来模拟登录到获取豆瓣主页昵称,具体代码以下:缓存

/**
 * Jsoup 模拟登陆豆瓣 访问我的中心
 * 在豆瓣登陆时先输入一个错误的帐号密码,查看到登陆所须要的参数
 * 先构造登陆请求参数,成功后获取到cookies
 * 设置request cookies,再次请求
 * @param loginUrl 登陆url
 * @param userInfoUrl 我的中心url
 * @throws IOException
 */
public void jsoupLogin(String loginUrl,String userInfoUrl)  throws IOException {

    // 构造登录参数
    Map<String,String> data = new HashMap<>();
    data.put("name","your_account");
    data.put("password","your_password");
    data.put("remember","false");
    data.put("ticket","");
    data.put("ck","");
    Connection.Response login = Jsoup.connect(loginUrl)
            .ignoreContentType(true) // 忽略类型验证
            .followRedirects(false) // 禁止重定向
            .postDataCharset("utf-8")
            .header("Upgrade-Insecure-Requests","1")
            .header("Accept","application/json")
            .header("Content-Type","application/x-www-form-urlencoded")
            .header("X-Requested-With","XMLHttpRequest")
            .header("User-Agent","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36")
            .data(data)
            .method(Connection.Method.POST)
            .execute();
    login.charset("UTF-8");
    // login 中已经获取到登陆成功以后的cookies
    // 构造访问我的中心的请求
    Document document = Jsoup.connect(userInfoUrl)
            // 取出login对象里面的cookies
            .cookies(login.cookies())
            .get();
    if (document != null) {
        Element element = document.select(".info h1").first();
        if (element == null) {
            System.out.println("没有找到 .info h1 标签");
            return;
        }
        String userName = element.ownText();
        System.out.println("豆瓣个人网名为:" + userName);
    } else {
        System.out.println("出错啦!!!!!");
    }
}

这段代码分两段,前一段是模拟登录,后一段是解析豆瓣主页,在这段代码中发起了两次请求,第一次请求是模拟登录获取到 cookie,第二次请求时携带第一次模拟登录后获取的cookie,这样也能够访问须要登录的页面,修改 main 方法微信

public static void main(String[] args) throws Exception {
    // 我的中心url
    String user_info_url = "https://www.douban.com/people/150968577/";

    // 登录接口
    String login_url = "https://accounts.douban.com/j/mobile/login/basic";

    // new CrawleLogin().setCookies(user_info_url);
    new CrawleLogin().jsoupLogin(login_url,user_info_url);
}

运行 main 方法,获得以下结果:

模拟登录的方式也成功的获取到了网名缺心眼那叫单纯,虽然这已是最简单的模拟登录啦,从代码量上就能够看出它比设置 cookie 要复杂不少,对于其余有验证码的登录,我就不在这里介绍了,第一是我在这方面也没什么经验,第二是这个实现起来比较复杂,会涉及到一些算法和一些辅助工具的使用,有兴趣的朋友能够参考崔庆才老师的博客研究研究。模拟登录写起来虽然比较复杂,可是只要你编写好以后,你就可以一劳永逸,若是你须要长期采集须要登录的信息,这个仍是值得你的作的。cookie

除了使用 jsoup 模拟登录外,咱们还可使用 httpclient 模拟登录,httpclient 模拟登录没有 Jsoup 那么复杂,由于 httpclient 可以像浏览器同样保存 session 会话,这样登录以后就保存下了 cookie ,在同一个 httpclient 内请求就会带上 cookie 啦。httpclient 模拟登录代码以下:

/**
 * httpclient 的方式模拟登陆豆瓣
 * httpclient 跟jsoup差很少,不一样的地方在于 httpclient 有session的概念
 * 在同一个httpclient 内不须要设置cookies ,会默认缓存下来
 * @param loginUrl
 * @param userInfoUrl
 */
public void httpClientLogin(String loginUrl,String userInfoUrl) throws Exception{

    CloseableHttpClient httpclient = HttpClients.createDefault();
    HttpUriRequest login = RequestBuilder.post()
            .setUri(new URI(loginUrl))// 登录url
            .setHeader("Upgrade-Insecure-Requests","1")
            .setHeader("Accept","application/json")
            .setHeader("Content-Type","application/x-www-form-urlencoded")
            .setHeader("X-Requested-With","XMLHttpRequest")
            .setHeader("User-Agent","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36")
            // 设置帐号信息
            .addParameter("name","your_account")
            .addParameter("password","your_password")
            .addParameter("remember","false")
            .addParameter("ticket","")
            .addParameter("ck","")
            .build();
    // 模拟登录
    CloseableHttpResponse response = httpclient.execute(login);
    if (response.getStatusLine().getStatusCode() == 200){
        // 构造访问我的中心请求
        HttpGet httpGet = new HttpGet(userInfoUrl);
        CloseableHttpResponse user_response = httpclient.execute(httpGet);
        HttpEntity entity = user_response.getEntity();
        //
        String body = EntityUtils.toString(entity, "utf-8");

        // 偷个懒,直接判断 缺心眼那叫单纯 是否存在字符串中
        System.out.println("缺心眼那叫单纯是否查找到?"+(body.contains("缺心眼那叫单纯")));
    }else {
        System.out.println("httpclient 模拟登陆豆瓣失败了!!!!");
    }
}

运行这段代码,返回的结果也是 true。

有关 Java 爬虫遇到登录问题就聊得差很少啦,来总结一下:对于爬虫遇到登录问题有两种解决办法,一种是手动设置cookie,这种方式适用于短暂性采集或者一次性采集,成本较低。另外一种方式是模拟登录的方式,这种方式适用于长期采集的网站,由于模拟登录的代价仍是蛮高的,特别是一些变态的验证码,好处就是可以让你一劳永逸

以上就是 Java 爬虫时遇到登录问题相关知识分享,但愿对你有所帮助,下一篇是关于爬虫是遇到数据异步加载的问题。若是你对爬虫感兴趣,不妨关注一波,相互学习,相互进步

源代码:源代码

文章不足之处,望你们多多指点,共同窗习,共同进步

最后

打个小广告,欢迎扫码关注微信公众号:「平头哥的技术博文」,一块儿进步吧。
平头哥的技术博文