java爬虫小项目-挖取CSDN博客文章
开始学习java爬虫,网上好多的小项目,做起来简单,能增加学习的自信心,现在就教大家我学习的一个小项目。
以《第一行代码--安卓》的作者为例,将他在csdn发表的博客信息都挖取出来,郭霖的博客地址是:“http://blog.csdn.net/guolin_blog”
文章图片
在动手实验之前,假设你已经基本掌握了如下的技能:JAVA基础编程,简单的正则表达式,JS或者jQuery的编程能力,此外还学过http协议的一些知识。除了这些知识还有一个jsoup,它能使Java程序像jQuery那样的语法来操作html的Dom节点元素,jsoup也有像jQuery一样的选择器功能,比如getElementById,getElemementsByClass等语法与JavaScript像极了,此外jsoup还有select选择器功能。所以,这里只要稍微掌握jsoup语法就可以像JS操作Dom一样的用Java来处理请求到的html网页。jsoup的中文官方教程地址http://www.open-open.com/jsoup/。
【java爬虫小项目-挖取CSDN博客文章】
工欲善其事必先利其器。开始之前,你应该有一定的工具,比如一款熟悉的ide,用来调试和查看变量。一个web调试工具,如火狐的firebug之类的。总之,就是有一个java web程序员日常开发和调试使用的工具就差不多了。
第一步:新建一个Java Se项目。这个项目如果是一个Maven项目,那么需要添加如下的依赖配置:
org.jsoup
jsoup
1.10.2
如果这个项目不是Maven项目,那么你需要下载jsoup-1.10.2.jar包,然后在你的项目右键新建一个lib目录,把下载的jar包放进去,并把jar包添加到classpath。
文章图片
第二步:分析代码编写思路:先创建一个Conection链接,这个链接包含向CSDN服务器发起请求的url地址,userAgent参数,timeOut参数已经Http请求类型等。接着向服务器发送get请求Document doc = conn.get(); 注意:很多网络原因导致这一步出现异常,比如timeOut时间设置太短也会报错,这里我设置为5000毫秒。如果这一步你的程序没有报错,说明已经获取了请求的html文档。然后我们可以将html的body标签里面的内容赋值给遍历Element数据类型的实例body。处理body标签的内容jsoup正式出场,除了jsoup还会夹杂简单的正则内容。
用firebug可以看到。首页与博客文章相关的内容在这个div class="list_item_new"标签下面,而这个div下面包含3个div,分别是:div id="article_toplist" class="list"表示置顶的文章,div id="article_list" class="list"博文列表所在的div,div id="papelist" class="pagelist"底下分页信息所在的div。抛开置顶这个div,我们只关注文章列表的div和分页信息div。如果你仔细的分析,那么会发现我们关心的每篇文章而每篇文章的标签如下div:`
文章图片
每篇文章占据的div,完整的html元素如下:
Dialog和Toast所有人肯定都不会陌生的,这个我们平时用的实在是太多了。而Snackbar是Design Support库中提供的新控件,有些朋友可能已经用过了,有些朋友可能还没去了解。但是你真的知道什么时候应该使用Dialog,什么时候应该使用Toast,什么时候应该使用Snackbar吗?本篇文章中我们就来学习一下这三者使用的时机,另外还会介绍一些额外的技巧...
仔细分析一下,这个div中涵盖了文章的简介,阅读次数,连接地址等等,总之,这个div才是重头戏要获取的数据都在这呢。
文章图片
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
/**
* java爬取csdn博客的简单的案例,如果你只爬取某个博主的首页文章,那么参考本程序员
* 如果你想爬取某位博主的所有文章,请参考Main.java
* @author shizongger
* @date 2017/02/09
*/
public class Main1 {//需要进行爬取得博客首页
//private static final String URL = "http://blog.csdn.net/guolin_blog";
private static final String URL = "http://blog.csdn.net/sinyu890807/article/list/2";
public static void main(String[] args) throws IOException {//获取url地址的http链接Connection
Connection conn = Jsoup.connect(URL)//博客首页的url地址
.userAgent("Mozilla/5.0 (Windows NT 6.1;
rv:47.0) Gecko/20100101 Firefox/47.0") //http请求的浏览器设置
.timeout(5000)//http连接时长
.method(Connection.Method.GET);
//请求类型是get请求,http请求还是post,delete等方式
//获取页面的html文档
Document doc = conn.get();
Element body = doc.body();
//将爬取出来的文章封装到Artcle中,并放到ArrayList里面去
List resultList = new ArrayList();
Element articleListDiv = body.getElementById("article_list");
Elements articleList = articleListDiv.getElementsByClass("list_item");
for(Element article : articleList){
Article articleEntity = new Article();
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);
}//遍历输出ArrayList里面的爬取到的文章
System.out.println("文章总数:" + resultList.size());
for(Article article : resultList) {
System.out.println("文章绝对路劲地址:http://blog.csdn.net" + article.getAddress());
}
}}这样运行一下,就可以得到大神的文章了,运行结果像这样:
文章图片
现在可以将当前页数的文章挖掘出来了,但是郭神的技术文章不止一页啊,还要进一步分页挖掘。以前我是想Java的分页思路是怎么写的,我们可以逆着它的分页思路来。但是现在感觉与它怎么分页无关,但是如果你了解Java分页那么更好的理解接下来怎么做。
要想爬取它的所有文章,可以对他的博客每一个页面分别进行请求。
首页地址可以是:
http://blog.csdn.net/guolin_blog
也可以是:
http://blog.csdn.net/guolin_blog/article/list/1
那么第二页以后的url地址如下:
http://blog.csdn.net/guolin_blog/article/list/index
index表示请求的页数。
现在的任务就是来抓取总页数了。来来来,我们用firebug看一看。
文章图片
可以看到总页数位于上诉div下的第一个span标签,幸运的是这个div有一个独一无二的id号,而这个span与div的关系是父节点与子节点的关系,获取图中红圈内字符串的代码是"body.getElementById("papelist").select("span:eq(0)").text(); "。而span标签里面的内容" 100条 共7页"是汉字,空格和数字混合组成,这时候正则表达式闪亮登场。为了选取"共x页"里面的x的值,正则的语法关键代码是:String regex = ".+共(\d+)页";
至此,就可以将郭霖的csdn技术博客都可以获取了。此时你只需要将得到的信息都封装好,在需要的时候调用就行了。
完整代码如下:
Article.java
/**
* 文章的JavaBean.
* date:2017-02-09
*/
public class Article {/**
* 文章链接的相对地址
*/
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;
}
}
Main.java
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.javaspider.Article;
/**
* @author shizongger
* @date 2017/02/09
*/
public class Main {private static final String URL = "http://blog.csdn.net/guolin_blog";
public static void main(String[] args) throws IOException {
Connection conn = Jsoup.connect(URL)
.userAgent("Mozilla/5.0 (X11;
Ubuntu;
Linux x86_64;
rv:50.0) Gecko/20100101 Firefox/50.0")
.timeout(5000)
.method(Connection.Method.GET);
Document doc = conn.get();
Element body = doc.body();
//获取总页数
String totalPageStr = body.getElementById("papelist").select("span:eq(0)").text();
String regex = ".+共(\\d+)页";
totalPageStr = totalPageStr.replaceAll(regex, "$1");
int totalPage = Integer.parseInt(totalPageStr);
int pageNow = 1;
List articleList = new ArrayList();
for(pageNow = 1;
pageNow <= totalPage;
pageNow++){
articleList.addAll(getArtitcleByPage(pageNow));
}//遍历输出博主所有的文章
for(Article article : articleList) {
System.out.println("文章标题:" + article.getTitle());
System.out.println("文章绝对路劲地址:http://blog.csdn.net" + article.getAddress());
System.out.println("文章简介:" + article.getDesption());
System.out.println("发表时间:" + article.getTime());
}
}public static List 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(5000)
.method(Connection.Method.GET);
Document doc = conn.get();
Element body = doc.body();
List resultList = new ArrayList();
Element articleListDiv = body.getElementById("article_list");
Elements articleList = articleListDiv.getElementsByClass("list_item");
for(Element article : articleList){
Article articleEntity = new Article();
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;
}
}运行一下:
文章图片
是不是非常简单!!!
两个注意之处
- Conection的timeOut不宜过短。如果把timeOut设置为20毫秒,则会报错。
Exception in thread "main" java.net.SocketTimeoutException: connect timed out
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
at sun.net.www.http.HttpClient.
System.setProperty("http.maxRedirects", "50");
System.getProperties().setProperty("proxySet", "true");
String ip = "代理服务器地址";
System.getProperties().setProperty("http.proxyHost", ip);
System.getProperties().setProperty("http.proxyPort", "代理的端口");
推荐阅读
- 如何使用Node.js中的Website Scraper克隆网站(下载HTML,CSS,JavaScript,字体和图像)
- 前端 JavaScript 实现一个简易计算器
- 优雅编程 | 7 个你应该掌握的 JavaScript 编码技巧
- JavaScript 中的高阶函数
- 给定月,日和年,以及Zeller在JavaScript中的一致性,如何确定星期几
- 如何通过JavaScript中的十六进制代码检索颜色的人名
- 从内部类内部访问局部变量,需要将其声明为final(java)
- Java ASM系列((051)opcode: constant)
- 如何使用JavaScript确定Google地图上特定位置的用户点击的当前坐标
- 如何使用JavaScript在浏览器中检索MP3 / WAV音频文件的持续时间