htmlunit+quartz定时抓取博文并生成jsp页面

本文介绍了一种从优快云博客自动抓取文章,并将其转化为JSP页面的方法,包括页码、文章链接及内容的分析过程,最终通过IO流生成带有个人风格的网页。

前言

看视频看的累了 写写博文~
很久以前就想有个自己的博客。csdn很好,可是我不是专家啊,还是功底不够, 没有权限,也就不能实现自己的一些想法。所以就百度了一个静态模版。有了模版,问题来了。是个空壳子啊,没有任何内容,所以就想着爬取自己csdn上面的所有文章并生成相应的jsp页面保存到本地,而且为了实时保证自己的博文新鲜度所以就用spring-quartz定时器定时执行抓取页面的任务。博客地址(http://www.susulovefreedom.cn

分析网页

分析完网页,我终于发现为啥我的博文总是那么轻易的被别人爬走了 , 百度搜索排名还在我的前面。原来那么简单

页码分析

http://blog.youkuaiyun.com/su20145104009?viewmode=contents
在这个页面我们可以看到最短的分页信息。
然后通过审查元素定位到这里。
这里写图片描述
仔细查看,最大页码为12.在id为papelist标签下的第一个span元素。所以最大页码就可以这样获得

HtmlPage page=wc.getPage("http://blog.youkuaiyun.com/su20145104009?viewmode=contents");
            DomElement pageList = page.getElementById("papelist");
            //最大页码
            int MaxPage=Integer.parseInt(pageList.getFirstChild().asText().substring(6, 8));

文章链接分析

在上一步,我们得到了分页的最大页码。
通过查看url可以发现http://blog.youkuaiyun.com/su20145104009/article/list/2 最后一位
即为你要请求的页码。
那么就可以通过get请求来获得某一页的数据。
page=wc.getPage(“http://blog.youkuaiyun.com/su20145104009/article/list/“+MaxPage);
而在进入某一页后。继续查看源码
这里写图片描述
可以发现所有的文章都在article_list标签内。
所以我们可以首先获得article_list标签。
DomElement article_list = page.getElementById(“article_list”);

在article_list 标签内我们要继续获得文章的信息。
首先获得所有的article_list 子标签(并不包括子标签的子标签)。即上述图片的
这里写图片描述

    DomNodeList<DomNode> childNodes = article_list .getChildNodes();

然后通过for循环对每一篇的文章细节进行分析:

for (DomNode htmlElement : childNodes)

这里写图片描述
文章的所有信息一览无余。
由于里面只有两个a标签。

  • 在第一个a标签里面有文章的地址,标题信息。
  • 在第二个a标签里面有文章的阅读次数信息。然后第二个a标签的父标签的父标签下的第一个标签内有文章的创建日期信息。
  • 在获得题目的同时,使用md5码对文章的标题进行加密作为数据库中的主键

所以通过标签名字获取两个a标签:

    DomNodeList<HtmlElement> links = ((DomElement) htmlElement).getElementsByTagName("a");
String address=links.get(0).getAttribute("href");
                String title=links.get(0).asText();
                String readCounts=links.get(1).getParentNode().asText();
                String time=links.get(1).getParentNode().getParentNode().getFirstChild().asText();

到了这一步仅仅获得了所有文章的题目信息。
那么应该如何获得文章的具体内容呢?

文章内容分析

定位到某一篇文章。通过审查元素,我们可以找到所有的文章内容都在article_details标签里。
这里写图片描述
剩下的就是把article_details标签的所有内容存到自己的网页了。
在这里遇到了一问题。
起初我使用的是htmlunit工具直接通过getElementById的方法获取article_details的源码。可以生成后的网页分析后发现。所有的文章是正常显示了,可是代码出现严重的换行现象。通过对比源码后发现,htmlunit的axXml方法会自动对所有的标签进行格式化操作。google许久也没有找到适当的解决办法。灵光一闪,想到了URLConnection。
URLConnection的get请求获得的源码和浏览器源码一致。
于是定义了一个方法。对获得的源码进行字符串截取即可。正则表达式不是我不用,是爆栈啦~字符串太长了。。。。

/**
     * 
    * @Title: getArticle
    * @Description: (通过URLConnection的get请求获得文章的源码信息)
    * @param  link 文章的链接
    * @return String    文章的源码
     */
    public static String getArticle(String link){
        URL url=null;
        URLConnection conn=null;
        InputStream is = null;  
        //字符流  
        InputStreamReader isr = null;  
        //缓冲流  
        BufferedReader br = null;  

        try {
            url=new URL(link);
            conn= url.openConnection();
            conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");  
            conn.connect();
            //网页编码  
            String context = null;  
            //获得网页的字节输入流  
            is = conn.getInputStream();  
            //将字节输入流转换为字符输入流 并设置转码格式为utf-8  
            isr = new InputStreamReader(is, "utf-8");  
            //将字符流转换为缓冲流  
            br = new BufferedReader(isr);  
            //一行一行读取网页内容  
            context = br.readLine();  
            String txt="";
            while ((txt=br.readLine())!=null) {  
                context +=txt+"\n";  
            }  
            int st=context.indexOf("<div id=\"article_content\"");
            int ed=context.indexOf("<!-- Baidu Button BEGIN -->");

            return context.substring(st, ed);
        } catch (Exception e) {
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e1) {
                    throw new RuntimeException(e1);
                }
            }
            if(isr!=null){
                try {
                    isr.close();
                } catch (IOException e1) {
                    throw new RuntimeException(e1);
                }
            }
            if(br!=null){
                try {
                    br.close();
                } catch (IOException e1) {
                    throw new RuntimeException(e1);
                }
            }
            throw new RuntimeException(e);
        } 

到了这里基本就快完成了~
剩下的就是使用io流写入网页了

io流生成jsp页面

我们的jsp页面肯定不能只有个文章的信息。当然也需要一些其它的元素。所以这些其它元素就需要我们自己准备了,保存到web项目里,然后使用io流读取即可。由于至少需要读取两个部分。所以我新建了一个方法

/**
     * 
    * @Title: getHeadHtml
    * @Description: (通过文件的路径读取相应的文件信息并返回)
    * @param  path  文件路径
    * @return String   文件内的信息
    * @throws
     */
    public static String getHeadHtml(String path) {
        String res="";
        BufferedReader br=null;
        String data=null;
        try {
            br=new BufferedReader(new InputStreamReader(new FileInputStream(path),"utf-8"));
            while((data=br.readLine())!=null){
                res+=data+"\n";
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally{
            if(br!=null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return res;
    }

然后就是写入jsp信息了。如果当前数据库中没有这篇文章,那么就生成这个jsp页面。jsp的名称我使用的是文章题目md5码的后8位

if(!articleService.findArticleById(id)){
                    //将源码写入到文件

                    OutputStreamWriter bw=new OutputStreamWriter(new FileOutputStream(filePath+"articles/"+htmlname+".jsp"),"utf-8");
                    //jsp的头部信息
                    bw.write(MyStringUtil.getHeadHtml(filePath+"headHtml.txt"));
                    //head标签中的title标签
                    bw.write("<title>"+title+"</title>");
                    //head标签中的标题信息
                    bw.write("<meta name=\"description\" content=\"");
                    bw.write(content+"\" />");
                    bw.write("<base href=\"<%=basePath%>articleShow_"+htmlname+".html\"/>");
                    //文章内容前的信息 一些js文件css文件啦
                    bw.write(MyStringUtil.getHeadHtml(filePath+"titlePreHtml.txt"));
                    bw.write(title+"</a></h3>");
                    //通过文章的链接 调用getArticle方法获得文章的源码 并写入jsp页面
                    bw.write(MyStringUtil.getArticle(article.getLink()));
                    //文章底部的信息
                    bw.write(MyStringUtil.getHeadHtml(filePath+"endHtml.txt"));
                    bw.flush();
                    bw.close();
                    System.out.println(title+"制作网页完成");
                }

到了这里就大功告成了。

squarz设置定时抓取

配置如下:每12个小时执行一次抓取任务
如果对squarz定时器不懂,请转http://blog.youkuaiyun.com/su20145104009/article/details/72842526

<!-- 任务bean 
        由于我要写入数据库 所有注入了articleService 
    -->
    <bean id="hourWork" class="com.scx.hourwork.HourWork">
        <property name="articleService" ref="articleService"></property>
    </bean>
   <!-- 使用MethodInvokingJobDetailFactoryBean,
            任务类可以不实现Job接口,
            targetObject:调用类
            targetMethod:调用方法
    -->    
    <bean id="methodInvokingBean"  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="hourWork"></property>
        <property name="targetMethod" value="execute"></property>
    </bean>
    <!-- 任务触发器 -->
    <bean  id="myTriggers" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <property name="jobDetail" ref="methodInvokingBean"></property>
        <!-- 每隔12个小时更新一次 -->
        <property name="cronExpression" value="00 00 0/12 * * ?"></property>
    </bean>
    <!-- 任务调度工厂 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- 可以使用list配置多个,第一种方式已经演示 -->
        <property name="triggers" ref="myTriggers"></property>
    </bean>

执行后可以在文件夹中查看
这里写图片描述
当点击自己文章的链接时,去文章id的后8位转向这个jsp页面。
这里写图片描述

写了一个多小时 也该回宿舍了~~希望暑期能找到个实习工作 唉

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值