此次我以爬新浪微博为例,这个过程太纠结了,参考了好多大神的帖子,不过仍是遗留了不少问题,咱们慢慢来看,但愿大神帮于指正,个人方法暂时来讲仍是比较挫的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