JAVA爬虫挖取CSDN博客文章

来源:互联网 发布:腾讯红包数据控制 编辑:程序博客网 时间:2024/04/25 23:02

开门见山,看看这个教程的主要任务,就去csdn博客,挖取技术文章,我以《第一行代码–安卓》的作者为例,将他在csdn发表的额博客信息都挖取出来。因为郭神是我在大学期间比较崇拜的对象之一。他的csdn首页如下:http://blog.csdn.net/guolin_blog,首页如图:这里写图片描述

你需要掌握的技术有:java se,正则表达式,js dom编程思想,jsoup,此外还需要http协议的一些知识。其中其他技术点可能你以前就掌握了,只差一个jsoup了,这个哥们是干嘛使的呢?我用一句话来说,就是Java程序获取html之后,向js,jquery一样来解析dom节点的,很多语法与js/jquery十分的相似,比如getElementById,getElemementsByClass等等语法与js相似极了,此外jsoup还有select选择器功能。所以,这里只要稍微掌握jsoup语法就可以像js操作dom一样的用Java来操作请求到的html网页了。jsoup的官方教程地址:http://www.open-open.com/jsoup/。

开始之前,你应该有一定的工具的,不如已经有一款熟练的ide来调试和查看变量,有一个web调试工具,如火狐的firebug之类的,总之,就是有一个java web程序员日常开发和调试使用的工具就差不多了。

第一步:新建一个maven项目。这个maven项目可以是一个java se项目,选择jar包就行了。重点是引入jsoup所需要的jar包。pom.xml配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>com.shizongger.jsoup</groupId>  <artifactId>jsoup-shizongger</artifactId>  <version>0.0.1-SNAPSHOT</version>  <dependencies>    <dependency>      <!-- jsoup HTML parser library @ http://jsoup.org/ -->      <groupId>org.jsoup</groupId>      <artifactId>jsoup</artifactId>      <version>1.9.2</version>    </dependency>    <dependency>        <groupId>org.apache.httpcomponents</groupId>        <artifactId>httpclient</artifactId>        <version>4.5.1</version>    </dependency>       <dependency>        <groupId>org.mockito</groupId>        <artifactId>mockito-all</artifactId>        <version>1.8.4</version>    </dependency>    <dependency>        <groupId>junit</groupId>        <artifactId>junit</artifactId>        <version>4.12</version>    </dependency>  </dependencies>  <build>      <plugins>        <plugin>          <groupId>org.apache.maven.plugins</groupId>          <artifactId>maven-compiler-plugin</artifactId>          <configuration>            <source>1.6</source>            <target>1.6</target>          </configuration>        </plugin>      </plugins>  </build>  </project>

第二步:新建一个带有main方法的类,当然你也可以不带main方法,不过我只是写一个java se小程序而已。(如果是配置了http代理的,按照这一步:配置http代理,这点很重要,不然请求不到html网页的,我们都知道,我们的浏览器都配置了代理了的,不然根本无法请求到html网页,不信你可以把IE浏览器的代理取消,去浏览百度一下看看)。设置http代理的代码如下:

        System.setProperty("http.maxRedirects", "50");        System.getProperties().setProperty("proxySet", "true");        // 如果不设置,只要代理IP和代理端口正确,此项不设置也可以        String ip = "代理服务器地址";        System.getProperties().setProperty("http.proxyHost", ip);        System.getProperties().setProperty("http.proxyPort", "代理的端口");

上面ip是代理的ip地址,端口8080是代理的端口,参考的地方就是自己去IE浏览器的配置查看。
第三步:好了,如果是一般的网站,直接可以发送post或者get请求了,但是csdn还需要在http头发送几个信息,反正之前我没有发送这些头部信息,是请求不到的。用firebug分析数据,如图:
这里写图片描述

    private static final String URL = "http://blog.csdn.net/guolin_blog";    Connection conn = Jsoup.connect(URL)        .userAgent("Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101                                Firefox/47.")        .timeout(30000)        .method(Connection.Method.GET);

第三步:到这里,可以得到一个Connection了,接着就可以获取一个html文档了。
Document doc = conn.get();
这里写图片描述
如图,html的内容已经加载进来了。
第四步:用jsoup获取所得到的dom节点,有必须时,还需要用到正则表达式,不过本篇博客不会用到正则。其他任务可能会用到,总之,java爬虫应该经常用到正则表达式的。
我用firebug看到所有的文章,都是显示在<div id="article_list" class="list"></div>内,而每篇文章都是在这个div下面的这个div:<div class="list_item article_item">
,
这里写图片描述
每篇文章占据的div,完整的html元素如下:

<div class="list_item article_item">        <div class="article_title">            <span class="ico ico_type_Original"></span>    <h1>        <span class="link_title"><a href="/guolin_blog/article/details/51336415">        Android提醒微技巧,你真的了解Dialog、Toast和Snackbar吗?                    </a></span>    </h1></div>        <div class="article_description">Dialog和Toast所有人肯定都不会陌生的,这个我们平时用的实在是太多了。而Snackbar是Design Support库中提供的新控件,有些朋友可能已经用过了,有些朋友可能还没去了解。但是你真的知道什么时候应该使用Dialog,什么时候应该使用Toast,什么时候应该使用Snackbar吗?本篇文章中我们就来学习一下这三者使用的时机,另外还会介绍一些额外的技巧...        </div>            <div class="article_manage">             <span class="link_postdate">2016-07-26 07:55</span>        <span class="link_view" title="阅读次数"><a href="/guolin_blog/article/details/51336415" title="阅读次数">阅读</a>(7458)</span>        <span class="link_comments" title="评论次数"><a href="/guolin_blog/article/details/51336415#comments" title="评论次数" onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_pinglun'])">评论</a>(45)</span>    </div>        <div class="clear"></div>    </div>

仔细分析一下,这个div中涵盖了文章的简介,阅读次数,连接地址等等,总之,这个div才是重头戏要获取的数据都在这呢。
这里写图片描述

    public static List<ArticleEntity> getArtitcleByPage(int pageNow) throws IOException{        Connection conn = Jsoup.connect(URL + "/article/list/" + pageNow)                .userAgent("Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.")                .timeout(30000)                .method(Connection.Method.GET);        Document doc = conn.get();        Element body = doc.body();        List<ArticleEntity> resultList = new ArrayList<ArticleEntity>();        Element articleListDiv = body.getElementById("article_list");        Elements articleList = articleListDiv.getElementsByClass("list_item");        for(Element article : articleList){            ArticleEntity articleEntity = new ArticleEntity();            Element linkNode = (article.select("div h1 a")).get(0);                     Element desptionNode = (article.getElementsByClass("article_description")).get(0);            Element articleManageNode = (article.getElementsByClass("article_manage")).get(0);            articleEntity.setAddress(linkNode.attr("href"));            articleEntity.setTitle(linkNode.text());            articleEntity.setDesption(desptionNode.text());            articleEntity.setTime(articleManageNode.select("span:eq(0").text());            resultList.add(articleEntity);        }        return resultList;    }

现在可以将当前页数的文章挖掘出来了,但是郭神的技术文章不止一页啊。就是传说中的,还要进行分页挖掘,首先我们来看看我们的Java是如何做分页的。虚算法如下:
这里写图片描述
这里写图片描述

分页存储过程或者页面分页中的分页算法:int pagesize // 每页记录数int recordcount // 总记录数int pagecount // 总页数pagecount=(recordcount+pagesize-1)/pagesize此方法得出的结果为实际页码pagecount=(recordcount-1)/pagesize此方法得出的结果为实际页码-1注:两个整数相除的结果始终是整数。例如:5除以2的结果为2。若要确定5除以2的余数,请使用modulo运算符(%)。若要获取作为有理数或分数的商,应将被除数或除数设置为floatdouble型。可以通过在数字后添加一个小数点来隐式执行此操作

那么,我们先来获取总记录数,总页数,每页有多少技术文章吧!好在csdn的信息都放在地下的div里面了呢,来来来,我们用firebug看一看。

<div id="papelist" class="pagelist"><span> 94条  共7页</span><strong>1</strong> <a href="/sinyu890807/article/list/2">2</a> <a href="/sinyu890807/article/list/3">3</a> <a href="/sinyu890807/article/list/4">4</a> <a href="/sinyu890807/article/list/5">5</a> <a href="/sinyu890807/article/list/6">...</a> <a href="/sinyu890807/article/list/2">下一页</a> <a href="/sinyu890807/article/list/7">尾页</a>     </div>

代码如下:

    private int totalPage;    private int nowPage;    /**     *  despt:分页算法     * @return     * @throws IOException     */    public List<ArticleEntity> goPage() throws IOException{        Connection conn = Jsoup.connect(URL)                .userAgent("Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.")                .timeout(30000)                .method(Connection.Method.GET);        Document doc = conn.get();        Element body = doc.body();        //获取总页数        String totalPage = body.getElementById("papelist").select("span:eq(0)").text();        String regex = ".+共(\\d+)页";        totalPage = totalPage.replaceAll(regex, "$1");        this.totalPage = Integer.parseInt(totalPage);        this.nowPage = 1;        List<ArticleEntity> list = new ArrayList<ArticleEntity>();        for(nowPage = 1; this.nowPage <= this.totalPage; this.nowPage++){            list.addAll(getArtitcleByPage(this.nowPage));        }        return list;    }

至此,就可以将郭霖的csdn技术博客都可以获取了。此时你只需要将得到的信息都封装好,在需要的时候调用就行了。
完整代码如下:

public class ArticleEntity {    /**     * ���µ�ַ����Ե�ַ     */    private String address;    /**     * ���±���     */    private String title;    /**     * ��������     */    private String desption;    /**     * ����ʱ��     */    private String time;    public String getAddress() {        return address;    }    public void setAddress(String address) {        this.address = address;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public String getDesption() {        return desption;    }    public void setDesption(String desption) {        this.desption = desption;    }    public String getTime() {        return time;    }    public void setTime(String time) {        this.time = time;    }}
import java.io.IOException;import java.util.ArrayList;import java.util.List;import org.jsoup.*;import org.jsoup.nodes.*;import org.jsoup.select.*;import com.shizongger.entity.ArticleEntity;public class MyBlog {    private static final String URL = "http://blog.csdn.net/guolin_blog";    private int totalPage;    private int nowPage;    public static void main(String[] args) {        System.setProperty("http.maxRedirects", "50");        System.getProperties().setProperty("proxySet", "true");        // 如果不设置,只要代理IP和代理端口正确,此项不设置也可以        String ip = "172.17.18.80";        System.getProperties().setProperty("http.proxyHost", ip);        System.getProperties().setProperty("http.proxyPort", "8080");        MyBlog demo = new MyBlog();        try {            List<ArticleEntity> list = demo.goPage();            for(ArticleEntity tmp : list) {                System.out.println("文章地址:" + tmp.getAddress());                System.out.println("文章标题:" + tmp.getTitle());                System.out.println("文章简介:" + tmp.getDesption());                System.out.println("发表时间:" + tmp.getTime());            }        } catch (IOException e) {            e.printStackTrace();        }    }    /**     *  despt:分页算法     * @return     * @throws IOException     */    public List<ArticleEntity> goPage() throws IOException{        Connection conn = Jsoup.connect(URL)                .userAgent("Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.")                .timeout(30000)                .method(Connection.Method.GET);        Document doc = conn.get();        Element body = doc.body();        //获取总页数        String totalPage = body.getElementById("papelist").select("span:eq(0)").text();        String regex = ".+共(\\d+)页";        totalPage = totalPage.replaceAll(regex, "$1");        this.totalPage = Integer.parseInt(totalPage);        this.nowPage = 1;        List<ArticleEntity> list = new ArrayList<ArticleEntity>();        for(nowPage = 1; this.nowPage <= this.totalPage; this.nowPage++){            list.addAll(getArtitcleByPage(this.nowPage));        }        return list;    }    public static List<ArticleEntity> getArtitcleByPage(int pageNow) throws IOException{        Connection conn = Jsoup.connect(URL + "/article/list/" + pageNow)                .userAgent("Mozilla/5.0 (Windows NT 6.1; rv:47.0) Gecko/20100101 Firefox/47.")                .timeout(30000)                .method(Connection.Method.GET);        Document doc = conn.get();        Element body = doc.body();        List<ArticleEntity> resultList = new ArrayList<ArticleEntity>();        Element articleListDiv = body.getElementById("article_list");        Elements articleList = articleListDiv.getElementsByClass("list_item");        for(Element article : articleList){            ArticleEntity articleEntity = new ArticleEntity();            Element linkNode = (article.select("div h1 a")).get(0);                     Element desptionNode = (article.getElementsByClass("article_description")).get(0);            Element articleManageNode = (article.getElementsByClass("article_manage")).get(0);            articleEntity.setAddress(linkNode.attr("href"));            articleEntity.setTitle(linkNode.text());            articleEntity.setDesption(desptionNode.text());            articleEntity.setTime(articleManageNode.select("span:eq(0").text());            resultList.add(articleEntity);        }        return resultList;    }}

在控制台已经输出想要的信息,这里截取一段吧,

文章地址:/guolin_blog/article/details/51336415文章标题:Android提醒微技巧,你真的了解Dialog、Toast和Snackbar吗?文章简介:Dialog和Toast所有人肯定都不会陌生的,这个我们平时用的实在是太多了。而Snackbar是Design Support库中提供的新控件,有些朋友可能已经用过了,有些朋友可能还没去了解。但是你真的知道什么时候应该使用Dialog,什么时候应该使用Toast,什么时候应该使用Snackbar吗?本篇文章中我们就来学习一下这三者使用的时机,另外还会介绍一些额外的技巧...发表时间:2016-07-26 07:55
1 0