零、写在前面
博主在吃土的大学生活里,最喜欢的就是闲暇时间看一些有丶意思的小说,除去断章,太监,有时候最害怕的就是看到上架感言,又横不下心去充钱,看盗版吧,又都是广告。最后干脆写了个爬虫,爬盗版网站的小说,这大概就是盗中盗吧。
重点声明,支持正版,拒绝盗版。
一、网站分析
1、前期分析
开始爬虫之前,我们先要分析一下,我们需要什么?
小说名字、小说每一章标题、小说每一章的内容。
OK,我们就去找这三个东西。
2、我们以博主常去的盗版网站为例
这种不需要登录就可以查看小说的网站,就是我们要找的。
3、分析网页源代码
我们以这个《元尊》为例,打开它的网页源代码。
我们都知道 HTML 是一门标记语言,找到他的小说题目。
小说名字在 <h1> 标签中,写在小本本上。
明显看出,每一章标题在 <dd> 标签下的 <a> 标签中,正好 <a> 标签的 href 属性有为文表明的每一章的明确地址。写上。
点击<a> 标签的 href 属性进入具体的章节。
正好,这一章的内容就在一个 id 为 content 的 <div> 标签中,写上。
4、思路分析
我们大概分析一下实现的思路,程序访问我们输入的 url,先得到小说名,然后把每一章的名字和具体的 url 存储成一个 List,然后从 List 里一条一条去得到每一章的内容,最后,把得到的写到一个 txt 文件,OK,计划通。
二、代码实现
1、工程构建
IDE的话,我们选择 IDEA,新建一个 maven 工程,在 pom 文件增加 jsoup 依赖。
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
不想用 maven 的话,手动导入 jsoup jar包也是可行的。
好了,项目就搭建好了。
2、代码构建
我们先新建一个存储小说一章的类,Chapter类。
public class Chapter {
private String title; // 章节标题
private String url; // 章节 URL
public Chapter(String title, String url) {
this.title = title;
this.url = url;
}
// 省略 getset 和 toString 代码
…………
}
然后新建一个工具类 NovelUtil,用于实现具体的逻辑。
先写一个小说名的静态变量。
public static String NOVEL_NAME = null;
然后开始写方法:
首先写一个根据 url 获取 Document 的方法。
// 获取 Document
private static Document getDocument(String url) throws IOException {
return Jsoup.connect(url).timeout(5000).get();
}
一个根据得到的 Document 获取小说名字的方法
// 获取小说名字
private static String getNovelName(Document doc) {
return doc.getElementsByTag("h1").text();
}
一个根据 Document 获取一个章节 Chapter 的 List 的方法。
// 获取小说章节列表
private static List<Chapter> getChapterList(Document doc) {
List<Chapter> list = new ArrayList<Chapter>();
Elements e = doc.getElementsByTag("dd");
Iterator<Element> ite = e.iterator();
while (ite.hasNext()) {
Element t = ite.next();
String title = t.child(0).text();
String url = t.child(0).attr("href").split("/")[2]; //处理一下 url
list.add(new Chapter(title,url));
}
return list;
}
处于安全考虑,上面的类我们都设为私有,最后我们再写一个暴露在外的构造小说的方法和一个同样暴露在外的根据 url 获取小说内容的方法。
// 获取小说内容
public static String getContent(String url) throws IOException {
Document doc = Jsoup.connect(url).timeout(5000).get();
return doc.getElementById("content").text();
}
// 构造小说章节集合
public static List<Chapter> constructor(String url)throws IOException {
Document doc = getDocument(url);
NOVEL_NAME = getNovelName(doc);
List<Chapter> list = getChapterList(doc);
return list;
}
好了,工具类就写好了。
最后建立一个运行代码的类,Reptile。我们把 main 方法写在这里。
main 方法主要是把工具类中得到的章节 List 以及小说名字写到一个 txt 文件中。
下面是代码:
public static void main(String[] args) {
String url = "https://www.xbiquge6.com/78_78513/"; // 小说 url
String filepath = "C:\\Users\\97189\\Desktop\\"; // 文件位置
try{
List<Chapter> list = NovelUtil.constructor(url);
if(NovelUtil.NOVEL_NAME != null && list != null){ // 确保得到了
System.out.println("开始爬取" + NovelUtil.NOVEL_NAME);
File file = new File(filepath + NovelUtil.NOVEL_NAME +".txt");
file.createNewFile();// 创建新文件
BufferedWriter out = new BufferedWriter(new FileWriter(file, true));
// 写入小说名字
out.write(NovelUtil.NOVEL_NAME);
out.newLine();
// 写入每一章
for(Chapter chapter : list){
out.write(chapter.getTitle());
out.newLine();
out.write(NovelUtil.getContent(url + chapter.getUrl()));
out.newLine();
System.out.println(chapter.getTitle() + "------爬取结束");
}
out.close();
System.out.println("********爬虫结束*********");
}
}catch (Exception e){
e.printStackTrace();
}
}
3、运行代码
好了,开始运行。
好,生成了
三、优化
上面的代码已经实现了一个简单的 java 爬虫,不过这样的代码还是会至少产生以下几个问题的。
1、连接超时
有些时候会因为我们的网络原因而导致连接超时,不过更多的情况是我们爬取小说的时候,网站发现一个相同的 ip 在不停访问,出于安全考虑,网站将我们的 ip 暂时封禁了一小段时间。
解决的思路有很多,我们可以配置一个 ip 池,让多个合法 ip 交替进行访问。要是不想这么麻烦的话,也可以设置一些标记,让代码实现断点续传的功能。
2、小说排版
最后生成的 txt 文件的排版也许不是我们很想要的,那这个时候,可以稍微修改一下写入的形式,例如每出现一个句号都换行一次。这时候就看你的需求了。