java爬虫教程:爬取CSDN博客文章

这个是爬虫教程第二篇,教大家如何爬取普通页面的内容。

前期准备:

  • JSOUP 1.83 jar包
  • Eclipse 任意版本能运行java就行
  • 谷歌浏览器
第一步:分析页面结构

我们要爬CSDN博客文章,直接访问某个人的CSDN博客主页,比如说我的CSDN主页:http://blog.csdn.net/guoxiaolongonly
进入主页,在页面上按F12查看标签的内容。

可以看到如上内容,基本的html就是js+css+html标签的构造,我们使用比较多的是a、img这两个元素下的内容。第一个是链接的元素,通常获取这个来实现往后的链接的跳转。第二个是图片元素,有时候需要爬图片链接来下载图片就爬这个Tag来过滤。里面的内容也都是一个链接地址。

第二步:选取合适的过滤方法

我现在想要爬的就是这个标题中的链接。

这个标签里href属性放了我想要访问的文章详情的链接

可以使用Jsoup的方法:Elements elements = doc.getElementsByTag("a");//找到所有a标签
对a标签进行过滤就行了
也可以直接获取class标签下的内容,再在这个class下找到a标签获取a标签的href属性值。
如:Element elementTitle = document.getElementsByClass("link_title").first();//标题。
这样只需要再找到这个elementTitle下的a元素就好了。

当然JSOUP过滤标签的方式很多,可以根据需要选择合适的方法来过滤。

第三步:开始写代码

好了就是这么简单。接下来看看代码吧。

//代码为2018年11月10日更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
public class MySearchTest {
private static String url = "https://blog.csdn.net";
private static String blogName = "guoxiaolongonly";
public static void main(String[] args) {
getArticleListFromUrl(url+"/" + blogName);
}
 /**
 * 获取文章列表
 *
 * @param listurl
 */
public static void getArticleListFromUrl(final String listurl) {
    boolean isStop = false;
    Document doc = null;
    try {
        doc = Jsoup.connect(listurl).userAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36").timeout(3000).post();
    } catch (IOException e) {
        e.printStackTrace();
    }
    Elements elements = doc.getElementsByTag("a");//找到所有a标签
    for (Element element : elements) {
        final String relHref = element.attr("href"); // == "/"这个是href的属性值,一般都是链接。这里放的是文章的连接
        //用if语句过滤掉不是文章链接的内容。因为文章的链接有两个,但评论的链接只有一个,反正指向相同的页面就拿评论的链接来用吧
        if (!relHref.startsWith("http://") && relHref.contains("details") && relHref.endsWith("comments")) {
            StringBuffer sb = new StringBuffer();
            sb.append(relHref);
            System.out.println(sb.substring(0, sb.length() - 9));//去掉最后的#comment输出
            getArticleFromUrl(sb.substring(0, sb.length() - 9));//可以通过这个url获取文章了
        }
        if (relHref.equals("https://mp.csdn.net//postlist")) {
            isStop = true;
        }
    }
    if (!isStop) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (!listurl.contains("list")) {
                    getArticleListFromUrl(listurl + "/article/list/1");//获取下一页的列表
                } else {
                    getArticleListFromUrl(listurl.substring(0, listurl.length() - 1) +
                            (Integer.valueOf(listurl.substring(listurl.length() - 1, listurl.length())) + 1));//获取下一页的列表
                }
            }
        }).start();
    }
}
/**
* 获取文章内容
* @param detailurl
*/
public static void getArticleFromUrl(String detailurl) {
try {
Document document = Jsoup.connect(detailurl).userAgent("Mozilla/5.0").timeout(3000).post();
Element elementTitle = document.getElementsByClass("link_title").first();//标题。 这边根据class的内容来过滤
System.out.println(elementTitle.text());
String filename = elementTitle.text().replaceAll("/", "或");
Element elementContent = document.getElementsByClass("article_content").first();//内容。
saveArticle(filename , elementContent.text(), blogName);
// String Content =elementContent.te xt().replaceAll(" ", "\t");
// System.out.println(elementContent.text()+"\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 保存文章到本地
* @param titile
* @param content
* @param blogName
*/
public static void saveArticle(String titile, String content, String blogName) {
String lujing = "d:\\MyLoadArticle\\" + blogName + "\\" + titile + ".txt";//保存到本地的路径和文件名
File file = new File(lujing);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileWriter fw = new FileWriter(file, true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.flush();
bw.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

总结

这个类中我封装了获取文章列表、获取文章内容保存文章内容的方法。
首先解析首页获取首列表页中每个文章的链接,再依次访问每个链接爬取文章标题和内容。
如果列表页存在下一页。再对下一页列表进行相同的操作,是不是很好理解了。
文章保存格式为txt文本,保存的路径根据博客名来命名,保存的文件名称为文章标题,文本则保存文章内容。
有这个demo是不是可以写出文章搜索器啊,文章过滤器...甚至爬取整个csdnblog的所有文章都不是问题。就看你方法和过滤怎么做了。
大家可以设置blogName去爬你想要的文章了,比如说我:guoxiaolongonly
这边用post模拟浏览器请求。因为直接get,页面无法加载。
针对文本保存文档编码乱码问题。还希望大家自己研究一下。
好运~~ 晚安~

By Xiaolong:You have a dream,you got to protect it!