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 爬蟲時遇到登錄問題相關知識分享,但願對你有所幫助,下一篇是關於爬蟲是遇到數據異步加載的問題。若是你對爬蟲感興趣,不妨關注一波,相互學習,相互進步

源代碼:源代碼

文章不足之處,望你們多多指點,共同窗習,共同進步

最後

打個小廣告,歡迎掃碼關注微信公衆號:「平頭哥的技術博文」,一塊兒進步吧。
平頭哥的技術博文

相關文章
相關標籤/搜索