此次我以爬新浪微博爲例,這個過程太糾結了,參考了好多大神的帖子,不過仍是遺留了不少問題,咱們慢慢來看,但願大神幫於指正,個人方法暫時來講仍是比較挫的php
登錄問題html
爬新浪微博首先要登錄,以前爬的妹紙網站,因爲不用登錄,因此沒這一步,可是爬新浪微博咱們必需要先登陸,可是要涉及到一個問題,那就是驗證碼,驗證碼從我如今百度到的,和本身的理解,感受暫時仍是不能解決的,除非手工輸入,由於自己驗證碼就是防止惡意登錄,防爬蟲的,因此建議想試試的朋友用暫時用不輸入驗證碼的帳號試試(關於驗證碼,期盼大神能夠給些提示)web
下面是demo代碼json
WebClient webClient = new WebClient(); webClient.getOptions().setJavaScriptEnabled(true); webClient.getOptions().setCssEnabled(false); webClient.setAjaxController(new NicelyResynchronizingAjaxController()); webClient.getOptions().setThrowExceptionOnScriptError(false); HtmlPage htmlPage = null; try { htmlPage = webClient.getPage("http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)"); HtmlInput username = (HtmlInput) htmlPage.getElementsByName("username").get(0); HtmlInput password = (HtmlInput) htmlPage.getElementsByName("password").get(0); HtmlInput btn = htmlPage.getFirstByXPath(".//*[@id='vForm']/div[3]/ul/li[6]/div[2]/input"); username.setValueAttribute("****"); password.setValueAttribute("****"); btn.click(); } catch (IOException e) { e.printStackTrace(); }
代碼和以前的差很少太多,多了一些參數設置,經過方法命名就應該能夠大致知道啥含義了,我就不過多解釋了,可是可能有人會問爲何webClient.getPage的url不是www.weibo.com 呢?由於若直接get那個www.weibo.com,獲得的html,debug看,全是一些js代碼,沒有任何登錄模塊而言,可見新浪的登錄模塊多半是腳本畫出來的,因此很差,而這個網站,我開始是參考之前一個大神寫的 http://blog.csdn.net/bob007/article/details/29589059 api
這個網站打開實際上是新浪通行證的登錄頁面
瀏覽器
開始我想爲啥是這個界面,因此後面用httpwatch看了一下經過www.weibo.com登錄的時候整個請求過程dom
能夠看到,在輸入完密碼和帳號以後,咱們post的地址正是這個地址,也就是說,新浪微博的登錄內部應該是這樣的 工具
1. 請求www.weibo.com 獲得登錄頁面,輸入完用戶密碼post
2. 提交請求到新浪通行證,而後點擊登錄,完成登錄操做
學習
咱們日常用的微博帳號,對於新浪而言,實際上是新浪通行證的一種,這樣也就好理解了
咱們再回到htmlunit上,咱們獲取了新浪通行證的登錄頁面後(htmlpage對象),如何獲取到具體的用戶名和密碼的控件呢,這個你們能夠本身debug一下就很清楚了,而後用htmlpage.asXml()這個方法解讀一下這個頁面的各個元素,看一下就知道了
而獲取登錄按鈕的方式也是直接參考以前那個大神裏的作法,用到了api是getFirstByXPath這個方法,這個裏的XPath語法就是w3c標準語法,能夠參考http://www.w3school.com.cn/xpath/學習一下,有了這個語法對於頁面的任何節點,只要稍加分析,基本均可以拿到的
以後就簡單啦,獲取到了用戶名,密碼輸入框,還有提交按鈕,咱們最後三行代碼就是把用戶名和密碼set進去,而後點擊提交按鈕,這就完成了登錄操做,是否是感受到這個代碼邏輯就是和頁面操做同樣嘛,這也體現了模擬瀏覽器的描述了
由於你如今已經登錄了,因此如今你就能夠瀏覽你的主頁和看你關注的人的微博啦
可是你們這裏要注意一個很重要的問題,就是微博的展現方式,你們實際操做也能夠感受的到,展現微博時是這麼一個過程
1. 展現一部分微博
2. 滾動條向下滾動,刷出一部分新的微博
3. 滾動幾回後,最後頁面底部出現分頁控件
4. 點擊下一頁,又展現一部分微博,重複2-4的過程了
因此我開始就遇到這樣的問題,我開始用下面的代碼請求個人主頁,而且打印出了個人微博,惋惜只有15條,由於個人滾動條沒有下拉,因此沒有請求到更多的微博
page3 = webClient.getPage("http://weibo.com/5622296819/profile?topnav=1&wvr=6"); List<HtmlDivision> htmlDivisions = (List<HtmlDivision>) page4.getByXPath("//div[@class='WB_text W_f14']"); for(i = 0; i < htmlDivisions.size(); i++) { System.out.println("第" + (counter++) + "條微博: " + htmlDivisions.get(i).getTextContent().trim()); }
因此我就開始了處處百度如何用htmlunit模擬滾動條滾動,找了好多,模擬這個下拉這個動做基本看無法搞(你們可能夠看看這個可憐的兄弟,14年就遇到這個問題了,他最後兩段說出了個人心聲 http://stackoverflow.com/questions/23491638/is-there-a-way-to-trigger-scroll-event-with-htmlunit-or-is-it-not-possible-at-al )
不過有人提出能夠模擬滾動條下拉這個請求,而不是模擬這個動做,好比這個大神http://blog.csdn.net/zhoujianfeng3/article/details/21395223 可是代碼仍是不全,我試着嘗試,並且他模擬的url是14年的,不報但願的試了一下,果真是不行的,可是他的想法仍是給我很大的啓發,我參照他的方式,經過httpwatch看了一下,如今的滾動下拉請求基本是這個樣子(這是我關注的一個帳號的微博,由於微博比較多,能夠用來練習抓)
通過幾回分析,注意其中的這兩個參數,page=1和pagebar=0,這個page就是當前這個分頁,而這個pagebar是滾動的那個序列,也就是繼續滾下去的話,page=1不變,pagebar要變爲1,若分頁的時候,page要變爲2(變爲相應頁數),而pagebar又要從0開始,按照以前說的那個微博展現的過程理解一下就不難了
可是注意上面這個url是滾動的url,而點擊分頁又是另外一個,你們能夠本身用httpwatch看一下就知道了,這裏就不贅述
說完分析思路,再說說作法
這裏滾動下拉的請求,返回的是json數據,不是一個html頁面,因此就不能再用webClient.getPage了,你們要注意一下,而當前從我百度的來看,htmlunit還不能很好的解析json,因此這裏參考我以前說的大神的思路,用到了另外一個爬蟲工具JSoup,來解析,demo代碼以下
WebRequest requestOne = new WebRequest(new URL(url), HttpMethod.GET); WebResponse jsonOne = webClient.loadWebResponse(requestOne); JSONObject jsonObj = JSONObject.fromObject(jsonOne.getContentAsString()); String data = (String) jsonObj.get("data"); Document doc = Jsoup.parse(data); Elements elementsTwo = doc.select("div[class=WB_text W_f14]"); for (int i = 0; i < elementsTwo.size(); i++) { Element element = elementsTwo.get(i); System.out.println("第" + (counter++) + "條微博: " + elementsTwo.get(i).text()); }
url就是分頁的url,這樣用HtmlUnit來構建一個Web的請求,而後獲取到json返回的結果,轉換爲JSONObject對象,再用JSoup來解析json的字符轉,這裏的doc.select後面參數的語法是CSSQuery語法,和Xpath差不太多,你們隨即可以百度一下,也是比較好理解的,最後打印一下全部的微博就能夠了
差點忘了,這裏還須要增長新的依賴
<!-- json begin --> <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</artifactId> <classifier>jdk15</classifier> <version>2.2</version> </dependency> <!-- json end --> <!-- jsoup begin --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.6.3</version> </dependency> <!-- jsoup end -->
最後我是把這個微博帳號裏的微博5000多條微博,直接寫到一個文件裏了,算是完成了最基本的了,固然我知道這也只是爬蟲最基本的小demo,後面老大沒有給我分配其餘任務的時候,我會再繼續專研哈,但願大神們多指正
其實這裏也只是給你們提供思路了,多多debug分析一下就行了,固然我這個辦法仍是屬於比較笨的,如有大神有更吊的方法,歡迎告訴我o(^▽^)o