1.准备工作
中国诗词网网址:http://www.shicimingju.com/
开发平台:Ubentu18.10 + jdk1.8.191 + maven3.6 + IDEA
maven依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.需求分析
爬取中国诗词网 -- 史书典籍 -- 全部 -- 文章内容,如下图所示:
![](https://img.haomeiwen.com/i14052510/5947be87f5b96b98.png)
3.网页分析
史书典籍网址:http://www.shicimingju.com/book/
该网页下的代码结构如下,可以发现书名和链接
![](https://img.haomeiwen.com/i14052510/1762ba9417a16620.png)
随便点进一本书中,可以发现进入到的网页中包含每张的章节名和链接地址:
![](https://img.haomeiwen.com/i14052510/b9b38da0cd57b6df.png)
再随便点几章,就会发现每章网页中存信息的方式都是有规律的:
![](https://img.haomeiwen.com/i14052510/d3ff3e81a3e62c58.png)
至此,关系就很清晰了;我也找到了我想要下载的内容,正式开始编写代码
4.编写代码
在编写代码的过程中遇到的几个问题:
①如何实现网页的拼接?
②如何计算每本书下有都少章?
③要爬取的信息之间的逻辑关系有哪些?
④等等
代码如下:
package edu.neepu;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.Test;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
public class Main {
@Test
public void test2() throws IOException {
File file = new File("/home/lure/Downloads");
if (!file.exists()) {
file.mkdirs();
}
File file_son = new File(file + "/work");
if (!file_son.exists()) {
file_son.mkdirs();
}
File file_son_son = new File(file_son + "/1.txt");
if (!file_son_son.exists()) {
file_son_son.createNewFile();
}
}
@Test
public void test() throws IOException {
//爬书
Map<String, String> allBooks = getAllBooks();
//将数据存到下面的目录下
File file = new File("/home/lure/Downloads/books");
if (!file.exists()) {
file.mkdirs();
}
//测试输出
Set<Map.Entry<String, String>> entrys = allBooks.entrySet();
for (Map.Entry<String, String> entry: entrys) {
String bookName = entry.getKey(); //书名
//将书名作为目录名
File fileBookName = new File(file + "/" + bookName);
if (!fileBookName.exists()) {
fileBookName.mkdirs();
}
String bookLink = entry.getValue(); //书链接
// System.out.println(bookName + "=" + bookLink);
//构造每一本书的每一个章节对应的url
//问题:如何获取每本书中有多少章?
int capCount = getBookOfCap(bookLink);
for (int i = 1; i < capCount; i++) {
//具体章节的url
String capUrl = bookLink.substring(0, bookLink.length()-5) + "/" + i + ".html";
Map<String, String> aBookAllCap = getBookCap(capUrl);
Set<Map.Entry<String, String>> aBookEntrys = aBookAllCap.entrySet();
for (Map.Entry<String, String> entry_son : aBookEntrys) {
String capName = entry_son.getKey(); //章节名
String capText = entry_son.getValue(); //文章
File fileCapText = new File(fileBookName + "/" + capName + ".txt");
if (!fileCapText.exists()) {
fileCapText.createNewFile();
}
FileWriter fw = new FileWriter(fileCapText);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(capText);
bw.close();
fw.close();
// System.out.println(capName + "=" + capText);
}
}
}
}
//爬所有书
public Map<String, String> getAllBooks() throws IOException {
//用于存储书名和对应的链接
Map<String, String> name_info = new HashMap<String, String>();
String url = "http://www.shicimingju.com/book/";
Document document = Jsoup.connect(url).get(); //网页源代码
Elements all_infos = document.select("h2");
for (Element all_info : all_infos) {
name_info.put(all_info.select("a").text(), all_info.select("a[href]").attr("abs:href"));
}
return name_info;
}
//用于获取章节名和文本信息
public Map<String, String> getBookCap(String url) throws IOException {
//用于存储章节名和文本信息
Map<String, String> name_info = new HashMap<String, String>();
Document document = Jsoup.connect(url).get(); //文本网页源代码
Elements all_infos = document.select(".layui-col-md8");
for (Element all_info : all_infos) {
name_info.put(all_info.select("h1").text(), all_info.select("p").text());
}
return name_info;
}
//获取每个本书中有多少个章节
public int getBookOfCap (String url) throws IOException {
int count = 0;
Document document = Jsoup.connect(url).get();
Elements test = document.select(".book-mulu").select("a");
for (Element element : test) {
count++;
// System.out.println(element.text());
}
return count;
}
}
5.代码中存在的问题
1.本人水平较低,导致每次代码运行时间较长;
2.无法实现自动退出,每次运行都需要在最后提示报错信息后,整段代码才会结束;
3.test2()是用于在测试目录创建时使用的,因为在我第一次创建时,竟然将文件都下载到了/home目录下
4.如果想要在windows上运行的话,只需要将目录名改成对应的就可以了
6.end
由于本人只是实现了最基本的需求,并且本人还是个菜J,所以代码可以有潜在的异常或错误,若是有哪位大佬发现代码中的错误,望请指正。
网友评论