在日常中,我们经常需要浏览网页,阅读一些内容。
但网页中并不是所有内容都是我们所需要的。
毕竟,谁都不想看的好好时突然出现一个“澳门棋牌”。
那么这时我们就可以爬取它的内容。
这里就以大家熟知的 笔*阁为例。
打开笔*阁的首页。
不对,打开一本小说。
这里以《进化的四十六亿重奏》为例(我是挺推荐这本书的,还有,如果可以的话尽量支持正版。)
打开主页,查看源代码,我们可以从其中换取我们需要的一起。
那我们需要什么呢?
那我们就需要明确我们爬取的对象。
1 小说的名称。
2 章节名称。
3 章节内容。
ok,明确了对象后,那我们就需要针对这些对象进行爬取。
首先是小说的名称。
通过观察源代码,我们可以看到:
小说的名称和简介是储存在:
11,12行的标签中的。
小说的目录 和 章节内容是储存在:
标签中的。我们可以将网页的内容全部存入一个字符串数组中。
然后进行比较,确定位置。
然后将我们需要的内容提取出来。
在放入新的文件中。
话不多说,上实例:
public static void main(String[] args){ //确定主页链接 String link = "https://www.biquwx.la/0_376/"; //确定文件存放位置 String path = "/Users/apple/Downloads/test/"; //默认运行一次,当连接不上链接时(也就是出现SSLException异常时),runTime会+1,也就是仔运行一次 int runTimes = 1; for (int runtime=0;runtime<runTimes;runtime++){ try{ //新建URL对象 URL url = new URL(link); //打开连接 URLConnection urlConnection = url.openConnection(); //建立Http的连接 HttpsURLConnection connection = (HttpsURLConnection) urlConnection; //新建流 InputStreamReader isr = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr); //现在网页中的内容已经读进了BufferedReader中。 System.out.println("连接成功!"); //新建一个容器,容器的长度会随内容的增加而增加,不用担心数组越界的问题 ArrayList al = new ArrayList(); //while循环,一直执行直到,line == null时停止,也就是网页内容读完后停止。 while (true){ //新建字符串,用于储存一行的内容 String line = br.readLine(); //如果line == null ,则跳出循环 if (line == null){ break; } //如果line != null,那么把字符串添加进容器中。 else { al.add(line); } } //关闭流 br.close(); isr.close(); //while循环完毕后,网页中的内容也都被放进了容器中 //现在将容器中的内容放入一个字符串数组中 //字符串的长度就为容器的大小 String[] str = new String[al.size()]; for (int i=0;i<str.length;i++){ str[i] = String.valueOf(al.get(i)); } //如果出现乱码,则重新运行 //其实,没多大用,我也不知道为什么会出现乱码。 //但一般等会在运行就好了 //如果有办法的,可以在评论中告诉我,谢谢。 if (!str[0].contains("<!DOCTYPE html")){ System.out.println(str[9]); System.out.println("出现乱码,重新连接中..."); Thread.sleep(3000); runTimes++; continue; } //初始化标题 String name = ""; //初始化简介 String description = ""; /* 通过观察我们可以发现,一行的字符串中,我们所需的全在""中。 那么我们可以,以"为分割符,创建一个字符串数组,然后提取我们所需的。 */ //创建循环,判断我们所需内容 for (int i = 0;i<str.length;i++){ if (str[i].contains("property=\"og:title\"")){ //创建一个字符串数组,以"为分割符 String[] temp = str[i].split("\""); //标题位于这个数组的第4位 name = temp[3]; continue; }else if (str[i].contains("property=\"og:description\"")){ //简介不一样,它不仅占了一行,它占据了<meta property="og:description" content="/>这个标签。 //而随后的标签是<meta property="og:image" 那我们就可以检测这个标签,来作为结束。 //i1是用来计算开始到结束的行数的 int i1 = 1; while (true){ if (str[i+i1].contains("<meta property=\"og:image\"")){ for (int i2 = i;i2<i1+i;i2++){ //因为只有少量字符串拼接,所以我就用了String的+ description += str[i2]; } break; }else i1++; } //现在description不仅包含了简介还包含了标签,所以要像小说名一样操纵一下。 //创建一个字符串数组,以"为分割符 String[] temp = description.split("\""); //标题位于这个数组的第4位 description = temp[3]; } } System.out.printf("小说的名字为:%n" + name + "%n"); System.out.printf( "小说的简介为: %n" + description); System.out.println("--------------"); System.out.println("正在获取章节内容中"); //小说名 和 简介 我们都有了 //现在就是小说章节了 //依旧由观察可知,章节在<div id="list"> 标签中 //而每个章节的前面都会有href 和 title ,我们就从这俩下手 //例:<a href="3102496.html" title="目前细胞的一些数据及第一卷解释">目前细胞的一些数据及第一卷解释</a> //现将包含章节的行提取到一个字符串数组,再进行操纵 //同上依旧先用容器装,在转成字符串数组 ArrayList al2 = new ArrayList(); for (int i = 0;i<str.length;i++){ if (str[i].contains("href") && str[i].contains("title")){ al2.add(str[i]); } } String[] chapter = new String[al2.size()]; for (int i=0;i<chapter.length;i++){ chapter[i] = String.valueOf(al2.get(i)); } //现在已经转完了,那么我们就可以进行操纵了 //创建文件放置目录 File directory = new File(path + name); System.out.println("已创建文件" + directory); for (int i=0;i<chapter.length;i++){ //将一个字符串以"为分割符分割 String[] temp = chapter[i].split("\""); //章节名位于第四位 String chapterName = temp[3]; //章节链接位于第二位 String chapterLink = temp[1]; //首先初始化上,下一章的名称 String nextChapterName = ""; String beforeChapterName = ""; if (i != chapter.length-1){ //获取下一章的名称 String[] temp1 = chapter[i+1].split("\""); nextChapterName = temp1[3]; } if (i != 0){ //获取上一章的名称 String[] temp2 = chapter[i-1].split("\""); beforeChapterName = temp2[3]; } //现在要读取章节中我们需要的内容 //创建流来输入 //新建一个文件,文件名为获得的章节名 File f = new File(path + name + "/" + chapterName + ".html"); System.out.print("正在创建文件" + f + " "); if (!f.exists()){ f.getParentFile().mkdirs(); } try ( FileOutputStream fos = new FileOutputStream(f); PrintWriter pw = new PrintWriter(fos) ){ //新建URL对象 URL url1 = new URL(link + chapterLink); //打开连接 URLConnection urlConnection1 = url1.openConnection(); //建立Http的连接 HttpsURLConnection connection1 = (HttpsURLConnection) urlConnection1; //新建流 InputStreamReader isr1 = new InputStreamReader(connection1.getInputStream(), StandardCharsets.UTF_8); BufferedReader br1 = new BufferedReader(isr1); //现在网页中的内容已经读进了BufferedReader中。 //打开一个一个章节的源码,我们可以看正文部分前面都有空格标识符 所以我们可以从这个下手. //例: 在城市的街道上,所有的虚民都在疯狂地奔跑着,在大地的震颤之下,它们纷纷从建筑之中逃了出来…… //用一个容器,和一个字符串数组就够了 //用StringBuffer更节约性能 ArrayList al3 = new ArrayList(); while (true){ String line = br1.readLine(); if (line == null){ break; }else {al3.add(line);} } String[] content = new String[al3.size()]; for (int i1= 0;i1<al3.size();i1++){ content[i1] = String.valueOf(al3.get(i1)); } StringBuffer sb = new StringBuffer(); for (int i1 = 0;i1<content.length;i1++){ if (content[i1].contains(" ")){ sb.append(content[i1]); } } //我们要创建一个html文件,用于实现方向键换章。 //这里可以读取一个文件模版,但我懒的做了所以直接写这了. pw.println("<!DOCTYPE html>"); pw.println("<html lang=\"en\">"); pw.println("<head>"); pw.println(" <meta charset=\"UTF-8\">"); pw.println(" <title>"+ chapterName + "</title>"); pw.println(" <script>function onDocKeydown(e) {e = e || window.event;if (e.keyCode==39) {"); pw.println(" window.location.href=\"" + directory + "/"+ nextChapterName + ".html" +"\";"); pw.println(" }else if (e.keyCode==37){"); pw.println(" window.location.href=\"" + directory + "/" + beforeChapterName + ".html" + "\";"); pw.println(" }}document.onkeydown = onDocKeydown;</script>"); pw.println("</head>"); pw.println("<body>"); pw.println("<div align=\"center\">"); pw.println(" <h1>"+ chapterName +"</h1></br></br>"); pw.println(sb); pw.println("</div>"); pw.println("</body>"); pw.println("</html>"); System.out.println("文件创建完毕"); } catch (IOException e) { e.printStackTrace(); } } } catch (Exception e) { //如果异常为SSLException,那么跳出循环,在运行一次 if (e instanceof SSLException){ System.out.println("出现异常:未连接成功"); System.out.println("尝试再次运行..."); runTimes++; }e.printStackTrace(); } } }
还是希望支持正版。
有问题放在评论区,若我看到了,会给你尽快回复的。
如果文章内容有什么问题也欢迎指正。
感谢你的阅读