码迷,mamicode.com
首页 > Web开发 > 详细

HtmlUnit 网络爬虫 菜鸟的学习笔记(二)

时间:2015-09-01 15:25:48      阅读:1075      评论:0      收藏:0      [点我收藏+]

标签:

这次我以爬新浪微博为例,这个过程太纠结了,参考了好多大神的帖子,不过还是遗留了很多问题,我们慢慢来看,希望大神帮于指正,我的方法暂时来说还是比较挫的

        登陆问题

        爬新浪微博首先要登陆,之前爬的妹纸网站,由于不用登陆,所以没这一步,但是爬新浪微博我们必须要先登录,但是要涉及到一个问题,那就是验证码,验证码从我现在百度到的,和自己的理解,感觉暂时还是不能解决的,除非手工输入,因为本身验证码就是防止恶意登陆,防爬虫的,所以建议想试试的朋友用暂时用不输入验证码的账号试试(关于验证码,期盼大神可以给些提示

        下面是demo代码

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 

            这个网站打开其实是新浪通行证的登陆页面

           技术分享

            开始我想为啥是这个界面,所以后面用httpwatch看了一下通过www.weibo.com登陆的时候整个请求过程

             可以看到,在输入完密码和账号之后,我们post的地址正是这个地址,也就是说,新浪微博的登陆内部应该是这样的            

             1. 请求www.weibo.com 得到登陆页面,输入完用户密码

              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看了一下,现在的滚动下拉请求基本是这个样子(这是我关注的一个账号的微博,因为微博比较多,可以用来练习抓)

            http://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=100505&from=myfollow_all&pre_page=1&page=1&max_id=&end_id=3882220290581087&pagebar=0&filtered_min_id=&pl_name=Pl_Official_MyProfileFeed__22&id=1005051645851277&script_uri=/u/1645851277&feed_type=0&domain_op=100505&__rnd=1441077128511 

            经过几次分析,注意其中的这两个参数,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

 

        

HtmlUnit 网络爬虫 菜鸟的学习笔记(二)

标签:

原文地址:http://my.oschina.net/u/259421/blog/500070

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!