Java 爬蟲如何爬取需要登入的網站
這是 Java 網路爬蟲系列博文的第二篇,在上一篇 Java 網路爬蟲新手入門詳解 中,我們簡單的學習了一下如何利用 Java 進行網路爬蟲。在這一篇中我們將簡單的聊一聊在網路爬蟲時,遇到需要登入的網站,我們該怎麼辦?
在做爬蟲時,遇到需要登陸的問題也比較常見,比如寫指令碼搶票之類的,但凡需要個人資訊的都需要登陸,對於這類問題主要有兩種解決方式:一種方式是手動設定 cookie ,就是先在網站上面登入,複製登陸後的 cookies ,在爬蟲程式中手動設定 HTTP 請求中的 Cookie 屬性,這種方式適用於採集頻次不高、採集週期短,因為 cookie 會失效,如果長期採集的話就需要頻繁設定 cookie,這不是一種可行的辦法,第二種方式就是使用程式模擬登陸,通過模擬登陸獲取到 cookies,這種方式適用於長期採集該網站,因為每次採集都會先登陸,這樣就不需要擔心 cookie 過期的問題。
為了能讓大家更好的理解這兩種方式的運用,我以獲取豆瓣個人主頁暱稱為例,分別用這兩種方式來獲取需要登陸後才能看到的資訊。獲取資訊如下圖所示:
獲取圖片中的缺心眼那叫單純,這個資訊顯然是需要登陸後才能看到的,這就符合我們的主題啦。接下來分別用上面兩種辦法來解決這個問題。
手動設定 cookie
手動設定 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 方法
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 要複雜很多,對於其他有驗證碼的登陸,我就不在這裡介紹了,第一是我在這方面也沒什麼經驗,第二是這個實現起來比較複雜,會涉及到一些演算法和一些輔助工具的使用,有興趣的朋友可以參考崔慶才老師的部落格研究研究。模擬登陸寫起來雖然比較複雜,但是隻要你編寫好之後,你就能夠一勞永逸,如果你需要長期採集需要登陸的資訊,這個還是值得你的做的。
除了使用 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,這種方式適用於短暫性採集或者一次性採集,成本較低。另一種方式是模擬登陸的方式,這種方式適用於長期採集的網站,因為模擬登陸的代價還是蠻高的,特別是一些變態的驗證碼,好處就是能夠讓你一勞永逸
原始碼:原始碼
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援碼農教程。