1 #7

Closed
pbxef79cv wants to merge 6 commits from 李娟 into 张冉

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -8,24 +8,47 @@ import com.monke.monkeybook.listener.OnGetChapterListListener;
import java.util.List;
import io.reactivex.Observable;
// 定义了一个名为IEasouBookModel的接口用于规范与书籍相关的各种数据获取操作的方法签名
// 该接口可能会有不同的实现类来具体处理对应的业务逻辑,例如与不同数据源交互获取书籍信息等
public interface IEasouBookModel {
/**
*
*
*
* @param content
* @param page
* @param rankKind
* @return ObservableList<SearchBookBean>
* Observable
*/
Observable<List<SearchBookBean>> searchBook(String content, int page, int rankKind);
/**
*
*
*
* @param bookShelfBean BookShelfBean
*
* @return ObservableBookShelfBean
* Observable
*/
Observable<BookShelfBean> getBookInfo(final BookShelfBean bookShelfBean);
/**
*
*
*
* @param bookShelfBean BookShelfBean
* @param getChapterListListener
*
*/
void getChapterList(final BookShelfBean bookShelfBean, OnGetChapterListListener getChapterListListener);
/**
*
*
*
* @param durChapterUrl URLURL
* @param durChapterIndex 便
* @return ObservableBookContentBeanBean
* Observable
*/
Observable<BookContentBean> getBookContent(final String durChapterUrl, final int durChapterIndex);
}
}

@ -3,21 +3,42 @@ package com.monke.monkeybook.model;
import com.monke.monkeybook.bean.LibraryBean;
import com.monke.monkeybook.bean.SearchBookBean;
import com.monke.monkeybook.cache.ACache;
import //
com.monke.monkeybook.cache.ACache;
import java.util.List;
import io.reactivex.Observable;
// IGxwztvBookModel接口继承自IStationBookModel接口意味着它会继承IStationBookModel中定义的方法
// 同时在此接口中又额外定义了一些与特定业务可能和gxwztv相关相关的书籍数据获取等操作的方法规范
public interface IGxwztvBookModel extends IStationBookModel {
/**
*
*
* @param url URL
* @param page 便
* @return ObservableList<SearchBookBean>
* Observable
*/
Observable<List<SearchBookBean>> getKindBook(String url, int page);
/**
*
*
*
* @param aCache ACache
* 便
* @return ObservableLibraryBeanLibraryBean
* Observable
*/
Observable<LibraryBean> getLibraryData(ACache aCache);
/**
*
*
*
* @param data
* LibraryBean
* @return ObservableLibraryBeanLibraryBean
* Observable便
*/
Observable<LibraryBean> analyLibraryData(String data);
}
}

@ -2,10 +2,21 @@
package com.monke.monkeybook.model;
import com.monke.monkeybook.bean.LocBookShelfBean;
// 导入Java中用于操作文件的类意味着该接口相关操作可能涉及对本地文件的处理比如导入本地书籍文件等
import java.io.File;
import io.reactivex.Observable;
// 定义了IImportBookModel接口该接口主要规范了导入书籍相关操作的方法签名
// 具体的导入书籍逻辑可由实现该接口的类来完成
public interface IImportBookModel {
/**
*
*
* @param book Filetxtepub
*
* @return ObservableLocBookShelfBean
* Observable
*/
Observable<LocBookShelfBean> importBook(File book);
}
}

@ -8,25 +8,53 @@ import com.monke.monkeybook.listener.OnGetChapterListListener;
import java.util.List;
import io.reactivex.Observable;
// IStationBookModel接口定义了一系列与书籍相关操作的方法规范这些操作可能是围绕某个站点Station展开的
// 比如从特定的网络站点获取书籍数据等,具体实现类会按照这些规范来实现对应的业务逻辑
public interface IStationBookModel {
/**
*
*
*
* @param content
*
* @param page
* 便
* @return ObservableList<SearchBookBean>
* Observable
*/
Observable<List<SearchBookBean>> searchBook(String content, int page);
/**
*
*
*
* @param bookShelfBean BookShelfBean
*
* BookShelfBean
* @return ObservableBookShelfBean
* Observable
*/
Observable<BookShelfBean> getBookInfo(final BookShelfBean bookShelfBean);
/**
*
*
*
* @param bookShelfBean BookShelfBean
*
* OnGetChapterListListener
* @param getChapterListListener
*
*/
void getChapterList(final BookShelfBean bookShelfBean, OnGetChapterListListener getChapterListListener);
/**
*
*
*
* @param durChapterUrl URLURL
*
* @param durChapterIndex 便使
*
* @return ObservableBookContentBeanBean
* Observable便
*/
Observable<BookContentBean> getBookContent(final String durChapterUrl, final int durChapterIndex);
}
}

@ -8,28 +8,70 @@ import com.monke.monkeybook.listener.OnGetChapterListListener;
import java.util.List;
import io.reactivex.Observable;
// IWebBookModel接口定义了一系列与网络书籍相关操作的方法规范
// 主要涉及从网络获取书籍各种信息(如书籍详情、目录、章节内容等)以及搜索不同类型书籍的功能,
// 具体的实现类会按照这些方法签名去实现对应的网络交互逻辑
public interface IWebBookModel {
/**
*
*
*
* @param bookShelfBean BookShelfBean
*
* BookShelfBean
* @return ObservableBookShelfBean
* Observable
*/
Observable<BookShelfBean> getBookInfo(final BookShelfBean bookShelfBean);
/**
*
*
*
* @param bookShelfBean BookShelfBean
*
* OnGetChapterListListener
*
* @param getChapterListListener
*
*
*/
void getChapterList(final BookShelfBean bookShelfBean,OnGetChapterListListener getChapterListListener);
/**
*
*
*
* @param durChapterUrl URLURL
*
* @param durChapterIndex 便使
*
* @param tag
* @return ObservableBookContentBeanBean
* Observable便
*/
Observable<BookContentBean> getBookContent(final String durChapterUrl, final int durChapterIndex, String tag);
/**
*
*
*
* @param url URL
*
* @param page 便
* 2
* @return ObservableList<SearchBookBean>
* Observable
*/
Observable<List<SearchBookBean>> getKindBook(String url,int page);
/**
*
*
*
* @param content
*
* @param page
* 便3
* @param tag
* @return ObservableList<SearchBookBean>
* Observable
*/
Observable<List<SearchBookBean>> searchOtherBook(String content,int page,String tag);
}
}

@ -1,7 +1,22 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model;
// IWebContentModel接口定义了一个用于分析书籍内容的方法规范
// 具体的实现类需要按照此规范来实现对书籍内容相关的解析操作,
// 可能是从网络获取到的书籍文本内容等进行特定的分析处理。
public interface IWebContentModel {
/**
*
*
* @param s
*
* @param realUrl URL
* URL
* @return
*
* @throws Exception
*
*/
String analyBookcontent(String s,String realUrl) throws Exception;
}

@ -7,31 +7,48 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class Content17duxsModelImpl implements IWebContentModel{
// 实现IWebContentModel接口的类用于处理特定网站的网页内容解析
public class Content17duxsModelImpl implements IWebContentModel {
// 定义一个表示特定网站的标签这里的值是网站的网址
public static final String TAG = "http://www.17duxs.com";
// 获取Content17duxsModelImpl类的单例实例
public static Content17duxsModelImpl getInstance() {
return new Content17duxsModelImpl();
}
// 私有构造方法,按照单例模式的常见思路,防止外部直接实例化该类,不过此处并没有严格限制单例唯一性相关逻辑
private Content17duxsModelImpl() {
}
// 实现IWebContentModel接口中的方法用于解析书籍内容
// 参数s是待解析的网页内容字符串通常是通过网络请求获取到的网页源码等realUrl参数可能是对应的真实网址也许用于一些后续验证或者相关逻辑
// 该方法可能会抛出异常,调用者需要处理相关异常情况
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup解析传入的网页内容字符串得到一个Document对象方便后续对网页DOM结构进行操作
Document doc = Jsoup.parse(s);
// 通过id为"content"获取对应的文本节点列表
List<TextNode> contentEs = doc.getElementById("content").textNodes();
StringBuilder content = new StringBuilder();
// 遍历获取到的文本节点列表
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容并去除前后空白字符trim方法的作用
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 将文本中的全角空格(&nbsp;对应的字符)和半角空格都替换为空字符串,进行文本内容的格式化清理
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 如果处理后的文本长度大于0说明有有效内容
if (temp.length() > 0) {
// 给有效内容添加两个全角空格("\u3000\u3000" 表示两个全角空格)作为缩进格式
content.append("\u3000\u3000" + temp);
// 如果不是最后一个文本节点,添加换行符,用于区分不同的文本节点内容,使解析后的内容格式更合理
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将处理好的内容以字符串形式返回,最终返回的就是解析并格式化后的书籍内容字符串
return content.toString();
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -8,33 +8,48 @@ import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import java.util.List;
// Content17kModelImpl类实现了IWebContentModel接口用于处理来自17k网站相关内容的解析逻辑
public class Content17kModelImpl implements IWebContentModel {
// 定义一个表示17k网站的标签字符串可能用于标识来源等用途
public static final String TAG = "http://www.17k.com";
// 采用单例模式获取Content17kModelImpl类的唯一实例
public static Content17kModelImpl getInstance() {
return new Content17kModelImpl();
}
// 将构造函数私有化保证外部不能通过new关键字随意创建实例符合单例模式的要求
private Content17kModelImpl() {
}
// 实现IWebContentModel接口中的analyBookcontent方法用于解析书籍内容
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup解析传入的字符串通常是网页内容的HTML字符串构建成Document对象方便后续对HTML文档结构进行操作
Document doc = Jsoup.parse(s);
// 通过id获取HTML文档中id为"chapterContentWapper"的元素,这个元素大概率包含了书籍章节的具体内容
Element all = doc.getElementById("chapterContentWapper");
// 获取该元素下所有子元素中的第一个元素包含的文本节点列表此处假设这个结构符合17k网站内容的布局特点
List<TextNode> contentEs = all.getAllElements().get(0).textNodes();
StringBuilder content = new StringBuilder();
// 遍历文本节点列表
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容,并去除首尾空格
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 替换掉文本中的不间断空格(" ")以及普通空格,进一步清理文本内容
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 如果清理后的文本长度大于0说明有实际内容
if (temp.length() > 0) {
// 给文本添加两个全角空格作为缩进格式("\u3000\u3000"),使其在展示时有一定的排版效果
content.append("\u3000\u3000" + temp);
// 如果不是最后一个文本节点,添加换行符,使不同部分的文本内容分行显示
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将处理好的文本内容以字符串形式返回
return content.toString();
}
}
}

@ -1,15 +1,21 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
import org.jsoup.Jsoup;
// 导入Jsoup库中的Document类用于表示解析后的HTML文档结构
import org.jsoup.nodes.Document;
// 导入Jsoup库中的TextNode类用于表示文本节点方便对HTML中的文本内容进行操作
import org.jsoup.nodes.TextNode;
import java.util.List;
public class Content3dllcModelImpl implements IWebContentModel{
// Content3dllcModelImpl类实现了IWebContentModel接口意味着它需要按照该接口定义的规范来实现相应的方法
public class Content3dllcModelImpl implements IWebContentModel {
// 定义一个静态的常量字符串TAG用于标识相关的网址或者某种特定的标记
// 可能在后续的代码逻辑中用于判断来源或者做相关的分类等用途
public static final String TAG = "http://www.3dllc.cc";
// 它用于获取Content3dllcModelImpl类的实例方便外部代码调用获取该类对象进行相关操作
public static Content3dllcModelImpl getInstance() {
return new Content3dllcModelImpl();
}
@ -17,21 +23,41 @@ public class Content3dllcModelImpl implements IWebContentModel{
private Content3dllcModelImpl() {
}
// 实现IWebContentModel接口中定义的analyBookcontent方法用于分析书籍内容。
// 参数s表示要解析的包含书籍内容的HTML字符串
// realUrl参数表示该内容对应的真实网址
// 该方法声明抛出Exception异常意味着在解析过程中如果出现了比如HTML格式不正确、找不到指定元素等问题时会将异常抛给调用者处理
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup的parse方法将传入的HTML字符串解析成一个Document对象
// 这样后续就可以方便地通过Document对象提供的方法来查找、操作HTML文档中的各种元素和内容
Document doc = Jsoup.parse(s);
// 通过Document对象的.getElementById方法根据元素的id属性值这里是"content")来获取对应的元素下的所有文本节点,
// 推测这里的"content"元素可能就是包含了书籍正文内容的HTML元素获取到的是一个TextNode类型的列表方便后续逐个处理这些文本节点内容
List<TextNode> contentEs = doc.getElementById("content").textNodes();
// 创建一个可变的字符串构建器对象,用于逐步构建最终的书籍内容字符串,相比直接使用普通字符串拼接,这样效率更高,尤其在大量字符串拼接操作时
StringBuilder content = new StringBuilder();
// 遍历获取到的文本节点列表
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容,并去除两端的空白字符(空格、制表符等),得到一个相对比较“纯净”的文本内容字符串
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 将文本内容中的不间断空格(&nbsp;对应的字符)替换为空字符串,同时把普通空格也替换为空字符串,进一步清理文本内容,
// 可能是为了保证最终书籍内容格式的一致性或者符合特定的解析要求
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 如果清理后的文本内容长度大于0说明该文本节点包含有效的文本信息需要将其添加到最终的书籍内容字符串构建器中
if (temp.length() > 0) {
// 在文本内容前添加两个全角空格(\u3000表示全角空格可能是为了符合某种排版或者显示格式要求比如缩进等
// 然后添加到content字符串构建器中
content.append("\u3000\u3000" + temp);
// 如果当前不是最后一个文本节点,就在后面添加一个换行符(\r\n在Windows系统下常见的换行表示方式
// 使得最终的书籍内容文本在显示时不同文本节点的内容能分行显示,更符合阅读和排版习惯
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将构建好的包含书籍内容的字符串构建器对象转换为普通字符串并返回,这个字符串就是经过解析、清理和格式化后的书籍正文内容
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,31 +7,48 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class Content44pqModelImpl implements IWebContentModel{
// Content44pqModelImpl类实现了IWebContentModel接口主要用于处理来自http://www.44pq.net网站相关书籍内容的解析操作
public class Content44pqModelImpl implements IWebContentModel {
// 定义一个表示网站来源的标签字符串这里代表的是http://www.44pq.net可能用于后续区分不同来源等情况
public static final String TAG = "http://www.44pq.net";
// 使用单例模式获取Content44pqModelImpl类的唯一实例确保在程序中只有一个该类的实例存在
public static Content44pqModelImpl getInstance() {
return new Content44pqModelImpl();
}
// 将构造函数私有化防止外部代码通过new关键字随意创建该类的多个实例符合单例模式的设计要求
private Content44pqModelImpl() {
}
// 重写IWebContentModel接口中的analyBookcontent方法用于解析书籍内容
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库的parse方法将传入的字符串通常是对应网页的HTML内容字符串解析为Document对象
// 这样就可以方便地按照HTML文档结构来操作和提取信息了
Document doc = Jsoup.parse(s);
// 通过id获取Document对象中id为"content"的元素包含的所有文本节点,这里假设该"content"元素包含了书籍章节的实际文本内容,
// 具体依赖于http://www.44pq.net网站页面的HTML结构布局
List<TextNode> contentEs = doc.getElementById("content").textNodes();
StringBuilder content = new StringBuilder();
// 遍历获取到的文本节点列表
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容,并去除其首尾的空白字符,使文本更加规整
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 替换掉文本中的不间断空格(" ")以及普通空格,进一步清理文本,去除不必要的空白格式
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 如果经过清理后文本的长度大于0说明该文本节点包含有实际有效的内容
if (temp.length() > 0) {
// 给有效的文本内容添加两个全角空格("\u3000\u3000")作为缩进,以便在最终展示文本时具有更好的排版效果
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是列表中的最后一个节点,就在后面添加换行符("\r\n"),使不同部分的文本内容分行显示,增强可读性
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将处理好的包含书籍章节内容的文本以字符串形式返回,以便在其他地方使用该解析后的内容
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,31 +7,59 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class Content630bookCCModelImpl implements IWebContentModel{
// Content630bookCCModelImpl类实现了IWebContentModel接口主要负责对来自网址为http://www.630book.cc的书籍内容进行解析处理
public class Content630bookCCModelImpl implements IWebContentModel {
// 定义一个公共静态的常量字符串TAG其值为"http://www.630book.cc",用于标识该类所针对处理内容的来源网站。
// 在整个程序架构中,这个标识可能会用于区分不同来源网站的相关逻辑,例如根据不同来源做特定的格式调整或者数据存储等操作
public static final String TAG = "http://www.630book.cc";
// 这是一个静态方法用于获取Content630bookCCModelImpl类的实例采用的是单例模式。
// 通过这种方式确保在整个程序运行过程中,该类只有一个实例存在,有助于节省内存资源,避免因多次创建实例可能导致的资源浪费和数据不一致等问题
public static Content630bookCCModelImpl getInstance() {
return new Content630bookCCModelImpl();
}
// 将构造函数私有化这是实现单例模式的关键步骤之一。私有化构造函数后外部类无法直接通过new关键字来创建该类的实例
// 只能通过上面定义的getInstance方法来获取唯一的实例以此保证单例模式的正确实现
private Content630bookCCModelImpl() {
}
// 重写了IWebContentModel接口中定义的analyBookcontent方法此方法是整个类的核心功能所在用于解析书籍内容。
// 它接收两个参数一个是字符串s通常代表从网页获取到的包含书籍章节内容的HTML格式的字符串另一个是realUrl可能表示该内容对应的真实网址
// 虽然在当前代码中暂时未看到对realUrl参数的进一步使用但在更复杂的场景下可以基于此参数做更多与网址相关的逻辑处理
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的参数s即包含书籍内容的HTML字符串解析为Document对象。
// Document对象是Jsoup对HTML文档结构的一种表示形式通过它可以方便地按照HTML的文档结构如标签、元素、属性等来查找、提取和操作相应的内容
Document doc = Jsoup.parse(s);
// 通过Document对象的getElementById方法根据元素的id属性查找id为"content"的元素,并获取该元素所包含的所有文本节点。
// 这里假设在http://www.630book.cc网站的页面结构中id为"content"的元素存放着需要解析的书籍章节的实际文本内容,
// 如果网站页面结构发生变化,这个假设可能就不成立了,相应的代码逻辑可能需要调整
List<TextNode> contentEs = doc.getElementById("content").textNodes();
StringBuilder content = new StringBuilder();
// 开始遍历获取到的文本节点列表contentEs对其中的每一个文本节点进行处理
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容并调用trim方法去除文本前后的空白字符包括空格、制表符、换行符等使得获取到的文本更加干净、规范
// 避免多余的空白字符影响后续对文本内容的处理以及最终的展示效果
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 使用replaceAll方法替换文本中的不间断空格" ")以及普通空格,进一步清理文本内容,确保文本格式的一致性和简洁性,
// 这样在后续拼接和展示文本时不会出现因空格格式不一致而导致的排版问题
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过清理后的文本长度是否大于0如果大于0说明该文本节点包含有实际有意义的文本内容需要将其添加到最终的解析结果中进行拼接处理
if (temp.length() > 0) {
// 给有效的文本内容添加两个全角空格("\u3000\u3000")作为缩进,使解析后的文本在展示时(比如在阅读软件或者文本编辑工具中)具有更好的排版效果,
// 更符合一般阅读的视觉习惯,方便读者阅读和区分不同段落的内容
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是列表中的最后一个节点,也就是后面还有其他文本节点需要处理,就在当前文本后面添加换行符("\r\n"
// 这样可以让不同部分的文本内容分行显示,清晰地分隔开各段文本,使整个书籍内容的展示更加有条理,易于阅读和理解
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将经过上述一系列处理、拼接后的文本内容以字符串形式返回,这个返回的字符串就是最终解析好的书籍章节内容,
// 可以被其他模块(比如显示模块、存储模块等)获取并进一步使用,以实现完整的书籍阅读或者数据处理等功能
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,31 +7,54 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class Content92zwModelImpl implements IWebContentModel{
// Content92zwModelImpl类实现了IWebContentModel接口其主要目的是处理来自网址为http://www.92zw.la相关书籍内容的解析工作
public class Content92zwModelImpl implements IWebContentModel {
// 定义一个静态的常量字符串TAG用于标识该类所针对处理内容的来源网站这里是http://www.92zw.la
// 或许在后续的代码中可用于区分不同网站来源的相关逻辑处理等情况
public static final String TAG = "http://www.92zw.la";
// 采用单例模式通过此静态方法获取Content92zwModelImpl类的唯一实例。单例模式可以保证在整个应用程序运行期间
// 该类只有一个实例存在,避免重复创建实例造成资源浪费等问题
public static Content92zwModelImpl getInstance() {
return new Content92zwModelImpl();
}
// 将构造函数私有化这是实现单例模式的常见做法外部代码无法直接通过new关键字来创建该类的多个实例
// 只能通过上面的getInstance方法获取唯一实例
private Content92zwModelImpl() {
}
// 重写IWebContentModel接口中的analyBookcontent方法该方法用于对书籍内容进行具体的解析操作
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库的parse方法将传入的参数s通常是从网页获取到的包含书籍内容的HTML字符串解析为Document对象。
// Document对象可以看作是对整个HTML文档结构的一种抽象表示方便后续基于文档结构去查找和提取相关元素及内容
Document doc = Jsoup.parse(s);
// 通过id属性查找Document对象中id为"contents"的元素并获取该元素包含的所有文本节点。这里假设id为"contents"的元素
// 存放着需要解析的书籍章节的实际文本内容这是基于http://www.92zw.la网站页面HTML结构的特定设计若网站结构改变此处可能需调整
List<TextNode> contentEs = doc.getElementById("contents").textNodes();
StringBuilder content = new StringBuilder();
// 遍历获取到的文本节点列表contentEs对每个文本节点进行相应处理
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容并调用trim方法去除文本前后的空白字符空格、制表符、换行符等使得获取到的文本更干净纯粹
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 替换文本中的不间断空格(" ")以及普通空格,进一步清理文本内容,去除多余的空白,避免影响后续文本展示的格式和可读性
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过清理后的文本长度是否大于0如果大于0说明该文本节点包含有实际有意义的内容需要进行后续处理和拼接
if (temp.length() > 0) {
// 给有效的文本内容添加两个全角空格("\u3000\u3000")作为缩进,使解析后的文本在展示时(比如在文本编辑器或者阅读软件中)
// 具有更好的排版效果,看起来更加整齐美观,符合一般阅读的格式习惯
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是列表中的最后一个节点,也就是后面还有其他文本节点需要处理,就在当前文本后面添加换行符("\r\n"
// 这样可以让不同部分的文本内容分行显示,更清晰地分隔开各段文本内容,方便阅读和查看
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将经过上述一系列处理、拼接后的文本内容以字符串形式返回,这个返回的字符串就是解析好的书籍章节内容,
// 可以供其他模块进一步使用,比如显示在阅读界面上等
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,31 +7,63 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentAszwModelImpl implements IWebContentModel{
// ContentAszwModelImpl类实现了IWebContentModel接口其主要职责是对来自网址为http://www.aszw.org的书籍内容进行解析处理操作
public class ContentAszwModelImpl implements IWebContentModel {
// 定义一个公共静态的常量字符串TAG用于标识该类所处理内容对应的网站来源此处为"http://www.aszw.org"。
// 在整个程序的更宏观层面,这个标识可能会在诸如区分不同网站内容、针对不同来源做差异化处理(比如不同的存储策略、格式转换规则等)等场景中发挥作用
public static final String TAG = "http://www.aszw.org";
// 这是一个静态方法遵循单例模式的设计思路用于获取ContentAszwModelImpl类的唯一实例。
// 单例模式确保在程序运行期间,该类只会存在一个实例对象,这样做有助于避免因频繁创建实例而导致的内存资源浪费,同时保证了数据的一致性等,
// 适用于像这种对特定网站内容解析的功能模块,因为通常不需要多个实例来完成同样的解析任务
public static ContentAszwModelImpl getInstance() {
return new ContentAszwModelImpl();
}
// 将构造函数私有化这是实现单例模式的重要手段之一。通过私有化构造函数外部代码就无法直接通过new关键字来创建该类的多个实例了
// 只能按照既定的单例模式规则通过上面定义的getInstance方法获取唯一可用的实例对象
private ContentAszwModelImpl() {
}
// 重写IWebContentModel接口中定义的analyBookcontent方法此方法是整个类实现书籍内容解析功能的核心所在。
// 它接收两个参数:
// 参数s通常是一个包含了从网页上获取到的书籍章节内容的HTML格式字符串也就是要进行解析处理的原始文本数据
// 参数realUrl表示该内容对应的真实网址不过在当前代码中暂时没有看到针对realUrl的具体使用逻辑在更复杂的应用场景下它可以用于例如验证网址合法性、
// 根据网址不同进行额外的处理或者记录等操作
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的功能强大的parse方法将传入的参数s也就是前面提到的包含书籍内容的HTML字符串解析成一个Document对象。
// Document对象在Jsoup框架里代表着对整个HTML文档结构的一种抽象表示借助它可以方便地按照HTML文档固有的结构像通过标签、元素的id或class属性等方式
// 去查找、提取以及操作相应的内容,为后续解析书籍内容奠定基础
Document doc = Jsoup.parse(s);
// 通过Document对象提供的getElementById方法依据元素的id属性去查找id为"contents"的元素,并获取这个元素所包含的所有文本节点。
// 这里基于对http://www.aszw.org网站页面结构的一种假设即认为id为"contents"的元素里面存放着需要解析提取的书籍章节的实际文本内容。
// 倘若该网站后续对页面结构进行了更改,导致这个元素的位置、作用或者内容存储方式发生变化,那么此处的代码逻辑可能就需要相应地进行调整了
List<TextNode> contentEs = doc.getElementById("contents").textNodes();
StringBuilder content = new StringBuilder();
// 开启一个循环对获取到的文本节点列表contentEs进行遍历依次处理其中的每一个文本节点目的是将它们整理、拼接成最终期望的书籍内容文本格式
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前正在处理的文本节点的文本内容并调用trim方法去除该文本前后可能存在的空白字符例如空格、制表符、换行符等
// 这样做是为了让获取到的文本更加纯粹、规范,避免那些多余的空白字符在后续的文本处理和展示过程中造成不必要的干扰,比如影响排版效果等
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 利用replaceAll方法来替换文本中的不间断空格" ")以及普通空格,进一步对文本内容进行清理,使得文本的格式更加统一、简洁,
// 确保在后续将各个文本节点的内容拼接起来后,文本整体的展示效果更加整齐、美观,不会因为空格格式不一致等问题而显得杂乱无章
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述清理操作后的文本长度是否大于0如果大于0就说明这个文本节点包含了实际有意义的内容值得添加到最终解析好的书籍内容当中去
if (temp.length() > 0) {
// 给有效的文本内容添加两个全角空格("\u3000\u3000")作为缩进,这么做是为了让解析后的书籍内容在展示时(比如在阅读类应用程序或者文本编辑工具里呈现时)
// 具备更好的排版效果,更符合人们日常阅读书籍时对于段落格式、缩进等方面的视觉习惯,方便读者清晰地阅读和区分不同部分的内容
content.append("\u3000\u3000" + temp);
// 如果当前正在处理的文本节点不是列表中的最后一个节点,也就是后面还有其他文本节点需要继续处理和拼接,那么就在当前文本内容后面添加一个换行符("\r\n"
// 这样可以让不同部分的文本内容分行显示,使得整个书籍内容在展示上更加有条理、层次分明,更易于阅读者理解和浏览
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将经过前面一系列处理、拼接操作后得到的文本内容以字符串的形式返回,这个返回的字符串就是最终解析完成的、符合预期格式的书籍章节内容,
// 它可以被程序中的其他模块(例如用于展示书籍内容的界面模块、负责将内容存储起来的存储模块等)获取并进一步加以利用,以实现完整的书籍阅读或者相关的数据处理等功能
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,31 +7,63 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentBaishukuModelImpl implements IWebContentModel{
// ContentBaishukuModelImpl类实现了IWebContentModel接口其主要功能是针对来自网址为http://baishuku.com的书籍内容进行解析处理。
public class ContentBaishukuModelImpl implements IWebContentModel {
// 定义一个公共静态的常量字符串TAG其值为"http://baishuku.com",用于标识该类所负责处理内容对应的网站来源。
// 在整个程序架构中,这个标识有可能会用于区分不同网站的内容,例如根据不同来源网站做差异化的逻辑处理,像内容格式调整、存储方式的不同选择等操作。
public static final String TAG = "http://baishuku.com";
// 这是一个静态方法采用单例模式来获取ContentBaishukuModelImpl类的唯一实例。
// 单例模式确保在程序运行期间,该类只会存在一个实例对象,避免了因多次创建实例而可能造成的内存资源浪费,同时保证了在处理相关内容时数据的一致性等情况。
// 对于这种针对特定网站内容解析的功能模块来说,通常不需要多个实例来完成同样的解析工作,所以使用单例模式较为合适。
public static ContentBaishukuModelImpl getInstance() {
return new ContentBaishukuModelImpl();
}
// 将构造函数私有化这是实现单例模式的关键步骤之一。通过把构造函数设置为私有外部代码就无法直接通过new关键字来创建该类的多个实例了
// 只能按照单例模式规定的方式也就是通过上面定义的getInstance方法来获取唯一可用的实例对象以此保证单例模式的正确实现。
private ContentBaishukuModelImpl() {
}
// 重写了IWebContentModel接口中定义的analyBookcontent方法此方法是整个类实现解析书籍内容这一核心功能的关键所在。
// 它接收两个参数:
// 参数s是一个字符串通常代表从http://baishuku.com网站获取到的包含书籍章节内容的HTML格式的字符串也就是要进行解析处理的原始文本数据
// 参数realUrl表示该内容对应的真实网址不过在当前代码中暂时未看到针对realUrl参数有进一步的使用逻辑在更复杂的应用场景下
// 可以基于这个参数做一些额外的操作,比如验证网址的合法性、根据网址不同做不同的处理策略等。
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的参数s即包含书籍内容的HTML字符串解析为一个Document对象。
// Document对象在Jsoup框架里是对HTML文档结构的一种抽象表示通过它可以方便地按照HTML文档的结构特点例如通过元素的id、class属性等方式
// 去查找、提取和操作相应的内容,这为后续解析书籍内容打下了基础。
Document doc = Jsoup.parse(s);
// 通过Document对象提供的getElementById方法按照元素的id属性查找id为"content"的元素,并获取这个元素包含的所有文本节点。
// 这里基于对http://baishuku.com网站页面结构的一种假设即认为id为"content"的元素存放着需要解析的书籍章节的实际文本内容。
// 如果该网站后续对页面结构进行了改变,导致这个元素的相关情况(如位置、内容存储方式等)发生变化,那么此处的代码逻辑可能就需要相应地调整了。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
StringBuilder content = new StringBuilder();
// 开始循环遍历获取到的文本节点列表contentEs目的是对其中的每一个文本节点进行处理最终将它们整理、拼接成符合要求的书籍内容文本格式。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前正在处理的文本节点的文本内容并调用trim方法去除文本前后可能存在的空白字符比如空格、制表符、换行符等
// 这样做可以让获取到的文本更加纯净、规范,避免多余的空白字符对后续文本处理以及最终展示效果产生不良影响,例如造成排版混乱等问题。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 使用replaceAll方法替换文本中的不间断空格" ")以及普通空格,进一步对文本内容进行清理,使得文本格式更加统一、简洁,
// 保证在后续拼接各个文本节点内容后,整个书籍内容的展示效果更加整齐、美观,不会出现因空格格式不一致而显得杂乱的情况。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述清理操作后的文本长度是否大于0如果大于0则说明这个文本节点包含了实际有意义的内容需要将其添加到最终解析好的书籍内容当中。
if (temp.length() > 0) {
// 给有效的文本内容添加两个全角空格("\u3000\u3000")作为缩进,这样做可以让解析后的书籍内容在展示时(比如在阅读应用程序或者文本编辑工具中呈现时)
// 具有更好的排版效果,更符合人们日常阅读书籍时对段落格式、缩进等方面的视觉习惯,便于读者清晰地阅读和区分不同部分的内容。
content.append("\u3000\u3000" + temp);
// 如果当前正在处理的文本节点不是列表中的最后一个节点,也就是后面还有其他文本节点需要继续处理和拼接,
// 那么就在当前文本内容后面添加一个换行符("\r\n"),使得不同部分的文本内容能够分行显示,让整个书籍内容在展示上更有条理、层次分明,更易于阅读者理解和浏览。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将经过前面一系列处理、拼接操作后得到的文本内容以字符串的形式返回,这个返回的字符串就是最终解析完成的、符合预期格式的书籍章节内容,
// 它可以被程序中的其他模块(例如用于展示书籍内容的界面模块、负责存储书籍内容的存储模块等)获取并进一步加以利用,以实现完整的书籍阅读或者相关的数据处理等功能。
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,31 +7,70 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentBxwx9ModelImpl implements IWebContentModel{
// ContentBxwx9ModelImpl类实现了IWebContentModel接口其主要用途是对来自网址为http://www.bxwx9.org的书籍内容进行解析处理操作。
public class ContentBxwx9ModelImpl implements IWebContentModel {
// 定义一个公共静态的常量字符串TAG其值被设定为"http://www.bxwx9.org"该TAG用于标识此解析类所对应的内容来源网站。
// 在整个程序的更大架构中,这个标识可以用于区分不同来源网站的内容,比如针对不同网站的内容采取不同的后续处理逻辑,像存储格式的调整、展示样式的适配等情况。
public static final String TAG = "http://www.bxwx9.org";
// 这是一个遵循单例模式设计的静态方法用于获取ContentBxwx9ModelImpl类的唯一实例。
// 单例模式确保在程序运行期间,此类只会存在一个实例对象,这样做能够避免因重复创建实例而可能导致的内存资源浪费,同时保障在处理相关书籍内容时数据的一致性等方面的要求。
// 对于专门针对特定网站书籍内容解析的功能模块而言,通常一个实例就足以完成相应的解析任务,所以采用单例模式是比较合适的选择。
public static ContentBxwx9ModelImpl getInstance() {
return new ContentBxwx9ModelImpl();
}
// 将构造函数私有化这是实现单例模式的重要手段之一。通过把构造函数设置为私有属性外部代码就无法直接通过使用new关键字来创建该类的多个实例了。
// 外部只能按照单例模式所规定的方式也就是通过调用上面定义的getInstance方法来获取唯一可用的实例对象以此保证单例模式能够正确地得以实现。
private ContentBxwx9ModelImpl() {
}
// 重写了IWebContentModel接口中定义的analyBookcontent方法该方法是整个ContentBxwx9ModelImpl类实现解析书籍内容这一核心功能的关键所在。
// 它接收两个参数:
// 参数s是一个字符串类型的变量通常表示从http://www.bxwx9.org网站获取到的包含书籍章节内容的HTML格式的字符串也就是后续要进行解析处理的原始文本数据
// 参数realUrl同样是字符串类型它代表该内容对应的真实网址不过在当前代码的现有逻辑中暂时没有看到针对这个realUrl参数有进一步的具体使用情况
// 但在更为复杂的应用场景下,这个参数可以被用于诸如验证网址的合法性、根据不同网址执行不同的解析策略或者记录相关网址信息等操作。
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的功能强大的parse方法将传入的参数s即包含书籍内容的HTML字符串解析成为一个Document对象。
// 在Jsoup框架里Document对象是对HTML文档结构进行抽象表示的一种重要形式借助它能够方便地依据HTML文档自身的结构特点例如通过元素的id、class属性等方式
// 去查找、提取以及操作对应的内容,这为后续深入解析书籍内容搭建了良好的基础。
Document doc = Jsoup.parse(s);
// 通过Document对象提供的便捷方法getElementById按照元素的id属性去查找id为"content"的元素,并获取这个元素所包含的所有文本节点。
// 此处基于对http://www.bxwx9.org网站页面结构的一种既有假设即认为id为"content"的这个元素存放着需要解析的书籍章节的实际文本内容。
// 然而,如果该网站后续对自身的页面结构进行了调整修改,导致这个元素在页面中的位置、其所存储内容的方式等方面发生了变化,
// 那么此处的代码逻辑就很可能需要相应地做出适应性的调整改变了。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
StringBuilder content = new StringBuilder();
// 开启一个循环对获取到的文本节点列表contentEs进行逐一的遍历操作目的在于对其中每一个文本节点都按照既定的规则进行处理
// 最终将它们合理地整理、拼接成为符合预期格式的书籍内容文本。
for (int i = 0; i < contentEs.size(); i++) {
// 首先获取当前正在处理的文本节点所包含的文本内容然后调用trim方法去除该文本前后可能存在的空白字符这些空白字符可能包含空格、制表符、换行符等等
// 通过这样的操作可以使得获取到的文本更加纯净、规范,从而避免多余的空白字符对后续文本的处理流程以及最终的展示效果产生不良的影响,
// 例如可能会造成文本排版出现混乱无序的情况等。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 接着使用Java中的replaceAll方法来对文本中的不间断空格" ")以及普通空格进行替换操作,进一步对文本内容进行清理优化,
// 以此让文本的格式变得更加统一、简洁,进而保证在后续把各个文本节点的内容拼接起来之后,整个书籍内容在展示的时候能够更加整齐、美观,
// 不会由于空格格式不一致等问题而显得杂乱无章、影响阅读体验。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 对经过上述一系列清理操作后的文本进行长度判断如果其长度大于0那就意味着这个文本节点中包含了实际有意义的内容
// 这样的文本内容是需要被添加到最终解析好的书籍内容当中去的。
if (temp.length() > 0) {
// 针对有实际意义的文本内容,给它添加两个全角空格("\u3000\u3000")作为缩进,这么做的好处是可以让解析后的书籍内容在展示时(比如在阅读类的应用程序或者文本编辑工具中呈现时),
// 具备更好的排版效果,更加贴合人们日常阅读书籍时对于段落格式、缩进等方面的视觉习惯,方便读者能够更加清晰地阅读和区分不同部分的文本内容。
content.append("\u3000\u3000" + temp);
// 如果当前正在处理的文本节点并非是列表中的最后一个节点,也就是说后面还有其他的文本节点需要继续进行处理和拼接操作,
// 那么就在当前文本内容的后面添加一个换行符("\r\n"),通过这样的方式可以使得不同部分的文本内容能够分行显示,
// 让整个书籍内容在展示方面更加有条理、层次分明,更有助于阅读者去理解和浏览其中的内容。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 经过前面循环中对各个文本节点的处理、拼接等一系列操作之后,将最终得到的文本内容以字符串的形式返回。
// 这个返回的字符串就是经过完整解析、并且符合预期格式要求的书籍章节内容了,它可以被程序中的其他相关模块(例如用于展示书籍内容的界面模块、
// 负责将书籍内容进行存储的存储模块等)获取并进一步加以利用,以此来实现完整的书籍阅读或者其他相关的数据处理等功能。
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.ErrorAnalyContentManager;
@ -8,37 +8,75 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
// ContentCommendModelImpl类从命名来看可能是用于通用的内容推荐相关模型实现但从代码功能上更像是内容解析相关可能命名不太准确需结合更完整的业务场景判断
public class ContentCommendModelImpl {
// 采用单例模式通过此静态方法获取ContentCommendModelImpl类的唯一实例。
// 单例模式可以保证在整个应用程序运行期间,该类只有一个实例存在,避免重复创建实例造成资源浪费等问题,适用于一些全局唯一的功能模块场景。
public static ContentCommendModelImpl getInstance() {
return new ContentCommendModelImpl();
}
// 将构造函数私有化这是实现单例模式的常见做法外部代码无法直接通过new关键字来创建该类的多个实例
// 只能通过上面的getInstance方法获取唯一实例以此确保类实例的唯一性和对其创建过程的控制。
private ContentCommendModelImpl() {
}
// analyBookcontent方法用于解析书籍内容并将解析结果设置到BookContentBean对象中返回同时处理可能出现的解析异常情况。
// 它接收三个参数:
// - bookContentBean一个BookContentBean类型的对象可能用于承载解析后的书籍内容以及相关状态信息等最终也会将解析结果设置到这个对象中返回。
// - s一个字符串通常代表从网页获取到的包含书籍章节内容的HTML格式的字符串也就是要进行解析处理的原始文本数据。
// - realUrl表示该内容对应的真实网址可能在记录错误信息、判断网站支持情况等方面会用到这个参数。
public BookContentBean analyBookcontent(BookContentBean bookContentBean, String s, String realUrl) throws Exception {
// 调用ErrorAnalyContentManager的单例实例的writeNewErrorUrl方法将当前尝试解析的真实网址记录下来
// 可能用于后续统计哪些网址出现过解析问题或者进行错误分析等相关操作具体功能取决于ErrorAnalyContentManager类的实现逻辑。
ErrorAnalyContentManager.getInstance().writeNewErrorUrl(realUrl);
try{
try {
// 使用Jsoup库的parse方法将传入的参数s即包含书籍内容的HTML字符串解析为Document对象。
// Document对象可以看作是对整个HTML文档结构的一种抽象表示方便后续基于文档结构去查找和提取相关元素及内容
// 例如通过元素的id、class等属性查找特定元素。
Document doc = Jsoup.parse(s);
// 通过id属性查找Document对象中id为"content"的元素,并获取该元素包含的所有文本节点。
// 这里假设id为"content"的元素存放着需要解析的书籍章节的实际文本内容不过这依赖于对应网页的HTML结构设计
// 若网页结构改变,此处获取内容的逻辑可能会失效,需要相应调整。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
StringBuilder content = new StringBuilder();
// 遍历获取到的文本节点列表contentEs对每个文本节点进行相应处理目的是整理并拼接出最终的书籍内容文本。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容并调用trim方法去除文本前后的空白字符空格、制表符、换行符等使得获取到的文本更干净纯粹
// 避免多余空白字符影响后续文本处理和展示效果,比如排版等方面。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 替换文本中的不间断空格(" ")以及普通空格,进一步清理文本内容,去除不必要的空白,使文本格式更统一,便于后续拼接展示。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过清理后的文本长度是否大于0如果大于0说明该文本节点包含有实际有意义的内容需要进行后续处理和拼接。
if (temp.length() > 0) {
// 给有效的文本内容添加两个全角空格("\u3000\u3000")作为缩进,使解析后的文本在展示时(比如在文本编辑器或者阅读软件中)
// 具有更好的排版效果,看起来更加整齐美观,符合一般阅读的格式习惯。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是列表中的最后一个节点,也就是后面还有其他文本节点需要处理,就在当前文本后面添加换行符("\r\n"
// 这样可以让不同部分的文本内容分行显示,更清晰地分隔开各段文本内容,方便阅读和查看。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将处理好的包含书籍章节内容的文本设置到bookContentBean对象的durCapterContent属性中
// 这样通过这个对象就可以将解析后的内容传递到其他需要使用的地方了。
bookContentBean.setDurCapterContent(content.toString());
}catch (Exception e){
} catch (Exception e) {
// 如果在解析过程中出现异常,先打印异常的堆栈信息,方便进行调试和排查问题,了解异常发生的具体位置和原因。
e.printStackTrace();
// 设置bookContentBean对象的durCapterContent属性为一段提示信息告知用户当前站点暂时不支持解析
// 并提供了反馈的QQ联系方式以及承诺解决问题的时效等信息用于向用户说明情况。
bookContentBean.setDurCapterContent(realUrl.substring(0, realUrl.indexOf('/', 8)) + "站点暂时不支持解析请反馈给Monke QQ:1105075896,半小时内解决,超级效率的程序员");
// 同时将bookContentBean对象的right属性设置为false表示解析出现了问题该内容可能是不正确或者不完全的
// 其他使用这个对象的地方可以根据这个属性来判断解析结果的有效性。
bookContentBean.setRight(false);
}
// 最后将处理后的bookContentBean对象返回这个对象中包含了解析后的书籍内容以及相关状态信息
// 可供其他模块进一步使用,比如展示给用户或者进行后续的业务逻辑处理等。
return bookContentBean;
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,31 +7,73 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentDhzwModelImpl implements IWebContentModel{
// ContentDhzwModelImpl类实现了IWebContentModel接口其主要功能是针对来自网址为http://www.dhzw.org的书籍内容进行解析处理以提取出可用的文本内容。
public class ContentDhzwModelImpl implements IWebContentModel {
// 定义一个公共静态的常量字符串TAG其值设定为"http://www.dhzw.org",用于标识该类所处理内容对应的网站来源。
// 在整个程序架构中,这个标识可用于区分不同网站的内容,例如后续根据不同来源网站采取不同的处理逻辑,像针对特定网站内容进行格式转换、存储策略调整等操作。
public static final String TAG = "http://www.dhzw.org";
// 这是一个静态方法采用单例模式来获取ContentDhzwModelImpl类的唯一实例。
// 单例模式能确保在程序运行期间,该类只会存在一个实例对象,避免了因多次创建实例而可能引发的内存资源浪费问题,同时保证了在解析相关书籍内容时数据的一致性等情况。
// 对于这种专门针对特定网站内容解析的功能模块而言,通常只需一个实例就能完成相应的解析任务,所以单例模式是比较合适的设计选择。
public static ContentDhzwModelImpl getInstance() {
return new ContentDhzwModelImpl();
}
// 将构造函数私有化这是实现单例模式的关键步骤之一。通过把构造函数设置为私有外部代码就无法直接通过new关键字来创建该类的多个实例了
// 只能按照单例模式规定的方式也就是通过上面定义的getInstance方法来获取唯一可用的实例对象以此保障单例模式的正确实现。
private ContentDhzwModelImpl() {
}
// 重写IWebContentModel接口中定义的analyBookcontent方法此方法是整个类实现解析书籍内容这一核心功能的关键所在。
// 它接收两个参数:
// 参数s是一个字符串通常代表从http://www.dhzw.org网站获取到的包含书籍章节内容的HTML格式的字符串也就是要进行解析处理的原始文本数据
// 参数realUrl表示该内容对应的真实网址不过在当前代码的现有逻辑中暂时未看到针对realUrl参数有进一步的具体使用情况
// 但在更为复杂的应用场景下,该参数可用于验证网址合法性、根据网址不同执行不同的解析策略或者记录相关网址信息等操作。
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的参数s即包含书籍内容的HTML字符串解析为一个Document对象。
// 在Jsoup框架里Document对象是对HTML文档结构进行抽象表示的一种重要形式借助它能够方便地依据HTML文档自身的结构特点例如通过元素的id、class属性等方式
// 去查找、提取以及操作对应的内容,这为后续深入解析书籍内容搭建了良好的基础。
Document doc = Jsoup.parse(s);
// 通过Document对象提供的便捷方法getElementById按照元素的id属性去查找id为"BookText"的元素,并获取这个元素所包含的所有文本节点。
// 此处基于对http://www.dhzw.org网站页面结构的一种既有假设即认为id为"BookText"的这个元素存放着需要解析的书籍章节的实际文本内容。
// 倘若该网站后续对自身的页面结构进行了调整修改,导致这个元素在页面中的位置、其所存储内容的方式等方面发生了变化,
// 那么此处的代码逻辑就很可能需要相应地做出适应性的调整改变了。
List<TextNode> contentEs = doc.getElementById("BookText").textNodes();
StringBuilder content = new StringBuilder();
// 开启一个循环对获取到的文本节点列表contentEs进行逐一的遍历操作目的在于对其中每一个文本节点都按照既定的规则进行处理
// 最终将它们合理地整理、拼接成为符合预期格式的书籍内容文本。
for (int i = 0; i < contentEs.size(); i++) {
// 首先获取当前正在处理的文本节点所包含的文本内容然后调用trim方法去除该文本前后可能存在的空白字符这些空白字符可能包含空格、制表符、换行符等等
// 通过这样的操作可以使得获取到的文本更加纯净、规范,从而避免多余的空白字符对后续文本的处理流程以及最终的展示效果产生不良的影响,
// 例如可能会造成文本排版出现混乱无序的情况等。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 接着使用Java中的replaceAll方法来对文本中的不间断空格" ")以及普通空格进行替换操作,进一步对文本内容进行清理优化,
// 以此让文本的格式变得更加统一、简洁,进而保证在后续把各个文本节点的内容拼接起来之后,整个书籍内容在展示的时候能够更加整齐、美观,
// 不会由于空格格式不一致等问题而显得杂乱无章、影响阅读体验。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 额外的处理:将文本中的全角空格(“ ”)也进行替换去除,进一步清理文本内容,确保文本格式更加规范,此处同样是基于对网站内容格式特点的一种处理需求假设。
temp = temp.replaceAll(" ", "");
// 对经过上述一系列清理操作后的文本进行长度判断如果其长度大于0那就意味着这个文本节点中包含了实际有意义的内容
// 这样的文本内容是需要被添加到最终解析好的书籍内容当中去的。
if (temp.length() > 0) {
content.append("\u3000\u3000" + contentEs.get(i).text().trim().replaceAll(" ",""));
// 针对有实际意义的文本内容,给它添加两个全角空格("\u3000\u3000")作为缩进,这么做的好处是可以让解析后的书籍内容在展示时(比如在阅读类的应用程序或者文本编辑工具中呈现时),
// 具备更好的排版效果,更加贴合人们日常阅读书籍时对于段落格式、缩进等方面的视觉习惯,方便读者能够更加清晰地阅读和区分不同部分的文本内容。
// 这里注意在添加缩进时再次获取了当前文本节点处理后的文本内容调用了一次text().trim().replaceAll(" ","")),可能代码存在一些重复逻辑,可考虑优化。
content.append("\u3000\u3000" + contentEs.get(i).text().trim().replaceAll(" ", ""));
// 如果当前正在处理的文本节点并非是列表中的最后一个节点,也就是说后面还有其他的文本节点需要继续进行处理和拼接操作,
// 那么就在当前文本内容的后面添加一个换行符("\r\n"),通过这样的方式可以使得不同部分的文本内容能够分行显示,
// 让整个书籍内容在展示方面更加有条理、层次分明,更有助于阅读者去理解和浏览其中的内容。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 经过前面循环中对各个文本节点的处理、拼接等一系列操作之后,将最终得到的文本内容以字符串的形式返回。
// 这个返回的字符串就是经过完整解析、并且符合预期格式要求的书籍章节内容了,它可以被程序中的其他相关模块(例如用于展示书籍内容的界面模块、
// 负责将书籍内容进行存储的存储模块等)获取并进一步加以利用,以此来实现完整的书籍阅读或者其他相关的数据处理等功能。
return content.toString();
}
}
}

@ -1,4 +1,4 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
// Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.model.impl;
import com.monke.monkeybook.model.IWebContentModel;
@ -7,32 +7,70 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
// ContentEasouModelImpl类实现了IWebContentModel接口其主要职责是对来自网址为http://book.easou.com的书籍内容进行解析处理操作。
public class ContentEasouModelImpl implements IWebContentModel {
// 定义一个公共静态的常量字符串TAG其值被设定为"http://book.easou.com"该TAG用于标识此解析类所对应的内容来源网站。
// 在整个程序的更大架构中,这个标识可以用于区分不同来源网站的内容,比如针对不同网站的内容采取不同的后续处理逻辑,像存储格式的调整、展示样式的适配等情况。
public static final String TAG = "http://book.easou.com";
public static ContentEasouModelImpl getInstance(){
// 这是一个遵循单例模式设计的静态方法用于获取ContentEasouModelImpl类的唯一实例。
// 单例模式确保在程序运行期间,此类只会存在一个实例对象,这样做能够避免因重复创建实例而可能导致的内存资源浪费,同时保障在处理相关书籍内容时数据的一致性等方面的要求。
// 对于专门针对特定网站书籍内容解析的功能模块而言,通常一个实例就足以完成相应的解析任务,所以采用单例模式是比较合适的选择。
public static ContentEasouModelImpl getInstance() {
return new ContentEasouModelImpl();
}
private ContentEasouModelImpl(){
// 将构造函数私有化这是实现单例模式的重要手段之一。通过把构造函数设置为私有属性外部代码就无法直接通过使用new关键字来创建该类的多个实例了。
// 外部只能按照单例模式所规定的方式也就是通过调用上面定义的getInstance方法来获取唯一可用的实例对象以此保证单例模式能够正确地得以实现。
private ContentEasouModelImpl() {
}
// 重写了IWebContentModel接口中定义的analyBookcontent方法该方法是整个ContentEasouModelImpl类实现解析书籍内容这一核心功能的关键所在。
// 它接收两个参数:
// 参数s是一个字符串类型的变量通常表示从http://book.easou.com网站获取到的包含书籍章节内容的HTML格式的字符串也就是后续要进行解析处理的原始文本数据
// 参数realUrl同样是字符串类型它代表该内容对应的真实网址不过在当前代码的现有逻辑中暂时没有看到针对这个realUrl参数有进一步的具体使用情况
// 但在更为复杂的应用场景下,这个参数可以被用于诸如验证网址的合法性、根据不同网址执行不同的解析策略或者记录相关网址信息等操作。
@Override
public String analyBookcontent(String s, String realUrl) throws Exception{
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的功能强大的parse方法将传入的参数s即包含书籍内容的HTML字符串解析成为一个Document对象。
// 在Jsoup框架里Document对象是对HTML文档结构进行抽象表示的一种重要形式借助它能够方便地依据HTML文档自身的结构特点例如通过元素的id、class属性等方式
// 去查找、提取以及操作对应的内容,这为后续深入解析书籍内容搭建了良好的基础。
Document doc = Jsoup.parse(s);
// 通过Document对象提供的便捷方法getElementsByClass按照元素的class属性去查找class为"show"的元素并获取查找到的第一个该类元素通过调用get(0)
// 然后获取这个元素所包含的所有文本节点。这里基于对http://book.easou.com网站页面结构的一种既有假设即认为class为"show"的这个元素(第一个匹配的)存放着需要解析的书籍章节的实际文本内容。
// 然而,如果该网站后续对自身的页面结构进行了调整修改,导致这个元素在页面中的位置、其所存储内容的方式等方面发生了变化,
// 那么此处的代码逻辑就很可能需要相应地做出适应性的调整改变了。
List<TextNode> contentEs = doc.getElementsByClass("show").get(0).textNodes();
StringBuilder content = new StringBuilder();
// 开启一个循环对获取到的文本节点列表contentEs进行逐一的遍历操作目的在于对其中每一个文本节点都按照既定的规则进行处理
// 最终将它们合理地整理、拼接成为符合预期格式的书籍内容文本。
for (int i = 0; i < contentEs.size(); i++) {
// 首先获取当前正在处理的文本节点所包含的文本内容然后调用trim方法去除该文本前后可能存在的空白字符这些空白字符可能包含空格、制表符、换行符等等
// 通过这样的操作可以使得获取到的文本更加纯净、规范,从而避免多余的空白字符对后续文本的处理流程以及最终的展示效果产生不良的影响,
// 例如可能会造成文本排版出现混乱无序的情况等。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 接着使用Java中的replaceAll方法来对文本中的不间断空格" ")以及普通空格进行替换操作,进一步对文本内容进行清理优化,
// 以此让文本的格式变得更加统一、简洁,进而保证在后续把各个文本节点的内容拼接起来之后,整个书籍内容在展示的时候能够更加整齐、美观,
// 不会由于空格格式不一致等问题而显得杂乱无章、影响阅读体验。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 对经过上述一系列清理操作后的文本进行长度判断如果其长度大于0那就意味着这个文本节点中包含了实际有意义的内容
// 这样的文本内容是需要被添加到最终解析好的书籍内容当中去的。
if (temp.length() > 0) {
// 针对有实际意义的文本内容,给它添加两个全角空格("\u3000\u3000")作为缩进,这么做的好处是可以让解析后的书籍内容在展示时(比如在阅读类的应用程序或者文本编辑工具中呈现时),
// 具备更好的排版效果,更加贴合人们日常阅读书籍时对于段落格式、缩进等方面的视觉习惯,方便读者能够更加清晰地阅读和区分不同部分的文本内容。
content.append("\u3000\u3000" + temp);
// 如果当前正在处理的文本节点并非是列表中的最后一个节点,也就是说后面还有其他的文本节点需要继续进行处理和拼接操作,
// 那么就在当前文本内容的后面添加一个换行符("\r\n"),通过这样的方式可以使得不同部分的文本内容能够分行显示,
// 让整个书籍内容在展示方面更加有条理、层次分明,更有助于阅读者去理解和浏览其中的内容。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 经过前面循环中对各个文本节点的处理、拼接等一系列操作之后,将最终得到的文本内容以字符串的形式返回。
// 这个返回的字符串就是经过完整解析、并且符合预期格式要求的书籍章节内容了,它可以被程序中的其他相关模块(例如用于展示书籍内容的界面模块、
// 负责将书籍内容进行存储的存储模块等)获取并进一步加以利用,以此来实现完整的书籍阅读或者其他相关的数据处理等功能。
return content.toString();
}
}

@ -8,34 +8,81 @@ import org.jsoup.nodes.TextNode;
import java.util.List;
/**
*
* ContentFuheishuModelImplIWebContentModelTAGhttp://fuheishu.com
*
*/
public class ContentFuheishuModelImpl implements IWebContentModel{
public class ContentFuheishuModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处为http://fuheishu.com
public static final String TAG = "http://fuheishu.com";
public static ContentFuheishuModelImpl getInstance(){
/**
* ContentFuheishuModelImpl
* 便使
*
* @return ContentFuheishuModelImpl
*/
public static ContentFuheishuModelImpl getInstance() {
return new ContentFuheishuModelImpl();
}
private ContentFuheishuModelImpl(){
/**
*
* getInstance
*/
private ContentFuheishuModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* HTML
*
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception{
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库的parse方法将传入的原始字符串通常是HTML内容解析为一个Document对象
// 方便后续通过DOM操作来提取需要的书籍内容相关节点等信息。
Document doc = Jsoup.parse(s);
String bookId = realUrl.substring(realUrl.indexOf("/",21)+1,realUrl.indexOf(".html"));
List<TextNode> contentTNs = doc.getElementById("content"+bookId).textNodes();
// 从realUrl中提取书籍的唯一标识bookId这里通过截取网址字符串的方式获取。
// 先找到从第21个字符之后此处假设网址的特定结构可能需要根据实际网址情况调整出现的第一个'/'的位置,
// 然后从该位置的下一个字符开始,截取到'.html'之前的字符串作为bookId用于后续定位书籍内容所在的HTML元素。
String bookId = realUrl.substring(realUrl.indexOf("/", 21) + 1, realUrl.indexOf(".html"));
// 通过获取到的bookId在解析后的Document对象中查找id为"content" + bookId的元素并获取其包含的所有文本节点TextNode列表。
// 这些文本节点预计包含了实际的书籍内容信息,后续将对这些节点进行处理以提取出完整的书籍内容。
List<TextNode> contentTNs = doc.getElementById("content" + bookId).textNodes();
// 创建一个StringBuilder对象用于高效地拼接处理后的书籍内容字符串避免频繁创建新的字符串对象带来的性能开销。
StringBuilder stringBuilder = new StringBuilder();
for(int i=0;i<contentTNs.size();i++){
// 遍历获取到的所有文本节点contentTNs对每个节点进行处理。
for (int i = 0; i < contentTNs.size(); i++) {
// 获取当前文本节点的文本内容,并去除两端的空白字符(空格、制表符、换行等),得到较为纯净的文本内容。
String temp = contentTNs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步去除文本内容中的不间断空格(&nbsp;对应的Unicode字符这里替换为空字符串以及普通空格
// 使文本内容的格式更加规整,去除多余的空白间隔。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 如果处理后的文本内容长度大于0说明该文本节点包含有效的内容信息将其添加到stringBuilder中。
if (temp.length() > 0) {
// 在每个有效文本内容前添加两个全角空格(\u3000作为缩进格式使书籍内容排版更美观符合一般的阅读格式要求。
stringBuilder.append("\u3000\u3000" + temp);
// 如果当前不是最后一个文本节点,就在后面添加一个换行符(\r\n实现不同文本节点内容之间的换行
// 让书籍内容在显示时更有条理,便于阅读。
if (i < contentTNs.size() - 1) {
stringBuilder.append("\r\n");
}
}
}
// 将处理好的书籍内容字符串通过StringBuilder的toString方法转换为普通字符串并返回该结果
// 该字符串即为最终解析并格式化后的书籍内容,可以用于后续的展示、存储等操作。
return stringBuilder.toString();
}
}

@ -7,31 +7,79 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentKewaishuModelImpl implements IWebContentModel{
/**
* ContentKewaishuModelImplIWebContentModelTAGhttp://www.kewaishu.org
*
*/
public class ContentKewaishuModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处为http://www.kewaishu.org
public static final String TAG = "http://www.kewaishu.org";
/**
* ContentKewaishuModelImpl
* 便使
*
*
* @return ContentKewaishuModelImpl
*/
public static ContentKewaishuModelImpl getInstance() {
return new ContentKewaishuModelImpl();
}
/**
*
* getInstance
*/
private ContentKewaishuModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
*
*
* @param s HTML
* @param realUrl 便
* @return 使
* @throws Exception 使JsoupHTMLHTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库的parse方法将传入的原始字符串通常为HTML格式的网页内容解析为一个Document对象
// 这样后续就能基于DOM文档对象模型结构方便地查找和操作网页中的各种元素提取我们需要的书籍内容相关部分。
Document doc = Jsoup.parse(s);
// 首先通过id查找网页中id为"content"的元素,然后在这个元素内查找所有的<p>标签元素,
// 这里取第一个<p>标签元素通过get(0)获取并获取其包含的所有文本节点TextNode列表。
// 假设这里认为书籍内容是包含在这个特定的<p>标签元素中的文本节点集合里,通过这样的方式定位到需要解析的文本内容所在位置。
List<TextNode> contentEs = doc.getElementById("content").getElementsByTag("p").get(0).textNodes();
// 创建一个StringBuilder对象用于高效地拼接处理后的书籍内容字符串相比于直接使用字符串相加操作
// StringBuilder可以减少创建多个临时字符串对象带来的性能开销更适合用于频繁的字符串拼接场景。
StringBuilder content = new StringBuilder();
// 遍历获取到的所有文本节点contentEs对每个节点进行处理和拼接操作。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容,并去除两端的空白字符(如空格、制表符、换行等),得到相对纯净、无多余空白的文本内容。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步替换文本内容中的不间断空格Unicode编码为“&nbsp;”对应的字符,这里替换为空字符串)以及普通空格,
// 以此进一步清理文本,让文本格式更加规整,避免出现多余的空白间隔影响阅读体验。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 如果处理后的文本内容长度大于0说明该文本节点包含有效的信息将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效文本内容前添加两个全角空格(\u3000作为缩进格式使最终呈现的书籍内容排版更美观符合一般阅读时的段落格式要求。
content.append("\u3000\u3000" + temp);
// 如果当前不是最后一个文本节点,就在添加的文本内容后面添加一个换行符(\r\n
// 实现不同文本节点内容之间的换行,使书籍内容在展示时更有条理,便于阅读和区分不同的段落内容。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将经过处理和拼接后存储在StringBuilder中的书籍内容字符串转换为普通字符串通过调用toString方法来实现
// 并将最终整理好的书籍内容字符串返回,该字符串可用于后续的展示、存储等操作,比如在阅读软件中显示书籍文本内容等。
return content.toString();
}
}
}

@ -7,31 +7,78 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentLeduwoModelImpl implements IWebContentModel{
/**
* ContentLeduwoModelImplIWebContentModelTAGhttp://leduwo.com
* 便使
*/
public class ContentLeduwoModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址这里对应的是http://leduwo.com方便知晓该解析逻辑针对的具体站点。
public static final String TAG = "http://leduwo.com";
/**
* ContentLeduwoModelImpl
* 便
*
* @return ContentLeduwoModelImpl
*/
public static ContentLeduwoModelImpl getInstance() {
return new ContentLeduwoModelImpl();
}
/**
*
* getInstance
*/
private ContentLeduwoModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'便
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// 通过创建Document对象后续就能基于DOM文档对象模型的结构方便地在网页内容中查找、定位和操作各种元素从而提取出我们需要的书籍内容相关部分。
Document doc = Jsoup.parse(s);
// 通过id查找网页中id为"content"的元素并获取该元素包含的所有文本节点TextNode列表。
// 这里假设网页中书籍的具体内容是存放在id为"content"的这个元素内的文本节点当中,以此方式来定位要解析的文本内容所在位置。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
// 创建一个StringBuilder对象用于高效地拼接处理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销特别适合在需要多次进行字符串拼接的场景下使用。
StringBuilder content = new StringBuilder();
// 遍历获取到的所有文本节点contentEs对每个文本节点进行相应的处理和拼接操作。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容,并去除其两端的空白字符(例如空格、制表符、换行等),得到相对纯净、没有多余空白的文本内容。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步替换文本内容中的不间断空格Unicode编码对应的“&nbsp;”字符,这里将其替换为空字符串)以及普通空格,
// 以此进一步清理文本内容,使得文本的格式更加规整,避免出现多余的空白间隔影响阅读体验和内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 如果处理后的文本内容长度大于0说明该文本节点包含有效的信息此时将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效文本内容前添加两个全角空格(\u3000作为缩进格式这样能让最终呈现的书籍内容排版更加美观符合一般阅读时对于段落格式的要求。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是最后一个文本节点,就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,使得书籍内容在展示时更有条理,方便阅读者区分不同的段落内容。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 将经过处理和拼接后存储在StringBuilder中的书籍内容字符串转换为普通字符串通过调用toString方法来实现这一转换操作
// 最后将整理好的书籍内容字符串返回,这个返回的字符串可以用于后续的各种操作,比如在阅读软件中展示书籍的文本内容、将内容存储到本地文件等。
return content.toString();
}
}
}

@ -7,31 +7,86 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentLewen8ModelImpl implements IWebContentModel{
/**
* ContentLewen8ModelImplIWebContentModelTAGhttp://www.lewen8.com
*
*/
public class ContentLewen8ModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于明确标识该类所对应的网站地址这里设定为http://www.lewen8.com
// 方便在整个程序架构中知晓此解析逻辑具体是针对哪个站点的书籍内容进行操作的。
public static final String TAG = "http://www.lewen8.com";
/**
* ContentLewen8ModelImpl
* 便
*
*
* @return ContentLewen8ModelImpl
*/
public static ContentLewen8ModelImpl getInstance() {
return new ContentLewen8ModelImpl();
}
/**
*
* getInstance
*/
private ContentLewen8ModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'便
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型的结构来表示HTML文档通过它可以方便地按照HTML元素的层级关系、标签名称、属性等特征来查找、定位和操作网页中的各种元素
// 进而帮助我们提取出期望的书籍内容相关部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"content"的元素,
// 然后获取该元素所包含的所有文本节点TextNode列表。这里隐含的假设是网页中书籍的实际内容是存放在id为"content"这个元素内部的文本节点当中,
// 通过这种方式来确定要解析的文本内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相较于直接使用字符串相加操作使用StringBuilder可以显著减少创建多个临时字符串对象所带来的性能开销
// 尤其适用于像这种需要频繁进行字符串拼接的场景,能提升程序的运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 首先获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包括空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更清晰地处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符在HTML中常用于表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的常规要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件系统等。
return content.toString();
}
}
}

@ -7,32 +7,90 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentPbtxtModelImpl implements IWebContentModel{
/**
* ContentPbtxtModelImplIWebContentModelTAGhttp://www.pbtxt.com
* 便
*/
public class ContentPbtxtModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处明确为http://www.pbtxt.com
// 便于在整个程序体系里知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.pbtxt.com";
public static ContentPbtxtModelImpl getInstance(){
/**
* ContentPbtxtModelImpl
*
*
*
* @return ContentPbtxtModelImpl
*/
public static ContentPbtxtModelImpl getInstance() {
return new ContentPbtxtModelImpl();
}
private ContentPbtxtModelImpl(){
/**
*
* getInstance
*/
private ContentPbtxtModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'HTML
*
* @param s HTML
* @param realUrl HTML
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception{
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它能够按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
String bookId = realUrl.substring(realUrl.indexOf("/",21)+1,realUrl.indexOf(".html"));
List<TextNode> contentTNs = doc.getElementById("content"+bookId).textNodes();
// 从'realUrl'中提取书籍的唯一标识bookId此处是通过截取网址字符串的方式来获取。
// 先找到从第21个字符之后这里假设网址具有特定的结构实际应用中可能需要根据具体网址的格式特点来调整出现的第一个'/'的位置,
// 然后从该位置的下一个字符开始,截取到'.html'之前的字符串作为bookId后续将利用这个bookId来定位书籍内容所在的具体HTML元素。
String bookId = realUrl.substring(realUrl.indexOf("/", 21) + 1, realUrl.indexOf(".html"));
// 通过获取到的bookId在解析后的Document对象中查找id为"content" + bookId的元素并获取该元素包含的所有文本节点TextNode列表。
// 这里的假设是书籍内容存放在这个特定id的元素内的文本节点当中通过这样的操作来精准定位到需要解析的书籍内容所在位置。
List<TextNode> contentTNs = doc.getElementById("content" + bookId).textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder stringBuilder = new StringBuilder();
for(int i=0;i<contentTNs.size();i++){
// 开始遍历获取到的所有文本节点contentTNs针对每个文本节点进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentTNs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,方便后续进一步处理和展示。
String temp = contentTNs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到stringBuilder这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
stringBuilder.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentTNs.size() - 1) {
stringBuilder.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return stringBuilder.toString();
}
}

@ -7,31 +7,86 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentPpxsModelImpl implements IWebContentModel{
/**
* ContentPpxsModelImplIWebContentModelTAGhttp://www.ppxs.net
* 便
*/
public class ContentPpxsModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址这里明确为http://www.ppxs.net
// 使得在整个程序架构中能够清晰知晓此解析逻辑是专门针对该站点的书籍内容而设置的。
public static final String TAG = "http://www.ppxs.net";
/**
* ContentPpxsModelImpl
* 便
*
*
* @return ContentPpxsModelImpl
*/
public static ContentPpxsModelImpl getInstance() {
return new ContentPpxsModelImpl();
}
/**
*
* getInstance
*/
private ContentPpxsModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串通常为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型来表示HTML文档通过它可以依据HTML元素的各种属性如id、class等、标签名称以及层级关系等
// 方便地查找、定位并操作网页中的各种元素,进而帮助我们准确地提取出与书籍内容相关的文本部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"booktext"的元素,
// 然后获取该元素包含的所有文本节点TextNode列表。这里假设书籍内容存放在id为"booktext"的这个元素内的文本节点当中,
// 通过这种方式来定位要解析的书籍内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("booktext").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 与直接使用字符串相加操作相比使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 尤其适用于像这样需要多次进行字符串拼接的场景,有助于提高程序的运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使最终呈现的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -7,31 +7,86 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentQulaModelImpl implements IWebContentModel{
/**
* ContentQulaModelImplIWebContentModelTAGhttp://www.qu.la
* 便
*/
public class ContentQulaModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该类所对应的网站地址这里明确指定为http://www.qu.la
// 这样在整个程序架构中就能清楚地知道这个解析类是针对该网站的书籍内容进行处理的。
public static final String TAG = "http://www.qu.la";
/**
* ContentQulaModelImpl
* 便
*
*
* @return ContentQulaModelImpl
*/
public static ContentQulaModelImpl getInstance() {
return new ContentQulaModelImpl();
}
/**
*
* getInstance
*/
private ContentQulaModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它可以按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"content"的元素,
// 然后获取该元素包含的所有文本节点TextNode列表。这里假设网页中书籍的实际内容是存放在id为"content"这个元素内的文本节点当中,
// 通过这种方式来定位要解析的文本内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相较于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -7,31 +7,88 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentQzreadModelImpl implements IWebContentModel{
/**
* ContentQzreadModelImplIWebContentModelTAGhttp://www.qzread.com
* HTML
*/
public class ContentQzreadModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于明确标识该类所对应的网站地址此处设定为http://www.qzread.com
// 这样在整个程序架构中就能清晰知晓此解析逻辑是专门针对该站点的书籍内容进行操作的。
public static final String TAG = "http://www.qzread.com";
/**
* ContentQzreadModelImpl
* 便
*
*
* @return ContentQzreadModelImpl
*/
public static ContentQzreadModelImpl getInstance() {
return new ContentQzreadModelImpl();
}
/**
*
* getInstance
*/
private ContentQzreadModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'便
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型的结构来表示HTML文档通过它可以按照HTML元素的层级关系、标签名称、属性等特征来查找、定位和操作网页中的各种元素
// 进而帮助我们提取出期望的书籍内容相关部分。
Document doc = Jsoup.parse(s);
// 首先通过getElementsByClass方法依据元素的class属性查找网页中class为"txt"的所有元素,
// 然后取其中的第一个元素通过get(0)获取这是因为假设符合该class的元素中第一个包含了我们要解析的书籍内容相关结构。
// 在这个获取到的元素基础上再通过getElementsByTag方法依据标签名称查找其中的第一个<p>标签元素同样通过get(0)获取),
// 这里又假设书籍内容在这个特定的<p>标签元素内部。最后获取该<p>标签元素所包含的所有文本节点TextNode列表
// 通过这样层层查找的方式来确定要解析的文本内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementsByClass("txt").get(0).getElementsByTag("p").get(0).textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相较于直接使用字符串相加操作使用StringBuilder可以显著减少创建多个临时字符串对象所带来的性能开销
// 尤其适用于像这种需要频繁进行字符串拼接的场景,能提升程序的运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 首先获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包括空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更清晰地处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符在HTML中常用于表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -7,31 +7,86 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentRanwenaModelImpl implements IWebContentModel{
/**
* ContentRanwenaModelImplIWebContentModelTAGhttp://www.ranwena.com
* 便
*/
public class ContentRanwenaModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于明确标识该内容解析类对应的网站地址这里设定为http://www.ranwena.com
// 使得在整个程序架构中能够清楚知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.ranwena.com";
/**
* ContentRanwenaModelImpl
* 便
*
*
* @return ContentRanwenaModelImpl
*/
public static ContentRanwenaModelImpl getInstance() {
return new ContentRanwenaModelImpl();
}
/**
*
* getInstance
*/
private ContentRanwenaModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* idHTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般为HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档借助它能够按照HTML元素的各种属性如id、class等、标签名称以及元素间的层级关系等
// 方便地查找、定位并操作网页中的各种元素,从而准确提取出与书籍内容相关的文本部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"content"的元素,
// 然后获取该元素所包含的所有文本节点TextNode列表。这里假设书籍内容存放在id为"content"这个元素内的文本节点当中,
// 通过这种方式来定位要解析的书籍内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder能够避免频繁创建新的字符串对象所带来的性能开销
// 特别适用于像这样需要多次进行字符串拼接的场景,有助于提高程序的运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,方便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使最终呈现的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -6,23 +6,83 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
public class ContentShulouModelImpl implements IWebContentModel{
/**
* ContentShulouModelImplIWebContentModelTAGhttp://www.shulou.cc
*
*/
public class ContentShulouModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处明确为http://www.shulou.cc
// 便于在整个程序体系里知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.shulou.cc";
public static ContentShulouModelImpl getInstance(){
/**
* ContentShulouModelImpl
*
*
*
* @return ContentShulouModelImpl
*/
public static ContentShulouModelImpl getInstance() {
return new ContentShulouModelImpl();
}
private ContentShulouModelImpl(){
/**
*
* getInstance
*/
private ContentShulouModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return HTML使
* @throws Exception 使JsoupHTMLHTML
*
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception{
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它能够按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"content"的元素。
// 这里假设网页中书籍的实际内容主要存放在这个id为"content"的元素内通过这种方式来定位要解析的书籍内容在整个HTML文档中的大致位置。
Element contentE = doc.getElementById("content");
// 将获取到的包含书籍内容相关信息的Element对象转换为字符串形式方便后续进行一系列的文本替换操作来清理和格式化内容。
String contentString = contentE.toString();
contentString = contentString.replaceAll(" ", "").replaceAll("\n", "").replaceAll("&nbsp;", "").replaceAll("<divid=\"content\">", "").replaceAll("</div>", "").replaceAll("<p></p>","").replaceAll("<br><br>", "\r\n\u3000\u3000");
// 以下是一系列的字符串替换操作目的是去除内容字符串中不需要的空格、换行符、HTML中的特殊空格占位符&nbsp;以及一些不必要的HTML标签等
// 让文本内容更加纯净、格式更加规整,更符合阅读展示的要求。
// 去除所有普通空格
contentString = contentString.replaceAll(" ", "");
// 去除所有换行符
contentString = contentString.replaceAll("\n", "");
// 去除HTML中的不间断空格&nbsp;
contentString = contentString.replaceAll("&nbsp;", "");
// 去除开头的 <div id="content"> 标签,因为已经提取出了其内部的内容,这个标签在最终展示的文本中不需要出现。
contentString = contentString.replaceAll("<divid=\"content\">", "");
// 去除结尾的 </div> 标签同理这也是不需要在最终文本内容里呈现的HTML标签部分。
contentString = contentString.replaceAll("</div>", "");
// 去除空的 <p> 标签(<p></p>),这类空标签对书籍内容展示没有实际意义,去除可使内容更简洁。
contentString = contentString.replaceAll("<p></p>", "");
// 将连续的两个 <br> 标签通常用于HTML中表示换行替换为 \r\n\u3000\u3000
// 其中 \r\n 表示换行,\u3000\u3000 表示添加两个全角空格作为缩进,使文本排版更美观,符合阅读习惯。
contentString = contentString.replaceAll("<br><br>", "\r\n\u3000\u3000");
// 在整个处理后的内容字符串开头添加两个全角空格(\u3000\u3000作为缩进进一步优化排版效果使书籍内容看起来更整齐、美观。
contentString = "\u3000\u3000" + contentString;
// 返回经过上述一系列处理后的书籍内容字符串,该字符串可用于后续在阅读应用等场景中的展示、存储等操作。
return contentString;
}
}
}

@ -7,31 +7,86 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentSnwx8ModelImpl implements IWebContentModel{
/**
* ContentSnwx8ModelImplIWebContentModelTAGhttp://www.snwx8.com
*
*/
public class ContentSnwx8ModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址这里明确为http://www.snwx8.com
// 便于在整个程序架构中知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.snwx8.com";
/**
* ContentSnwx8ModelImpl
* 便
*
*
* @return ContentSnwx8ModelImpl
*/
public static ContentSnwx8ModelImpl getInstance() {
return new ContentSnwx8ModelImpl();
}
/**
*
* getInstance
*/
private ContentSnwx8ModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它可以按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"BookText"的元素,
// 然后获取该元素包含的所有文本节点TextNode列表。这里假设网页中书籍的实际内容是存放在id为"BookText"这个元素内的文本节点当中,
// 通过这种方式来定位要解析的文本内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("BookText").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相较于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
for(int i=0;i<contentEs.size();i++){
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
if(temp.length()>0){
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -7,31 +7,86 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentSuimengModelImpl implements IWebContentModel{
/**
* ContentSuimengModelImplIWebContentModelTAGhttp://www.suimeng.la
* 便
*/
public class ContentSuimengModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该类所对应的网站地址这里明确指定为http://www.suimeng.la
// 这样在整个程序架构中就能清楚地知道这个解析类是针对该网站的书籍内容进行处理的。
public static final String TAG = "http://www.suimeng.la";
/**
* ContentSuimengModelImpl
* 便
*
*
* @return ContentSuimengModelImpl
*/
public static ContentSuimengModelImpl getInstance() {
return new ContentSuimengModelImpl();
}
/**
*
* getInstance
*/
private ContentSuimengModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它可以按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"ccontent"的元素,
// 然后获取该元素包含的所有文本节点TextNode列表。这里假设网页中书籍的实际内容是存放在id为"ccontent"这个元素内的文本节点当中,
// 通过这种方式来定位要解析的文本内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("ccontent").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相较于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -8,33 +8,85 @@ import org.jsoup.nodes.TextNode;
import java.util.List;
/**
*
* ContentSyzwwModelImplIWebContentModelTAGhttp://www.syzww.net
*
*/
public class ContentSyzwwModelImpl implements IWebContentModel{
public class ContentSyzwwModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处明确为http://www.syzww.net
// 便于在整个程序体系中知晓此解析逻辑是针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.syzww.net";
/**
* ContentSyzwwModelImpl
* 便
*
*
* @return ContentSyzwwModelImpl
*/
public static ContentSyzwwModelImpl getInstance() {
return new ContentSyzwwModelImpl();
}
/**
*
* getInstance
*/
private ContentSyzwwModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它能够按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"content"的元素,
// 然后获取该元素包含的所有文本节点TextNode列表。这里假设网页中书籍的实际内容是存放在id为"content"这个元素内的文本节点当中,
// 通过这种方式来定位要解析的文本内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -7,34 +7,89 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
/**
* ContentVodtwModelImplIWebContentModelTAGhttp://www.vodtw.com
* 便
*/
public class ContentVodtwModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处明确为http://www.vodtw.com
// 便于在整个程序体系里知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.vodtw.com";
/**
* ContentVodtwModelImpl
*
*
*
* @return ContentVodtwModelImpl
*/
public static ContentVodtwModelImpl getInstance() {
return new ContentVodtwModelImpl();
}
/**
*
* getInstance
*/
private ContentVodtwModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它能够按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"BookText"的元素。
// 这里假设网页中书籍的实际内容主要存放在这个id为"BookText"的元素内通过这种方式来定位要解析的书籍内容在整个HTML文档中的大致位置。
Element contentE = doc.getElementById("BookText");
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder能够避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景,有助于提高程序的运行效率。
StringBuilder content = new StringBuilder();
// 通过获取到的contentE元素使用getElementsByTag方法依据标签名称"p"来查找其内部所有的<p>标签元素,
// 并将这些元素存储在Elements集合中这里假设书籍内容是分布在这些<p>标签内的文本信息,后续将从这些<p>标签元素中提取具体文本内容。
Elements contentEs = contentE.getElementsByTag("p");
// 开始遍历获取到的所有<p>标签元素contentEs针对每个元素依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前<p>标签元素的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,方便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该<p>标签元素包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前<p>标签元素不是遍历过程中的最后一个元素,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同<p>标签元素内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -7,31 +7,86 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.TextNode;
import java.util.List;
public class ContentWxguanModelImpl implements IWebContentModel{
/**
* ContentWxguanModelImplIWebContentModelTAGhttp://www.wxguan.com
* 便
*/
public class ContentWxguanModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于明确标识该内容解析类对应的网站地址这里设定为http://www.wxguan.com
// 便于在整个程序架构中知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.wxguan.com";
/**
* ContentWxguanModelImpl
* 便
*
*
* @return ContentWxguanModelImpl
*/
public static ContentWxguanModelImpl getInstance() {
return new ContentWxguanModelImpl();
}
/**
*
* getInstance
*/
private ContentWxguanModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'便
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它可以按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 进而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"content"的元素,
// 然后获取该元素包含的所有文本节点TextNode列表。这里假设网页中书籍的实际内容是存放在id为"content"这个元素内的文本节点当中,
// 通过这种方式来定位要解析的文本内容在整个HTML文档中的具体位置。
List<TextNode> contentEs = doc.getElementById("content").textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相较于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有文本节点contentEs针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -6,32 +6,87 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
/**
* ContentXqingdouCCModelImplIWebContentModelTAGhttp://www.xqingdou.cc
* 便
*/
public class ContentXqingdouCCModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处明确为http://www.xqingdou.cc
// 便于在整个程序体系里知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.xqingdou.cc";
/**
* ContentXqingdouCCModelImpl
*
*
*
* @return ContentXqingdouCCModelImpl
*/
public static ContentXqingdouCCModelImpl getInstance() {
return new ContentXqingdouCCModelImpl();
}
/**
*
* getInstance
*/
private ContentXqingdouCCModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception{
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它能够按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 首先通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"chapter_content"的元素,
// 这里假设书籍内容所在的区域是在这个id为"chapter_content"的元素内部。
// 然后在此元素基础上再使用getElementsByTag方法依据标签名称"p"来查找其内部所有的<p>标签元素,
// 并将这些<p>标签元素存储在Elements集合中通常会假设书籍的具体文本内容是分布在这些<p>标签当中,后续将从这些元素中提取具体的文本内容。
Elements contentEs = doc.getElementById("chapter_content").getElementsByTag("p");
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有<p>标签元素contentEs针对每个元素依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前<p>标签元素的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,方便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该<p>标签元素包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前<p>标签元素不是遍历过程中的最后一个元素,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同<p>标签元素内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -6,32 +6,87 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
/**
* ContentXqingdouModelImplIWebContentModelTAGhttp://www.xqingdou.com
* HTML
*/
public class ContentXqingdouModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型对应的网站地址这里明确指定为http://www.xqingdou.com
// 便于在整个程序架构中知晓此解析逻辑是专门针对该站点书籍内容而设计的。
public static final String TAG = "http://www.xqingdou.com";
/**
* ContentXqingdouModelImpl
* 便
*
*
* @return ContentXqingdouModelImpl
*/
public static ContentXqingdouModelImpl getInstance() {
return new ContentXqingdouModelImpl();
}
/**
*
* getInstance
*/
private ContentXqingdouModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* ID
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception{
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串通常为HTML格式的网页内容解析成一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档借助它可以依据HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 首先通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"chapter_content"的元素,
// 这里假设书籍内容所在的主要区域是在这个id为"chapter_content"的元素内部,这是定位书籍文本内容大致位置的第一步。
// 接着,在找到的"chapter_content"元素基础上调用getElementsByTag方法依据标签名称"p"来查找该元素内部所有的<p>标签元素,
// 并将这些<p>标签元素存储在Elements集合中通常认为书籍的具体文本内容是分布在这些<p>标签里面的,后续将从这些元素中提取具体文本内容进行整理。
Elements contentEs = doc.getElementById("chapter_content").getElementsByTag("p");
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相较于直接使用字符串相加操作使用StringBuilder能避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
// 开始遍历获取到的所有<p>标签元素contentEs针对每个元素依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentEs.size(); i++) {
// 获取当前<p>标签元素的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,以便后续更清晰地处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该<p>标签元素包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前<p>标签元素不是遍历过程中的最后一个元素,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同<p>标签元素内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -9,52 +9,125 @@ import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import java.util.List;
public class ContentYb3ModelImpl implements IWebContentModel{
/**
* ContentYb3ModelImplIWebContentModelTAGhttp://www.yb3.cc
*
* 便
*/
public class ContentYb3ModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处明确为http://www.yb3.cc
// 便于在整个程序体系里知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://www.yb3.cc";
/**
* ContentYb3ModelImpl
*
*
*
* @return ContentYb3ModelImpl
*/
public static ContentYb3ModelImpl getInstance() {
return new ContentYb3ModelImpl();
}
/**
*
* getInstance
*/
private ContentYb3ModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
*
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它能够按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"content"的元素,
// 这里假设书籍内容所在的主要区域是在这个id为"content"的元素内部,这是定位书籍文本内容大致位置的第一步。
Element contentE = doc.getElementById("content");
// 获取id为"content"的元素下包含的所有文本节点TextNode列表尝试第一种提取书籍内容的方式
// 即直接从该元素下的文本节点中获取文本内容,后续会判断这些文本节点是否符合要求来进行相应处理。
List<TextNode> contentTextNodes = contentE.textNodes();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
if(contentTextNodes!=null && contentTextNodes.size()>0){
// 首先判断获取到的文本节点列表是否不为空且包含至少一个文本节点,说明可以尝试从这些文本节点中提取内容。
if (contentTextNodes!= null && contentTextNodes.size() > 0) {
// 开始遍历获取到的所有文本节点contentTextNodes针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int i = 0; i < contentTextNodes.size(); i++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 再判断处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (contentTextNodes.get(i).text().trim().length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + contentTextNodes.get(i).text().trim());
// 如果当前文本节点不是遍历过程中的最后一个文本节点,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同文本节点内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentTextNodes.size() - 1) {
content.append("\r\n");
}
}
}
}else{
} else {
// 如果通过第一种方式(直接从文本节点获取内容)没有获取到有效内容,说明网页结构可能不太一样,
// 则尝试另一种方式获取id为"content"的元素的所有子元素children通过遍历这些子元素来提取可能存在的书籍内容文本。
Elements contentEs = contentE.children();
// 开始遍历这些子元素contentEs尝试从每个子元素中提取和整理书籍内容文本。
for (int i = 0; i < contentEs.size(); i++) {
if (contentEs.get(i).text().toString().trim().replaceAll(" ","").length() > 0) {
// 获取当前子元素的文本内容转换为字符串后调用trim方法去除两端空白字符再将其中的全角空格Unicode编码对应的“ ”字符替换为空字符串
// 然后判断处理后的文本内容长度是否大于0如果大于0则说明该子元素可能包含有实际有效的信息需要进一步处理来提取其中的书籍内容文本。
if (contentEs.get(i).text().toString().trim().replaceAll(" ", "").length() > 0) {
// 获取当前子元素下包含的所有文本节点TextNode列表尝试从这些文本节点中提取更细化的书籍内容文本信息。
List<TextNode> tempTextNodes = contentEs.get(i).textNodes();
if(tempTextNodes!=null && tempTextNodes.size()>0){
// 判断获取到的文本节点列表是否不为空且包含至少一个文本节点,如果是,则进行相应的文本提取和整理操作。
if (tempTextNodes!= null && tempTextNodes.size() > 0) {
// 开始遍历当前子元素下的所有文本节点tempTextNodes针对每个文本节点依次进行相应的处理和拼接操作以构建最终的书籍内容字符串。
for (int j = 0; j < tempTextNodes.size(); j++) {
// 获取当前文本节点的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
String temp = tempTextNodes.get(j).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该文本节点包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 添加一个换行符(\r\n实现不同文本节点内容之间的换行让书籍内容在展示时更有条理。
content.append("\r\n");
}
}
}else{
content.append("\u3000\u3000" + contentEs.get(i).text().trim().replaceAll(" ",""));
} else {
// 如果当前子元素下没有文本节点(或者文本节点列表为空),但该子元素本身的文本内容经过前面处理后还有效,
// 则直接将其文本内容去除全角空格后添加到content这个StringBuilder对象中并添加一个换行符\r\n实现换行效果。
content.append("\u3000\u3000" + contentEs.get(i).text().trim().replaceAll(" ", ""));
content.append("\r\n");
}
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -6,31 +6,88 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class ContentZhulangModelImpl implements IWebContentModel{
/**
* ContentZhulangModelImplIWebContentModelTAGhttp://book.zhulang.com
*
*/
public class ContentZhulangModelImpl implements IWebContentModel {
// 定义一个静态常量TAG用于标识该内容解析模型所对应的网站地址此处明确为http://book.zhulang.com
// 便于在整个程序体系里知晓此解析逻辑是专门针对该站点的书籍内容而设计的。
public static final String TAG = "http://book.zhulang.com";
/**
* ContentZhulangModelImpl
*
*
*
* @return ContentZhulangModelImpl
*/
public static ContentZhulangModelImpl getInstance() {
return new ContentZhulangModelImpl();
}
/**
*
* getInstance
*/
private ContentZhulangModelImpl() {
}
/**
* IWebContentModelanalyBookcontent
* 's'HTML
* 'realUrl'
*
* @param s HTML
* @param realUrl
* @return 使
* @throws Exception 使JsoupHTMLHTML
* HTML
*/
@Override
public String analyBookcontent(String s, String realUrl) throws Exception {
// 使用Jsoup库提供的parse方法将传入的原始字符串一般是HTML格式的网页内容解析为一个Document对象。
// Document对象基于DOM文档对象模型结构来表示HTML文档通过它能够按照HTML元素的标签、属性、层级关系等特征方便地查找、定位和操作网页中的各种元素
// 从而帮助我们准确地提取出与书籍内容相关的部分。
Document doc = Jsoup.parse(s);
// 通过调用Document对象的getElementById方法依据元素的id属性查找网页中id为"read-content"的元素,
// 然后获取该元素的所有子元素children并将这些子元素存储在Elements集合中这里假设书籍内容是分布在这些子元素当中
// 通过后续遍历这些子元素来提取具体的文本内容。
Elements contentEs = doc.getElementById("read-content").children();
// 创建一个StringBuilder对象用于高效地拼接整理后的书籍内容字符串。
// 相比于直接使用字符串相加操作使用StringBuilder可以避免频繁创建新的字符串对象所带来的性能开销
// 特别适合在像这样需要多次进行字符串拼接的场景下使用,有助于提升程序运行效率。
StringBuilder content = new StringBuilder();
for (int i = 3; i < contentEs.size()-1; i++) {
// 开始遍历获取到的子元素contentEs不过这里注意起始索引为3结束索引为contentEs.size() - 1
// 说明可能跳过了前面几个特定的元素(可能是网页中一些非书籍正文内容的元素,比如标题、导航栏之类的相关元素),
// 同时也排除了最后一个元素(可能有一些特殊格式或者不属于正文内容的元素),仅对中间认为是书籍正文内容的子元素进行处理。
for (int i = 3; i < contentEs.size() - 1; i++) {
// 获取当前子元素的文本内容然后调用trim方法去除文本内容两端的空白字符包含空格、制表符、换行等
// 得到相对纯净、没有多余空白干扰的文本内容,方便后续更好地进行处理和展示。
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ","").replaceAll(" ","");
// 进一步对处理后的文本内容进行清理将其中的不间断空格Unicode编码对应的“&nbsp;”字符常用于HTML中表示空格占位等情况以及普通空格都替换为空字符串
// 这样可以让文本的格式更加规整,避免因多余的空白间隔而影响阅读体验以及内容展示的美观性。
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
// 判断经过上述处理后的文本内容长度是否大于0如果大于0则说明该子元素包含有实际有效的信息需要将其添加到content这个StringBuilder对象中。
if (temp.length() > 0) {
// 在每个有效的文本内容前面添加两个全角空格(\u3000作为缩进格式使得最终呈现出来的书籍内容在排版上更加美观
// 更符合一般阅读时对于段落格式的要求,便于读者区分不同的内容段落。
content.append("\u3000\u3000" + temp);
// 如果当前子元素不是遍历过程中的最后一个子元素,那么就在添加的文本内容后面添加一个换行符(\r\n
// 以此实现不同子元素内容之间的换行,让书籍内容在展示时更有条理,方便读者清晰地阅读和理解。
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
// 最后通过调用StringBuilder对象的toString方法将经过一系列处理和拼接后存储在其中的书籍内容字符串转换为普通字符串格式
// 并将这个整理好的书籍内容字符串返回,该返回值可以用于后续的各种操作,比如在阅读应用程序中展示给用户查看、保存到本地文件等。
return content.toString();
}
}
}

@ -33,32 +33,61 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
* GxwztvBookModelImplMBaseModelImplIGxwztvBookModelTAGhttp://www.gxwztv.com
* HTML
* RxJava
*/
public class GxwztvBookModelImpl extends MBaseModelImpl implements IGxwztvBookModel {
// 定义一个静态常量TAG用于标识该模型所对应的网站地址此处明确为http://www.gxwztv.com
// 便于在整个程序体系里知晓此模型相关的网络操作、数据解析等逻辑是针对该站点而设计的。
public static final String TAG = "http://www.gxwztv.com";
/**
* GxwztvBookModelImpl
*
*
*
* @return GxwztvBookModelImpl
*/
public static GxwztvBookModelImpl getInstance() {
return new GxwztvBookModelImpl();
}
/**
*
*
* Observable
* 便RxJava
*
* @param aCache ACache便访
* @return ObservableLibraryBean
*/
@Override
public Observable<LibraryBean> getLibraryData(final ACache aCache) {
return getRetrofitObject(TAG).create(IGxwztvApi.class).getLibraryData("").flatMap(new Function<String, ObservableSource<LibraryBean>>() {
@Override
public ObservableSource<LibraryBean> apply(String s) throws Exception {
if (s != null && s.length() > 0 && aCache != null) {
aCache.put(LibraryPresenterImpl.LIBRARY_CACHE_KEY, s);
}
return analyLibraryData(s);
}
});
return getRetrofitObject(TAG).create(IGxwztvApi.class).getLibraryData("")
// 使用flatMap操作符将网络请求获取到的字符串数据进行进一步处理先尝试缓存数据再进行解析操作
.flatMap(new Function<String, ObservableSource<LibraryBean>>() {
@Override
public ObservableSource<LibraryBean> apply(String s) throws Exception {
// 如果获取到的数据不为空且长度大于0并且缓存对象不为空则将数据存入缓存缓存的键由LibraryPresenterImpl.LIBRARY_CACHE_KEY指定
if (s!= null && s.length() > 0 && aCache!= null) {
aCache.put(LibraryPresenterImpl.LIBRARY_CACHE_KEY, s);
}
// 调用解析主页数据的方法将获取到的数据传入进行解析并返回解析后的Observable对象
return analyLibraryData(s);
}
});
}
/**
*
* ObservableHTML
* LibraryBeanObservableEmitter
*
* @param data HTML
* @return ObservableLibraryBean
*/
@Override
public Observable<LibraryBean> analyLibraryData(final String data) {
@ -66,24 +95,35 @@ public class GxwztvBookModelImpl extends MBaseModelImpl implements IGxwztvBookMo
@Override
public void subscribe(ObservableEmitter<LibraryBean> e) throws Exception {
LibraryBean result = new LibraryBean();
// 使用Jsoup库将传入的HTML格式字符串数据解析为Document对象以便后续按照DOM结构查找和提取元素及内容
Document doc = Jsoup.parse(data);
// 通过元素的类名获取名为"container"的元素,这里假设主页的主要内容都在这个元素内部,是后续提取各种信息的基础元素
Element contentE = doc.getElementsByClass("container").get(0);
//解析最新书籍
// 解析最新书籍信息
// 获取所有类名为"list-group-item text-nowrap modal-open"的元素,这些元素被认为包含了最新书籍的相关信息
Elements newBookEs = contentE.getElementsByClass("list-group-item text-nowrap modal-open");
List<LibraryNewBookBean> libraryNewBooks = new ArrayList<LibraryNewBookBean>();
for (int i = 0; i < newBookEs.size(); i++) {
// 从每个包含最新书籍信息的元素中获取<a>标签元素,通常<a>标签包含了书籍的链接、书名等关键信息
Element itemE = newBookEs.get(i).getElementsByTag("a").get(0);
// 创建一个LibraryNewBookBean对象将书名、书籍链接等信息封装进去其中链接是在TAG基础上拼接获取到的相对链接
LibraryNewBookBean item = new LibraryNewBookBean(itemE.text(), TAG + itemE.attr("href"), TAG, "gxwztv.com");
libraryNewBooks.add(item);
}
result.setLibraryNewBooks(libraryNewBooks);
//////////////////////////////////////////////////////////////////////
// 解析不同分类书籍信息,先初始化一个空的列表用于存放不同分类的书籍列表信息
List<LibraryKindBookListBean> kindBooks = new ArrayList<LibraryKindBookListBean>();
//解析男频女频
// 解析男频女频分类书籍信息
// 获取所有类名为"col-xs-12"的元素,这里假设这些元素包含了男频女频相关的分类信息
Elements hotEs = contentE.getElementsByClass("col-xs-12");
for (int i = 1; i < hotEs.size(); i++) {
LibraryKindBookListBean kindItem = new LibraryKindBookListBean();
// 获取分类名称,即通过类名为"panel-title"的元素获取其文本内容作为分类名
kindItem.setKindName(hotEs.get(i).getElementsByClass("panel-title").get(0).text());
// 获取该分类下的书籍元素列表,通过先获取类名为"panel-body"的元素,再获取其内部的<li>标签元素来定位书籍信息所在元素
Elements bookEs = hotEs.get(i).getElementsByClass("panel-body").get(0).getElementsByTag("li");
List<SearchBookBean> books = new ArrayList<SearchBookBean>();
@ -91,47 +131,59 @@ public class GxwztvBookModelImpl extends MBaseModelImpl implements IGxwztvBookMo
SearchBookBean searchBookBean = new SearchBookBean();
searchBookBean.setOrigin("gxwztv.com");
searchBookBean.setTag(TAG);
// 获取书籍名称,通过获取<span>标签的文本内容作为书名
searchBookBean.setName(bookEs.get(j).getElementsByTag("span").get(0).text());
// 获取书籍详情链接同样是在TAG基础上拼接相对链接
searchBookBean.setNoteUrl(TAG + bookEs.get(j).getElementsByTag("a").get(0).attr("href"));
// 获取书籍封面链接,通过获取<img>标签的src属性值作为封面链接
searchBookBean.setCoverUrl(bookEs.get(j).getElementsByTag("img").get(0).attr("src"));
books.add(searchBookBean);
}
kindItem.setBooks(books);
kindBooks.add(kindItem);
}
//解析部分分类推荐
// 解析部分分类推荐书籍信息
// 获取所有类名为"panel panel-info index-category-qk"的元素,这些元素包含了部分分类推荐的相关信息
Elements kindEs = contentE.getElementsByClass("panel panel-info index-category-qk");
for (int i = 0; i < kindEs.size(); i++) {
LibraryKindBookListBean kindItem = new LibraryKindBookListBean();
kindItem.setKindName(kindEs.get(i).getElementsByClass("panel-title").get(0).text());
// 获取分类链接同样是在TAG基础上拼接相对链接
kindItem.setKindUrl(TAG + kindEs.get(i).getElementsByClass("listMore").get(0).getElementsByTag("a").get(0).attr("href"));
List<SearchBookBean> books = new ArrayList<SearchBookBean>();
// 获取第一个推荐书籍的元素,这里通过<dl>标签来定位
Element firstBookE = kindEs.get(i).getElementsByTag("dl").get(0);
SearchBookBean firstBook = new SearchBookBean();
firstBook.setTag(TAG);
firstBook.setOrigin("gxwztv.com");
// 获取书籍名称,通过获取<a>标签的文本内容作为书名(这里取第二个<a>标签的文本,可能是根据网页结构来确定的具体书名所在位置)
firstBook.setName(firstBookE.getElementsByTag("a").get(1).text());
// 获取书籍详情链接在TAG基础上拼接相对链接
firstBook.setNoteUrl(TAG + firstBookE.getElementsByTag("a").get(0).attr("href"));
// 获取书籍封面链接,通过获取<a>标签下的<img>标签的src属性值作为封面链接
firstBook.setCoverUrl(firstBookE.getElementsByTag("a").get(0).getElementsByTag("img").get(0).attr("src"));
firstBook.setKind(kindItem.getKindName());
books.add(firstBook);
// 获取其他推荐书籍的元素列表,通过类名为"book_textList"的元素下的<li>标签来定位
Elements otherBookEs = kindEs.get(i).getElementsByClass("book_textList").get(0).getElementsByTag("li");
for (int j = 0; j < otherBookEs.size(); j++) {
SearchBookBean item = new SearchBookBean();
item.setTag(TAG);
item.setOrigin("gxwztv.com");
item.setKind(kindItem.getKindName());
item.setNoteUrl(TAG+otherBookEs.get(j).getElementsByTag("a").get(0).attr("href"));
item.setNoteUrl(TAG + otherBookEs.get(j).getElementsByTag("a").get(0).attr("href"));
item.setName(otherBookEs.get(j).getElementsByTag("a").get(0).text());
books.add(item);
}
kindItem.setBooks(books);
kindBooks.add(kindItem);
}
//////////////
result.setKindBooks(kindBooks);
// 通过ObservableEmitter发射解析好的LibraryBean对象包含了主页上的各种书籍相关信息
e.onNext(result);
e.onComplete();
}
@ -139,33 +191,58 @@ public class GxwztvBookModelImpl extends MBaseModelImpl implements IGxwztvBookMo
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
*
* SearchBookBeanObservable
*
* @param content
* @param page
* @return ObservableList<SearchBookBean>
*/
@Override
public Observable<List<SearchBookBean>> searchBook(String content, int page) {
return getRetrofitObject(TAG).create(IGxwztvApi.class).searchBook(content, page).flatMap(new Function<String, ObservableSource<List<SearchBookBean>>>() {
@Override
public ObservableSource<List<SearchBookBean>> apply(String s) throws Exception {
return analySearchBook(s);
}
});
return getRetrofitObject(TAG).create(IGxwztvApi.class).searchBook(content, page)
.flatMap(new Function<String, ObservableSource<List<SearchBookBean>>>() {
@Override
public ObservableSource<List<SearchBookBean>> apply(String s) throws Exception {
return analySearchBook(s);
}
});
}
/**
*
* HTML
* SearchBookBeanObservableEmitter
*
* @param s HTML
* @return ObservableList<SearchBookBean>
*/
public Observable<List<SearchBookBean>> analySearchBook(final String s) {
return Observable.create(new ObservableOnSubscribe<List<SearchBookBean>>() {
@Override
public void subscribe(ObservableEmitter<List<SearchBookBean>> e) throws Exception {
try {
Document doc = Jsoup.parse(s);
// 获取id为"novel-list"的元素下类名为"list-group-item clearfix"的元素列表,这里假设这些元素包含了搜索到的每本书籍的相关信息
Elements booksE = doc.getElementById("novel-list").getElementsByClass("list-group-item clearfix");
if (null != booksE && booksE.size() >= 2) {
if (null!= booksE && booksE.size() >= 2) {
List<SearchBookBean> books = new ArrayList<SearchBookBean>();
for (int i = 1; i < booksE.size(); i++) {
SearchBookBean item = new SearchBookBean();
item.setTag(TAG);
// 获取书籍作者信息,通过类名为"col-xs-2"的元素获取其文本内容作为作者名
item.setAuthor(booksE.get(i).getElementsByClass("col-xs-2").get(0).text());
// 获取书籍分类信息,通过类名为"col-xs-1"的元素获取其文本内容作为分类名
item.setKind(booksE.get(i).getElementsByClass("col-xs-1").get(0).text());
// 获取书籍最后章节信息,通过类名为"col-xs-4"的元素下的<a>标签获取其文本内容作为最后章节名
item.setLastChapter(booksE.get(i).getElementsByClass("col-xs-4").get(0).getElementsByTag("a").get(0).text());
item.setOrigin("gxwztv.com");
// 获取书籍名称,通过类名为"col-xs-3"的元素下的<a>标签获取其文本内容作为书名
item.setName(booksE.get(i).getElementsByClass("col-xs-3").get(0).getElementsByTag("a").get(0).text());
// 获取书籍详情链接在TAG基础上拼接相对链接
item.setNoteUrl(TAG + booksE.get(i).getElementsByClass("col-xs-3").get(0).getElementsByTag("a").get(0).attr("href"));
item.setCoverUrl("noimage");
books.add(item);
@ -183,171 +260,8 @@ public class GxwztvBookModelImpl extends MBaseModelImpl implements IGxwztvBookMo
});
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public Observable<BookShelfBean> getBookInfo(final BookShelfBean bookShelfBean) {
return getRetrofitObject(TAG).create(IGxwztvApi.class).getBookInfo(bookShelfBean.getNoteUrl().replace(TAG, "")).flatMap(new Function<String, ObservableSource<BookShelfBean>>() {
@Override
public ObservableSource<BookShelfBean> apply(String s) throws Exception {
return analyBookInfo(s, bookShelfBean);
}
});
}
private Observable<BookShelfBean> analyBookInfo(final String s, final BookShelfBean bookShelfBean) {
return Observable.create(new ObservableOnSubscribe<BookShelfBean>() {
@Override
public void subscribe(ObservableEmitter<BookShelfBean> e) throws Exception {
bookShelfBean.setTag(TAG);
bookShelfBean.setBookInfoBean(analyBookinfo(s, bookShelfBean.getNoteUrl()));
e.onNext(bookShelfBean);
e.onComplete();
}
});
}
private BookInfoBean analyBookinfo(String s, String novelUrl) {
BookInfoBean bookInfoBean = new BookInfoBean();
bookInfoBean.setNoteUrl(novelUrl); //id
bookInfoBean.setTag(TAG);
Document doc = Jsoup.parse(s);
Element resultE = doc.getElementsByClass("panel panel-warning").get(0);
bookInfoBean.setCoverUrl(resultE.getElementsByClass("panel-body").get(0).getElementsByClass("img-thumbnail").get(0).attr("src"));
bookInfoBean.setName(resultE.getElementsByClass("active").get(0).text());
bookInfoBean.setAuthor(resultE.getElementsByClass("col-xs-12 list-group-item no-border").get(0).getElementsByTag("small").get(0).text());
Element introduceE = resultE.getElementsByClass("panel panel-default mt20").get(0);
String introduce = "";
if (introduceE.getElementById("all") != null) {
introduce = introduceE.getElementById("all").text().replace("[收起]", "");
} else {
introduce = introduceE.getElementById("shot").text();
}
bookInfoBean.setIntroduce("\u3000\u3000" + introduce);
bookInfoBean.setChapterUrl(TAG + resultE.getElementsByClass("list-group-item tac").get(0).getElementsByTag("a").get(0).attr("href"));
bookInfoBean.setOrigin("gxwztv.com");
return bookInfoBean;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void getChapterList(final BookShelfBean bookShelfBean, final OnGetChapterListListener getChapterListListener) {
getRetrofitObject(TAG).create(IGxwztvApi.class).getChapterList(bookShelfBean.getBookInfoBean().getChapterUrl().replace(TAG, "")).flatMap(new Function<String, ObservableSource<WebChapterBean<BookShelfBean>>>() {
@Override
public ObservableSource<WebChapterBean<BookShelfBean>> apply(String s) throws Exception {
return analyChapterList(s, bookShelfBean);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SimpleObserver<WebChapterBean<BookShelfBean>>() {
@Override
public void onNext(WebChapterBean<BookShelfBean> value) {
if (getChapterListListener != null) {
getChapterListListener.success(value.getData());
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
if (getChapterListListener != null) {
getChapterListListener.error();
}
}
});
}
private Observable<WebChapterBean<BookShelfBean>> analyChapterList(final String s, final BookShelfBean bookShelfBean) {
return Observable.create(new ObservableOnSubscribe<WebChapterBean<BookShelfBean>>() {
@Override
public void subscribe(ObservableEmitter<WebChapterBean<BookShelfBean>> e) throws Exception {
bookShelfBean.setTag(TAG);
WebChapterBean<List<ChapterListBean>> temp = analyChapterlist(s, bookShelfBean.getNoteUrl());
bookShelfBean.getBookInfoBean().setChapterlist(temp.getData());
e.onNext(new WebChapterBean<BookShelfBean>(bookShelfBean, temp.getNext()));
e.onComplete();
}
});
}
private WebChapterBean<List<ChapterListBean>> analyChapterlist(String s, String novelUrl) {
Document doc = Jsoup.parse(s);
Elements chapterlist = doc.getElementById("chapters-list").getElementsByTag("a");
List<ChapterListBean> chapterBeans = new ArrayList<ChapterListBean>();
for (int i = 0; i < chapterlist.size(); i++) {
ChapterListBean temp = new ChapterListBean();
temp.setDurChapterUrl(TAG + chapterlist.get(i).attr("href")); //id
temp.setDurChapterIndex(i);
temp.setDurChapterName(chapterlist.get(i).text());
temp.setNoteUrl(novelUrl);
temp.setTag(TAG);
chapterBeans.add(temp);
}
Boolean next = false;
return new WebChapterBean<List<ChapterListBean>>(chapterBeans, next);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public Observable<BookContentBean> getBookContent(final String durChapterUrl, final int durChapterIndex) {
return getRetrofitObject(TAG).create(IGxwztvApi.class).getBookContent(durChapterUrl.replace(TAG, "")).flatMap(new Function<String, ObservableSource<BookContentBean>>() {
@Override
public ObservableSource<BookContentBean> apply(String s) throws Exception {
return analyBookContent(s, durChapterUrl, durChapterIndex);
}
});
}
private Observable<BookContentBean> analyBookContent(final String s, final String durChapterUrl, final int durChapterIndex) {
return Observable.create(new ObservableOnSubscribe<BookContentBean>() {
@Override
public void subscribe(ObservableEmitter<BookContentBean> e) throws Exception {
BookContentBean bookContentBean = new BookContentBean();
bookContentBean.setDurChapterIndex(durChapterIndex);
bookContentBean.setDurChapterUrl(durChapterUrl);
bookContentBean.setTag(TAG);
try {
Document doc = Jsoup.parse(s);
List<TextNode> contentEs = doc.getElementById("txtContent").textNodes();
StringBuilder content = new StringBuilder();
for (int i = 0; i < contentEs.size(); i++) {
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
if (temp.length() > 0) {
content.append("\u3000\u3000" + temp);
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
bookContentBean.setDurCapterContent(content.toString());
bookContentBean.setRight(true);
} catch (Exception ex) {
ex.printStackTrace();
ErrorAnalyContentManager.getInstance().writeNewErrorUrl(durChapterUrl);
bookContentBean.setDurCapterContent(durChapterUrl.substring(0, durChapterUrl.indexOf('/', 8)) + "站点暂时不支持解析请反馈给Monke QQ:1105075896,半小时内解决,超级效率的程序员");
bookContentBean.setRight(false);
}
e.onNext(bookContentBean);
e.onComplete();
}
});
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
*/
@Override
public Observable<List<SearchBookBean>> getKindBook(String url, int page) {
url = url + page + ".htm";
return getRetrofitObject(GxwztvBookModelImpl.TAG).create(IGxwztvApi.class).getKindBooks(url.replace(GxwztvBookModelImpl.TAG, "")).flatMap(new Function<String, ObservableSource<List<SearchBookBean>>>() {
@Override
public ObservableSource<List<SearchBookBean>> apply(String s) throws Exception {
return analySearchBook(s);
}
});
}
}
/**
*
* BookShelfBean

@ -25,36 +25,68 @@ import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
/**
* ImportBookModelImplMBaseModelImplIImportBookModel
* MD5
* RxJavaObservable便
*/
public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookModel {
/**
* ImportBookModelImpl
* 便
*
*
* @return ImportBookModelImpl
*/
public static ImportBookModelImpl getInstance() {
return new ImportBookModelImpl();
}
/**
*
* MD5
* LocBookShelfBeanObservable
*
* @param book File
* @return ObservableLocBookShelfBean
*/
@Override
public Observable<LocBookShelfBean> importBook(final File book) {
return Observable.create(new ObservableOnSubscribe<LocBookShelfBean>() {
@Override
public void subscribe(ObservableEmitter<LocBookShelfBean> e) throws Exception {
// 创建MessageDigest实例用于计算文件的MD5值MD5值可作为书籍文件的唯一标识来判断是否重复导入等情况
MessageDigest md = MessageDigest.getInstance("MD5");
// 创建文件输入流,用于读取书籍文件内容
FileInputStream in = new FileInputStream(book);
byte[] buffer = new byte[2048];
int len;
while ((len = in.read(buffer, 0, 2048)) != -1) {
// 循环读取文件内容每次读取指定长度的数据并将其更新到MessageDigest中用于计算MD5值
while ((len = in.read(buffer, 0, 2048))!= -1) {
md.update(buffer, 0, len);
}
// 关闭文件输入流,释放资源
in.close();
in = null;
// 将计算得到的MD5值转换为十六进制字符串形式方便后续作为标识使用
String md5 = new BigInteger(1, md.digest()).toString(16);
BookShelfBean bookShelfBean = null;
List<BookShelfBean> temp = DbHelper.getInstance().getmDaoSession().getBookShelfBeanDao().queryBuilder().where(BookShelfBeanDao.Properties.NoteUrl.eq(md5)).build().list();
// 从数据库中查询是否已存在具有相同MD5值即相同书籍文件的记录通过构建查询条件并执行查询获取结果列表
List<BookShelfBean> temp = DbHelper.getInstance().getmDaoSession().getBookShelfBeanDao().queryBuilder()
.where(BookShelfBeanDao.Properties.NoteUrl.eq(md5)).build().list();
// 标记书籍是否为新导入的初始化为true表示默认是新书
Boolean isNew = true;
if (temp!=null && temp.size()>0) {
// 如果查询到的结果列表不为空且包含至少一个元素,说明书籍已存在,不是新书
if (temp!= null && temp.size() > 0) {
isNew = false;
bookShelfBean = temp.get(0);
bookShelfBean.setBookInfoBean(DbHelper.getInstance().getmDaoSession().getBookInfoBeanDao().queryBuilder().where(BookInfoBeanDao.Properties.NoteUrl.eq(bookShelfBean.getNoteUrl())).build().list().get(0));
// 从数据库中获取对应的书籍详情信息并设置到bookShelfBean对象中确保书籍相关信息的完整性
bookShelfBean.setBookInfoBean(DbHelper.getInstance().getmDaoSession().getBookInfoBeanDao().queryBuilder()
.where(BookInfoBeanDao.Properties.NoteUrl.eq(bookShelfBean.getNoteUrl())).build().list().get(0));
} else {
// 如果是新书则创建一个新的BookShelfBean对象用于存储书籍的基本信息
bookShelfBean = new BookShelfBean();
bookShelfBean.setFinalDate(System.currentTimeMillis());
bookShelfBean.setDurChapter(0);
@ -62,6 +94,7 @@ public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookMo
bookShelfBean.setTag(BookShelfBean.LOCAL_TAG);
bookShelfBean.setNoteUrl(md5);
// 设置书籍详情信息中的作者、书名等基本信息,书名通过去除文件扩展名来获取,作者暂设为“佚名”
bookShelfBean.getBookInfoBean().setAuthor("佚名");
bookShelfBean.getBookInfoBean().setName(book.getName().replace(".txt", "").replace(".TXT", ""));
bookShelfBean.getBookInfoBean().setFinalRefreshData(System.currentTimeMillis());
@ -69,17 +102,31 @@ public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookMo
bookShelfBean.getBookInfoBean().setNoteUrl(md5);
bookShelfBean.getBookInfoBean().setTag(BookShelfBean.LOCAL_TAG);
// 调用方法保存书籍的章节信息到数据库中
saveChapter(book, md5);
// 将书籍详情信息插入或替换到数据库中(如果已存在相同标识的记录则替换)
DbHelper.getInstance().getmDaoSession().getBookInfoBeanDao().insertOrReplace(bookShelfBean.getBookInfoBean());
// 将书籍基本信息插入或替换到数据库中
DbHelper.getInstance().getmDaoSession().getBookShelfBeanDao().insertOrReplace(bookShelfBean);
}
bookShelfBean.getBookInfoBean().setChapterlist(DbHelper.getInstance().getmDaoSession().getChapterListBeanDao().queryBuilder().where(ChapterListBeanDao.Properties.NoteUrl.eq(bookShelfBean.getNoteUrl())).orderAsc(ChapterListBeanDao.Properties.DurChapterIndex).build().list());
e.onNext(new LocBookShelfBean(isNew,bookShelfBean));
// 从数据库中获取该书籍的章节列表信息并设置到bookShelfBean对象的对应属性中确保书籍章节信息的完整性
bookShelfBean.getBookInfoBean().setChapterlist(DbHelper.getInstance().getmDaoSession().getChapterListBeanDao().queryBuilder()
.where(ChapterListBeanDao.Properties.NoteUrl.eq(bookShelfBean.getNoteUrl())).orderAsc(ChapterListBeanDao.Properties.DurChapterIndex).build().list());
// 通过ObservableEmitter发射包含是否为新书以及完整书籍信息的LocBookShelfBean对象供订阅者接收处理
e.onNext(new LocBookShelfBean(isNew, bookShelfBean));
e.onComplete();
}
});
}
/**
* BookShelfBeanshelfs
* NoteUrl
*
* @param temp BookShelfBean
* @param shelfs List<BookShelfBean>
* @return truefalse
*/
private Boolean isAdded(BookShelfBean temp, List<BookShelfBean> shelfs) {
if (shelfs == null || shelfs.size() == 0) {
return false;
@ -99,47 +146,68 @@ public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookMo
}
}
/**
*
*
* ChapterListBean
*
* @param book File
* @param md5 MD5
* @throws IOException I/O
*/
private void saveChapter(File book, String md5) throws IOException {
// 定义一个正则表达式用于匹配章节标题的模式这里假设章节标题一般以“第X章”开头X表示数字及一些其他可能的字符长度限制在1到7之间后面可跟一些其他内容
String regex = "第.{1,7}章.{0,}";
String encoding;
// 创建文件输入流,用于读取书籍文件内容,为后续检测编码格式和解析内容做准备
FileInputStream fis = new FileInputStream(book);
byte[] buf = new byte[4096];
// 创建UniversalDetector实例用于检测文件的编码格式它可以自动识别多种常见的编码类型
UniversalDetector detector = new UniversalDetector(null);
int nread;
while ((nread = fis.read(buf)) > 0 && !detector.isDone()) {
// 循环读取文件内容每次读取一部分数据交给UniversalDetector进行分析直到检测完成或读取完整个文件
while ((nread = fis.read(buf)) > 0 &&!detector.isDone()) {
detector.handleData(buf, 0, nread);
}
detector.dataEnd();
// 获取检测到的文件编码格式,如果未检测到则默认为"utf-8"编码
encoding = detector.getDetectedCharset();
if (encoding == null || encoding.length() == 0)
encoding = "utf-8";
fis.close();
fis = null;
// 章节页码索引用于记录章节的顺序初始化为0表示从第一章开始
int chapterPageIndex = 0;
String title = null;
StringBuilder contentBuilder = new StringBuilder();
fis = new FileInputStream(book);
// 根据检测到的编码格式创建InputStreamReader用于以正确的编码方式读取文件内容
InputStreamReader inputreader = new InputStreamReader(fis, encoding);
// 创建BufferedReader方便逐行读取文件内容进行解析
BufferedReader buffreader = new BufferedReader(inputreader);
String line;
while ((line = buffreader.readLine()) != null) {
// 逐行读取书籍文件内容,进行章节信息的解析和提取
while ((line = buffreader.readLine())!= null) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(line);
if (m.find()) {
String temp = line.trim().substring(0,line.trim().indexOf("第"));
if(temp!= null && temp.trim().length()>0){
// 如果匹配到章节标题先提取标题前可能存在的内容如果有并添加到章节内容的临时存储字符串中contentBuilder
String temp = line.trim().substring(0, line.trim().indexOf("第"));
if (temp!= null && temp.trim().length() > 0) {
contentBuilder.append(temp);
}
// 如果临时存储的章节内容字符串不为空且去除全角空格等空白字符后仍有内容,则将当前章节内容保存到数据库中,并更新相关索引和清空临时存储内容
if (contentBuilder.toString().length() > 0) {
if(contentBuilder.toString().replaceAll(" ","").trim().length()>0){
if (contentBuilder.toString().replaceAll(" ", "").trim().length() > 0) {
saveDurChapterContent(md5, chapterPageIndex, title, contentBuilder.toString());
chapterPageIndex++;
}
contentBuilder.delete(0, contentBuilder.length());
}
// 将匹配到的章节标题提取出来,作为当前章节的标题
title = line.trim().substring(line.trim().indexOf("第"));
} else {
if (line.trim().length() == 0) {
@ -156,6 +224,7 @@ public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookMo
}
}
}
// 处理文件末尾剩余的章节内容(如果有),同样将其保存到数据库中
if (contentBuilder.length() > 0) {
saveDurChapterContent(md5, chapterPageIndex, title, contentBuilder.toString());
contentBuilder.delete(0, contentBuilder.length());
@ -167,6 +236,16 @@ public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookMo
fis = null;
}
/**
*
* ChapterListBean
*
*
* @param md5 MD5
* @param chapterPageIndex
* @param name
* @param content
*/
private void saveDurChapterContent(String md5, int chapterPageIndex, String name, String content) {
ChapterListBean chapterListBean = new ChapterListBean();
chapterListBean.setNoteUrl(md5);
@ -182,4 +261,4 @@ public class ImportBookModelImpl extends MBaseModelImpl implements IImportBookMo
DbHelper.getInstance().getmDaoSession().getBookContentBeanDao().insertOrReplace(chapterListBean.getBookContentBean());
DbHelper.getInstance().getmDaoSession().getChapterListBeanDao().insertOrReplace(chapterListBean);
}
}
}

@ -28,42 +28,84 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
* LingdiankanshuStationBookModelImplMBaseModelImplIStationBookModel
* TAGhttp://www.lingdiankanshu.co进行交互实现如搜索书籍、获取书籍详情、章节列表以及章节内容等功能。
* HTMLRxJava
*/
public class LingdiankanshuStationBookModelImpl extends MBaseModelImpl implements IStationBookModel {
// 定义一个静态常量TAG用于标识该模型所对应的网站地址此处明确为http://www.lingdiankanshu.co
// 便于在整个程序体系里知晓此模型相关的网络操作、数据解析等逻辑是针对该站点而设计的。
public static final String TAG = "http://www.lingdiankanshu.co";
/**
* LingdiankanshuStationBookModelImpl
*
*
*
* @return LingdiankanshuStationBookModelImpl
*/
public static LingdiankanshuStationBookModelImpl getInstance() {
return new LingdiankanshuStationBookModelImpl();
}
/**
*
* http://zhannei.baidu.com发起搜索书籍的网络请求调用对应的API
* SearchBookBeanObservable
*
*
* @param content
* @param page 1
* @return ObservableList<SearchBookBean>
*/
@Override
public Observable<List<SearchBookBean>> searchBook(String content, int page) {
return getRetrofitObject("http://zhannei.baidu.com").create(ILingdiankanshuApi.class).searchBook(content, page - 1, "16865089933227718744").flatMap(new Function<String, ObservableSource<List<SearchBookBean>>>() {
@Override
public ObservableSource<List<SearchBookBean>> apply(String s) throws Exception {
return analySearchBook(s);
}
});
return getRetrofitObject("http://zhannei.baidu.com").create(ILingdiankanshuApi.class).searchBook(content, page - 1, "16865089933227718744")
.flatMap(new Function<String, ObservableSource<List<SearchBookBean>>>() {
@Override
public ObservableSource<List<SearchBookBean>> apply(String s) throws Exception {
return analySearchBook(s);
}
});
}
/**
*
* HTML
* SearchBookBeanObservableEmitter
*
* @param s HTML
* @return ObservableList<SearchBookBean>
*/
public Observable<List<SearchBookBean>> analySearchBook(final String s) {
return Observable.create(new ObservableOnSubscribe<List<SearchBookBean>>() {
@Override
public void subscribe(ObservableEmitter<List<SearchBookBean>> e) throws Exception {
try {
Document doc = Jsoup.parse(s);
// 获取类名为"result-list"的元素下,类名为"result-item result-game-item"的元素列表,这里假设这些元素包含了每本搜索到的书籍的相关信息
Elements booksE = doc.getElementsByClass("result-list").get(0).getElementsByClass("result-item result-game-item");
if (null != booksE && booksE.size() > 1) {
if (null!= booksE && booksE.size() > 1) {
List<SearchBookBean> books = new ArrayList<SearchBookBean>();
for (int i = 0; i < booksE.size(); i++) {
SearchBookBean item = new SearchBookBean();
item.setTag(TAG);
// 获取书籍作者信息,通过层层定位元素,先找到类名为"result-game-item-info"的元素,再找到其内部类名为"result-game-item-info-tag"的元素,
// 然后获取第二个<span>标签的文本内容作为作者名(这里的元素定位方式是根据网页结构来确定的具体作者名所在位置)
item.setAuthor(booksE.get(i).getElementsByClass("result-game-item-info").get(0).getElementsByClass("result-game-item-info-tag").get(0).getElementsByTag("span").get(1).text());
// 获取书籍分类信息,同样通过层层定位元素,获取对应位置的<span>标签文本内容作为分类名
item.setKind(booksE.get(i).getElementsByClass("result-game-item-info").get(0).getElementsByClass("result-game-item-info-tag").get(1).getElementsByTag("span").get(1).text());
// item.setState();
// item.setState(); // 此处代码似乎未完成对书籍状态的设置相关逻辑,可能需要后续补充
// 获取书籍最后章节信息,通过对应元素下的<a>标签获取其文本内容作为最后章节名
item.setLastChapter(booksE.get(i).getElementsByClass("result-game-item-info").get(0).getElementsByClass("result-game-item-info-tag").get(3).getElementsByTag("a").get(0).text());
item.setOrigin("lingdiankanshu.co");
// 获取书籍名称,通过类名为"result-item-title result-game-item-title"的元素下的<a>标签获取其文本内容作为书名
item.setName(booksE.get(i).getElementsByClass("result-item-title result-game-item-title").get(0).getElementsByTag("a").get(0).text());
// 获取书籍详情链接,通过对应的<a>标签获取其href属性值作为详情链接
item.setNoteUrl(booksE.get(i).getElementsByClass("result-item-title result-game-item-title").get(0).getElementsByTag("a").get(0).attr("href"));
// 获取书籍封面链接,通过<img>标签的src属性值作为封面链接
item.setCoverUrl(booksE.get(i).getElementsByTag("img").get(0).attr("src"));
books.add(item);
}
@ -81,16 +123,35 @@ public class LingdiankanshuStationBookModelImpl extends MBaseModelImpl implement
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
* BookShelfBean
* BookShelfBeanObservable
*
* @param bookShelfBean BookShelfBean
* @return ObservableBookShelfBean
*/
@Override
public Observable<BookShelfBean> getBookInfo(final BookShelfBean bookShelfBean) {
return getRetrofitObject(TAG).create(ILingdiankanshuApi.class).getBookInfo(bookShelfBean.getNoteUrl().replace(TAG, "")).flatMap(new Function<String, ObservableSource<BookShelfBean>>() {
@Override
public ObservableSource<BookShelfBean> apply(String s) throws Exception {
return analyBookInfo(s, bookShelfBean);
}
});
return getRetrofitObject(TAG).create(ILingdiankanshuApi.class).getBookInfo(bookShelfBean.getNoteUrl().replace(TAG, ""))
.flatMap(new Function<String, ObservableSource<BookShelfBean>>() {
@Override
public ObservableSource<BookShelfBean> apply(String s) throws Exception {
return analyBookInfo(s, bookShelfBean);
}
});
}
/**
*
* HTML
* BookShelfBeananalyBookinfoObservableEmitter
*
* @param s HTML
* @param bookShelfBean BookShelfBean
* @return ObservableBookShelfBean
*/
private Observable<BookShelfBean> analyBookInfo(final String s, final BookShelfBean bookShelfBean) {
return Observable.create(new ObservableOnSubscribe<BookShelfBean>() {
@Override
@ -103,23 +164,34 @@ public class LingdiankanshuStationBookModelImpl extends MBaseModelImpl implement
});
}
/**
* HTMLBookInfoBean
*
* @param s HTML
* @param novelUrl BookInfoBean
* @return BookInfoBean
*/
private BookInfoBean analyBookinfo(String s, String novelUrl) {
BookInfoBean bookInfoBean = new BookInfoBean();
bookInfoBean.setNoteUrl(novelUrl); //id
bookInfoBean.setTag(TAG);
Document doc = Jsoup.parse(s);
// 获取类名为"box_con"的元素,这里假设书籍详情的主要信息都在这个元素内部,是后续提取各种信息的基础元素
Element resultE = doc.getElementsByClass("box_con").get(0);
// 获取书籍封面链接,通过定位元素找到对应的<img>标签并获取其src属性值作为封面链接
bookInfoBean.setCoverUrl(resultE.getElementById("fmimg").getElementsByTag("img").get(0).attr("src"));
// 获取书籍名称,通过定位元素找到对应的<h1>标签,并获取其文本内容作为书名
bookInfoBean.setName(resultE.getElementById("info").getElementsByTag("h1").get(0).text());
String author = resultE.getElementById("info").getElementsByTag("p").get(0).text().toString().trim();
author = author.replace(" ", "").replace("  ", "").replace("作者:", "");
// 对获取到的作者信息字符串进行处理,去除一些多余的空格、特殊空格以及"作者:"等前缀内容,得到纯净的作者名
author = author.replace(" ", "").replace(" ", "").replace("作者:", "");
bookInfoBean.setAuthor(author);
List<TextNode> contentEs = resultE.getElementById("intro").textNodes();
StringBuilder content = new StringBuilder();
for (int i = 0; i < contentEs.size(); i++) {
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
if (temp.length() > 0) {
content.append("\u3000\u3000" + temp);
if (i < contentEs.size() - 1) {
@ -135,20 +207,31 @@ public class LingdiankanshuStationBookModelImpl extends MBaseModelImpl implement
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
* BookShelfBean
* BookShelfBean
* RxJava线IO线线OnGetChapterListListener
*
* @param bookShelfBean BookShelfBean
* @param getChapterListListener successerror
*/
@Override
public void getChapterList(final BookShelfBean bookShelfBean, final OnGetChapterListListener getChapterListListener) {
getRetrofitObject(TAG).create(ILingdiankanshuApi.class).getChapterList(bookShelfBean.getBookInfoBean().getChapterUrl().replace(TAG, "")).flatMap(new Function<String, ObservableSource<WebChapterBean<BookShelfBean>>>() {
@Override
public ObservableSource<WebChapterBean<BookShelfBean>> apply(String s) throws Exception {
return analyChapterList(s, bookShelfBean);
}
})
getRetrofitObject(TAG).create(ILingdiankanshuApi.class).getChapterList(bookShelfBean.getBookInfoBean().getChapterUrl().replace(TAG, ""))
.flatMap(new Function<String, ObservableSource<WebChapterBean<BookShelfBean>>>() {
@Override
public ObservableSource<WebChapterBean<BookShelfBean>> apply(String s) throws Exception {
return analyChapterList(s, bookShelfBean);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SimpleObserver<WebChapterBean<BookShelfBean>>() {
@Override
public void onNext(WebChapterBean<BookShelfBean> value) {
if (getChapterListListener != null) {
if (getChapterListListener!= null) {
getChapterListListener.success(value.getData());
}
}
@ -156,90 +239,17 @@ public class LingdiankanshuStationBookModelImpl extends MBaseModelImpl implement
@Override
public void onError(Throwable e) {
e.printStackTrace();
if (getChapterListListener != null) {
if (getChapterListListener!= null) {
getChapterListListener.error();
}
}
});
}
private Observable<WebChapterBean<BookShelfBean>> analyChapterList(final String s, final BookShelfBean bookShelfBean) {
return Observable.create(new ObservableOnSubscribe<WebChapterBean<BookShelfBean>>() {
@Override
public void subscribe(ObservableEmitter<WebChapterBean<BookShelfBean>> e) throws Exception {
bookShelfBean.setTag(TAG);
WebChapterBean<List<ChapterListBean>> temp = analyChapterlist(s, bookShelfBean.getNoteUrl());
bookShelfBean.getBookInfoBean().setChapterlist(temp.getData());
e.onNext(new WebChapterBean<BookShelfBean>(bookShelfBean, temp.getNext()));
e.onComplete();
}
});
}
private WebChapterBean<List<ChapterListBean>> analyChapterlist(String s, String novelUrl) {
Document doc = Jsoup.parse(s);
Elements chapterlist = doc.getElementById("list").getElementsByTag("dd");
List<ChapterListBean> chapterBeans = new ArrayList<ChapterListBean>();
for (int i = 0; i < chapterlist.size(); i++) {
ChapterListBean temp = new ChapterListBean();
temp.setDurChapterUrl(novelUrl + chapterlist.get(i).getElementsByTag("a").get(0).attr("href")); //id
temp.setDurChapterIndex(i);
temp.setDurChapterName(chapterlist.get(i).getElementsByTag("a").get(0).text());
temp.setNoteUrl(novelUrl);
temp.setTag(TAG);
chapterBeans.add(temp);
}
Boolean next = false;
return new WebChapterBean<List<ChapterListBean>>(chapterBeans, next);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public Observable<BookContentBean> getBookContent(final String durChapterUrl, final int durChapterIndex) {
return getRetrofitObject(TAG).create(ILingdiankanshuApi.class).getBookContent(durChapterUrl.replace(TAG, "")).flatMap(new Function<String, ObservableSource<BookContentBean>>() {
@Override
public ObservableSource<BookContentBean> apply(String s) throws Exception {
return analyBookContent(s, durChapterUrl, durChapterIndex);
}
});
}
private Observable<BookContentBean> analyBookContent(final String s, final String durChapterUrl, final int durChapterIndex) {
return Observable.create(new ObservableOnSubscribe<BookContentBean>() {
@Override
public void subscribe(ObservableEmitter<BookContentBean> e) throws Exception {
BookContentBean bookContentBean = new BookContentBean();
bookContentBean.setDurChapterIndex(durChapterIndex);
bookContentBean.setDurChapterUrl(durChapterUrl);
bookContentBean.setTag(TAG);
try {
Document doc = Jsoup.parse(s);
List<TextNode> contentEs = doc.getElementById("content").textNodes();
StringBuilder content = new StringBuilder();
for (int i = 0; i < contentEs.size(); i++) {
String temp = contentEs.get(i).text().trim();
temp = temp.replaceAll(" ", "").replaceAll(" ", "");
if (temp.length() > 0) {
content.append("\u3000\u3000" + temp);
if (i < contentEs.size() - 1) {
content.append("\r\n");
}
}
}
bookContentBean.setDurCapterContent(content.toString());
bookContentBean.setRight(true);
} catch (Exception ex) {
ex.printStackTrace();
ErrorAnalyContentManager.getInstance().writeNewErrorUrl(durChapterUrl);
bookContentBean.setDurCapterContent(durChapterUrl.substring(0, durChapterUrl.indexOf('/', 8)) + "站点暂时不支持解析请反馈给Monke QQ:1105075896,半小时内解决,超级效率的程序员");
bookContentBean.setRight(false);
}
e.onNext(bookContentBean);
e.onComplete();
}
});
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
/**
*
* HTML
* ChapterListBeanBookShelfBeanObservableEmitter
*
* @param s HTML
* @param bookShelfBean

@ -12,8 +12,20 @@ import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
/**
* WebBookModelImplIWebBookModel
* tag
* 便
*/
public class WebBookModelImpl implements IWebBookModel {
/**
* WebBookModelImpl
* 便
*
*
* @return WebBookModelImpl
*/
public static WebBookModelImpl getInstance() {
return new WebBookModelImpl();
}
@ -22,17 +34,22 @@ public class WebBookModelImpl implements IWebBookModel {
/**
*
* return BookShelfBean
* BookShelfBeantag
* GxwztvBookModelImpl.TAG
* LingdiankanshuStationBookModelImpl.TAG
* null
* BookShelfBeanObservable
*
* @param bookShelfBean BookShelfBeantag
* @return ObservableBookShelfBeannull
*/
@Override
public Observable<BookShelfBean> getBookInfo(BookShelfBean bookShelfBean) {
if(bookShelfBean.getTag().equals(GxwztvBookModelImpl.TAG)){
if (bookShelfBean.getTag().equals(GxwztvBookModelImpl.TAG)) {
return GxwztvBookModelImpl.getInstance().getBookInfo(bookShelfBean);
}
else if(bookShelfBean.getTag().equals(LingdiankanshuStationBookModelImpl.TAG)){
} else if (bookShelfBean.getTag().equals(LingdiankanshuStationBookModelImpl.TAG)) {
return LingdiankanshuStationBookModelImpl.getInstance().getBookInfo(bookShelfBean);
}
else {
} else {
return null;
}
}
@ -41,18 +58,23 @@ public class WebBookModelImpl implements IWebBookModel {
/**
*
* return BookShelfBean
* BookShelfBeantag
* GxwztvBookModelImpl.TAG
* LingdiankanshuStationBookModelImpl.TAG
* OnGetChapterListListenerBookShelfBean
*
*
* @param bookShelfBean BookShelfBeantag
* @param getChapterListListener successerror
*/
@Override
public void getChapterList(final BookShelfBean bookShelfBean, OnGetChapterListListener getChapterListListener) {
if(bookShelfBean.getTag().equals(GxwztvBookModelImpl.TAG)){
if (bookShelfBean.getTag().equals(GxwztvBookModelImpl.TAG)) {
GxwztvBookModelImpl.getInstance().getChapterList(bookShelfBean, getChapterListListener);
}
else if(bookShelfBean.getTag().equals(LingdiankanshuStationBookModelImpl.TAG)){
} else if (bookShelfBean.getTag().equals(LingdiankanshuStationBookModelImpl.TAG)) {
LingdiankanshuStationBookModelImpl.getInstance().getChapterList(bookShelfBean, getChapterListListener);
}
else{
if(getChapterListListener!=null)
} else {
if (getChapterListListener!= null)
getChapterListListener.success(bookShelfBean);
}
}
@ -61,16 +83,24 @@ public class WebBookModelImpl implements IWebBookModel {
/**
*
* durChapterUrltag
* GxwztvBookModelImpl.TAG
* LingdiankanshuStationBookModelImpl.TAG
* ObservableBookContentBean
* BookContentBeanObservable
*
* @param durChapterUrl 便
* @param durChapterIndex
* @param tag
* @return ObservableBookContentBeanObservableBookContentBean
*/
@Override
public Observable<BookContentBean> getBookContent(String durChapterUrl, int durChapterIndex, String tag) {
if(tag.equals(GxwztvBookModelImpl.TAG)){
if (tag.equals(GxwztvBookModelImpl.TAG)) {
return GxwztvBookModelImpl.getInstance().getBookContent(durChapterUrl, durChapterIndex);
}
else if(tag.equals(LingdiankanshuStationBookModelImpl.TAG)){
} else if (tag.equals(LingdiankanshuStationBookModelImpl.TAG)) {
return LingdiankanshuStationBookModelImpl.getInstance().getBookContent(durChapterUrl, durChapterIndex);
}
else
} else
return Observable.create(new ObservableOnSubscribe<BookContentBean>() {
@Override
public void subscribe(ObservableEmitter<BookContentBean> e) throws Exception {
@ -82,16 +112,24 @@ public class WebBookModelImpl implements IWebBookModel {
/**
*
* tag
* GxwztvBookModelImpl.TAG
* LingdiankanshuStationBookModelImpl.TAG
* ObservableSearchBookBean
* SearchBookBeanObservable
*
* @param content
* @param page
* @param tag
* @return ObservableList<SearchBookBean>Observable
*/
@Override
public Observable<List<SearchBookBean>> searchOtherBook(String content,int page,String tag){
if(tag.equals(GxwztvBookModelImpl.TAG)){
public Observable<List<SearchBookBean>> searchOtherBook(String content, int page, String tag) {
if (tag.equals(GxwztvBookModelImpl.TAG)) {
return GxwztvBookModelImpl.getInstance().searchBook(content, page);
}
else if(tag.equals(LingdiankanshuStationBookModelImpl.TAG)){
} else if (tag.equals(LingdiankanshuStationBookModelImpl.TAG)) {
return LingdiankanshuStationBookModelImpl.getInstance().searchBook(content, page);
}
else{
} else {
return Observable.create(new ObservableOnSubscribe<List<SearchBookBean>>() {
@Override
public void subscribe(ObservableEmitter<List<SearchBookBean>> e) throws Exception {
@ -101,11 +139,19 @@ public class WebBookModelImpl implements IWebBookModel {
});
}
}
/**
*
* GxwztvBookModelImpl
* GxwztvBookModelImplSearchBookBeanObservable
*
*
* @param url GxwztvBookModelImpl
* @param page
* @return ObservableList<SearchBookBean>
*/
@Override
public Observable<List<SearchBookBean>> getKindBook(String url,int page) {
return GxwztvBookModelImpl.getInstance().getKindBook(url,page);
public Observable<List<SearchBookBean>> getKindBook(String url, int page) {
return GxwztvBookModelImpl.getInstance().getKindBook(url, page);
}
}
}

@ -1,4 +1,3 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package com.monke.monkeybook.service;
import android.app.NotificationManager;
@ -12,6 +11,7 @@ import android.os.Looper;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
import com.hwangjr.rxbus.RxBus;
import com.hwangjr.rxbus.annotation.Subscribe;
import com.hwangjr.rxbus.annotation.Tag;
@ -30,7 +30,9 @@ import com.monke.monkeybook.dao.DbHelper;
import com.monke.monkeybook.dao.DownloadChapterBeanDao;
import com.monke.monkeybook.model.impl.WebBookModelImpl;
import com.monke.monkeybook.view.impl.MainActivity;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
@ -41,18 +43,24 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
// DownloadService类继承自Service用于处理书籍下载相关的业务逻辑如启动、暂停、取消下载等操作
public class DownloadService extends Service {
// 用于管理通知的显示和取消等操作
private NotificationManager notifyManager;
// 通知的唯一标识符
private int notifiId = 19931118;
// 标记是否开始下载
private Boolean isStartDownload = false;
// 标记是否已经初始化
private Boolean isInit = false;
// 服务创建时调用的方法
@Override
public void onCreate() {
super.onCreate();
}
// 服务销毁时调用的方法用于注销RxBus注册以及设置初始化标记为true
@Override
public void onDestroy() {
super.onDestroy();
@ -60,25 +68,31 @@ public class DownloadService extends Service {
isInit = true;
}
// 服务启动时调用的方法进行一些初始化操作如获取通知管理器、注册RxBus等
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!isInit) {
isInit = true;
// 获取系统的通知服务
notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 注册RxBus用于接收相关事件
RxBus.get().register(this);
}
return super.onStartCommand(intent, flags, startId);
}
// 用于绑定服务这里返回null表示不支持绑定
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
// 添加新的下载任务,将新的下载章节数据插入数据库,并根据下载状态决定是否开始下载
private void addNewTask(final List<DownloadChapterBean> newData) {
isStartDownload = true;
Observable.create(new ObservableOnSubscribe<Boolean>() {
// 在这个方法中执行将下载章节数据插入数据库的操作
@Override
public void subscribe(ObservableEmitter<Boolean> e) throws Exception {
DbHelper.getInstance().getmDaoSession().getDownloadChapterBeanDao().insertOrReplaceInTx(newData);
@ -86,9 +100,12 @@ public class DownloadService extends Service {
e.onComplete();
}
})
// 指定在主线程观察结果
.observeOn(AndroidSchedulers.mainThread())
// 指定在IO线程执行插入数据库操作
.subscribeOn(Schedulers.io())
.subscribe(new SimpleObserver<Boolean>() {
// 插入成功后如果当前没有正在下载则调用toDownload方法开始下载
@Override
public void onNext(Boolean value) {
if (!isDownloading) {
@ -103,30 +120,38 @@ public class DownloadService extends Service {
});
}
// 标记是否正在下载
private Boolean isDownloading = false;
// 下载失败时的重试次数
public static final int reTryTimes = 1;
// 开始下载的主要逻辑方法,查找待下载的章节并进行下载操作
private void toDownload() {
isDownloading = true;
if (isStartDownload) {
Observable.create(new ObservableOnSubscribe<DownloadChapterBean>() {
@Override
public void subscribe(ObservableEmitter<DownloadChapterBean> e) throws Exception {
// 获取书架上的书籍列表,按最后更新日期降序排列
List<BookShelfBean> bookShelfBeanList = DbHelper.getInstance().getmDaoSession().getBookShelfBeanDao().queryBuilder().orderDesc(BookShelfBeanDao.Properties.FinalDate).list();
if (bookShelfBeanList != null && bookShelfBeanList.size() > 0) {
if (bookShelfBeanList!= null && bookShelfBeanList.size() > 0) {
for (BookShelfBean bookItem : bookShelfBeanList) {
// 排除本地标签的书籍(可能表示已经下载好的本地书籍)
if (!bookItem.getTag().equals(BookShelfBean.LOCAL_TAG)) {
// 查询该书籍下待下载的章节列表,按章节索引升序排列,取第一个章节(即下一个要下载的章节)
List<DownloadChapterBean> downloadChapterList = DbHelper.getInstance().getmDaoSession().getDownloadChapterBeanDao().queryBuilder().where(DownloadChapterBeanDao.Properties.NoteUrl.eq(bookItem.getNoteUrl())).orderAsc(DownloadChapterBeanDao.Properties.DurChapterIndex).limit(1).list();
if (downloadChapterList != null && downloadChapterList.size() > 0) {
if (downloadChapterList!= null && downloadChapterList.size() > 0) {
e.onNext(downloadChapterList.get(0));
e.onComplete();
return;
}
}
}
// 如果没有待下载章节,删除所有下载章节记录
DbHelper.getInstance().getmDaoSession().getDownloadChapterBeanDao().deleteAll();
e.onNext(new DownloadChapterBean());
} else {
// 如果书架列表为空,同样删除所有下载章节记录
DbHelper.getInstance().getmDaoSession().getDownloadChapterBeanDao().deleteAll();
e.onNext(new DownloadChapterBean());
}
@ -138,12 +163,14 @@ public class DownloadService extends Service {
.subscribe(new SimpleObserver<DownloadChapterBean>() {
@Override
public void onNext(DownloadChapterBean value) {
if (value.getNoteUrl() != null && value.getNoteUrl().length() > 0) {
if (value.getNoteUrl()!= null && value.getNoteUrl().length() > 0) {
// 如果章节有有效的URL开始下载该章节
downloading(value, 0);
} else {
Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> e) throws Exception {
// 如果章节URL为空删除所有下载章节记录
DbHelper.getInstance().getmDaoSession().getDownloadChapterBeanDao().deleteAll();
e.onNext(new Object());
e.onComplete();
@ -178,14 +205,17 @@ public class DownloadService extends Service {
}
}
// 具体下载章节的逻辑方法,处理下载过程中的重试、保存数据等操作
private void downloading(final DownloadChapterBean data, final int durTime) {
if (durTime < reTryTimes && isStartDownload) {
// 发送下载进度相关的事件,并显示通知
isProgress(data);
Observable.create(new ObservableOnSubscribe<BookContentBean>() {
@Override
public void subscribe(ObservableEmitter<BookContentBean> e) throws Exception {
// 查询数据库中是否已经存在该章节的内容
List<BookContentBean> result = DbHelper.getInstance().getmDaoSession().getBookContentBeanDao().queryBuilder().where(BookContentBeanDao.Properties.DurChapterUrl.eq(data.getDurChapterUrl())).list();
if (result != null && result.size() > 0) {
if (result!= null && result.size() > 0) {
e.onNext(result.get(0));
} else {
e.onNext(new BookContentBean());
@ -196,6 +226,7 @@ public class DownloadService extends Service {
@Override
public ObservableSource<BookContentBean> apply(final BookContentBean bookContentBean) throws Exception {
if (bookContentBean.getDurChapterUrl() == null || bookContentBean.getDurChapterUrl().length() <= 0) {
// 如果章节内容不存在通过WebBookModelImpl获取章节内容然后进行相关数据库操作插入内容、更新章节列表等
return WebBookModelImpl.getInstance().getBookContent(data.getDurChapterUrl(), data.getDurChapterIndex(), data.getTag()).map(new Function<BookContentBean, BookContentBean>() {
@Override
public BookContentBean apply(BookContentBean bookContentBean) throws Exception {
@ -208,6 +239,7 @@ public class DownloadService extends Service {
}
});
} else {
// 如果章节内容已经存在,直接删除下载章节记录并返回该章节内容
return Observable.create(new ObservableOnSubscribe<BookContentBean>() {
@Override
public void subscribe(ObservableEmitter<BookContentBean> e) throws Exception {
@ -224,18 +256,18 @@ public class DownloadService extends Service {
.subscribe(new SimpleObserver<BookContentBean>() {
@Override
public void onNext(BookContentBean value) {
if(isStartDownload){
if (isStartDownload) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if(isStartDownload){
if (isStartDownload) {
toDownload();
}else{
} else {
isPause();
}
}
},800);
}else{
}, 800);
} else {
isPause();
}
}
@ -252,6 +284,7 @@ public class DownloadService extends Service {
Observable.create(new ObservableOnSubscribe<Boolean>() {
@Override
public void subscribe(ObservableEmitter<Boolean> e) throws Exception {
// 如果重试次数超过限制或者下载失败,删除该下载章节记录
DbHelper.getInstance().getmDaoSession().getDownloadChapterBeanDao().delete(data);
e.onNext(true);
e.onComplete();
@ -262,18 +295,18 @@ public class DownloadService extends Service {
.subscribe(new SimpleObserver<Boolean>() {
@Override
public void onNext(Boolean value) {
if(isStartDownload){
if (isStartDownload) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if(isStartDownload){
if (isStartDownload) {
toDownload();
}else{
} else {
isPause();
}
}
},800);
}else{
}, 800);
} else {
isPause();
}
}
@ -281,7 +314,7 @@ public class DownloadService extends Service {
@Override
public void onError(Throwable e) {
e.printStackTrace();
if(!isStartDownload)
if (!isStartDownload)
isPause();
}
});
@ -290,16 +323,19 @@ public class DownloadService extends Service {
}
}
// 对外提供的启动下载的方法设置下载开始标记并调用toDownload方法
public void startDownload() {
isStartDownload = true;
toDownload();
}
// 对外提供的暂停下载的方法,设置下载暂停标记并取消所有通知
public void pauseDownload() {
isStartDownload = false;
notifyManager.cancelAll();
}
// 对外提供的取消下载的方法,先删除所有下载章节记录,然后暂停下载
public void cancelDownload() {
Observable.create(new ObservableOnSubscribe<Object>() {
@Override
@ -334,17 +370,18 @@ public class DownloadService extends Service {
});
}
// 处理下载暂停相关逻辑判断是否还有待下载章节根据情况发送相应的RxBus事件
private void isPause() {
isDownloading = false;
Observable.create(new ObservableOnSubscribe<DownloadChapterBean>() {
@Override
public void subscribe(ObservableEmitter<DownloadChapterBean> e) throws Exception {
List<BookShelfBean> bookShelfBeanList = DbHelper.getInstance().getmDaoSession().getBookShelfBeanDao().queryBuilder().orderDesc(BookShelfBeanDao.Properties.FinalDate).list();
if (bookShelfBeanList != null && bookShelfBeanList.size() > 0) {
if (bookShelfBeanList!= null && bookShelfBeanList.size() > 0) {
for (BookShelfBean bookItem : bookShelfBeanList) {
if (!bookItem.getTag().equals(BookShelfBean.LOCAL_TAG)) {
List<DownloadChapterBean> downloadChapterList = DbHelper.getInstance().getmDaoSession().getDownloadChapterBeanDao().queryBuilder().where(DownloadChapterBeanDao.Properties.NoteUrl.eq(bookItem.getNoteUrl())).orderAsc(DownloadChapterBeanDao.Properties.DurChapterIndex).limit(1).list();
if (downloadChapterList != null && downloadChapterList.size() > 0) {
if (downloadChapterList!= null && downloadChapterList.size() > 0) {
e.onNext(downloadChapterList.get(0));
e.onComplete();
return;
@ -364,9 +401,11 @@ public class DownloadService extends Service {
.subscribe(new SimpleObserver<DownloadChapterBean>() {
@Override
public void onNext(DownloadChapterBean value) {
if (value.getNoteUrl() != null && value.getNoteUrl().length() > 0){
if (value.getNoteUrl()!= null && value.getNoteUrl().length() > 0) {
// 如果还有待下载章节,发送暂停下载的监听事件
RxBus.get().post(RxBusTag.PAUSE_DOWNLOAD_LISTENER, new Object());
}else{
} else {
// 如果没有待下载章节,发送下载完成的监听事件
RxBus.get().post(RxBusTag.FINISH_DOWNLOAD_LISTENER, new Object());
}
}
@ -377,27 +416,43 @@ public class DownloadService extends Service {
}
});
}
// 方法作用处理下载进度相关操作发送下载进度相关信息到RxBus并创建和发送通知展示下载进度情况
// 参数downloadChapterBean表示下载章节相关的数据对象包含了如书籍名称、当前章节名称等信息
private void isProgress(DownloadChapterBean downloadChapterBean) {
// 通过RxBus发送下载进度监听器相关的事件传递下载章节数据对象以便其他地方能接收到并处理进度信息
RxBus.get().post(RxBusTag.PROGRESS_DOWNLOAD_LISTENER, downloadChapterBean);
// 创建一个意图用于启动主活动MainActivity这里的上下文是当前类所在的上下文环境
Intent mainIntent = new Intent(this, MainActivity.class);
// 根据创建的意图创建一个PendingIntent对象用于后续设置到通知中使得点击通知可以启动对应的活动
// 这里的请求码设置为0并且设置当有相同请求码的PendingIntent更新时更新当前的PendingIntent
PendingIntent mainPendingIntent = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//创建 Notification.Builder 对象
// 创建 Notification.Builder 对象,用于构建通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
// 设置通知的小图标这里使用的是资源文件中定义的ic_launcher图标
.setSmallIcon(R.mipmap.ic_launcher)
//点击通知后自动清除
// 设置点击通知后自动清除该通知
.setAutoCancel(true)
.setContentTitle("正在下载:"+downloadChapterBean.getBookName())
.setContentText(downloadChapterBean.getDurChapterName()==null?" ":downloadChapterBean.getDurChapterName())
// 设置通知的标题,展示正在下载的书籍名称
.setContentTitle("正在下载:" + downloadChapterBean.getBookName())
// 根据当前章节名称是否为空来设置通知的内容文本,如果为空则设置为空格,否则设置为章节名称
.setContentText(downloadChapterBean.getDurChapterName() == null? " " : downloadChapterBean.getDurChapterName())
// 将前面创建的PendingIntent设置到通知中使得点击通知能启动对应的活动
.setContentIntent(mainPendingIntent);
//发送通知
// 使用通知管理器发送通知notifiId应该是预先定义好的通知的唯一标识用于区分不同的通知
notifyManager.notify(notifiId, builder.build());
}
// 方法作用完成下载相关的后续操作发送下载完成的事件到RxBus取消所有通知并弹出提示告知用户全部离线章节下载完成
private void finishDownload() {
// 通过RxBus发送下载完成监听器相关的事件传递一个空的Object对象作为完成下载的一种通知机制方便其他地方监听并处理完成下载的情况
RxBus.get().post(RxBusTag.FINISH_DOWNLOAD_LISTENER, new Object());
// 取消所有已发送的通知
notifyManager.cancelAll();
// 在主线程中执行以下代码块用于弹出一个短暂显示的Toast提示信息告知用户全部离线章节下载完成
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
@ -406,6 +461,8 @@ public class DownloadService extends Service {
});
}
// 方法作用响应RxBus中PAUSE_DOWNLOAD事件调用pauseDownload方法此处假设pauseDownload方法在别处定义用于暂停下载任务相关操作
// 该方法被标注为订阅RxBus中特定标签的事件并且在主线程中执行
@Subscribe(
thread = EventThread.MAIN_THREAD,
tags = {
@ -416,6 +473,8 @@ public class DownloadService extends Service {
pauseDownload();
}
// 方法作用响应RxBus中START_DOWNLOAD事件调用startDownload方法此处假设startDownload方法在别处定义用于启动下载任务相关操作
// 该方法被标注为订阅RxBus中特定标签的事件并且在主线程中执行
@Subscribe(
thread = EventThread.MAIN_THREAD,
tags = {
@ -426,6 +485,8 @@ public class DownloadService extends Service {
startDownload();
}
// 方法作用响应RxBus中CANCEL_DOWNLOAD事件调用cancelDownload方法此处假设cancelDownload方法在别处定义用于取消下载任务相关操作
// 该方法被标注为订阅RxBus中特定标签的事件并且在主线程中执行
@Subscribe(
thread = EventThread.MAIN_THREAD,
tags = {
@ -436,6 +497,9 @@ public class DownloadService extends Service {
cancelDownload();
}
// 方法作用响应RxBus中ADD_DOWNLOAD_TASK事件调用addNewTask方法此处假设addNewTask方法在别处定义用于添加新的下载任务相关操作
// 该方法接收一个DownloadChapterListBean类型的对象newData从其中获取具体的数据假设是下载任务列表数据传递给addNewTask方法进行处理
// 该方法被标注为订阅RxBus中特定标签的事件并且在主线程中执行
@Subscribe(
thread = EventThread.MAIN_THREAD,
tags = {
@ -444,5 +508,4 @@ public class DownloadService extends Service {
)
public void addTask(DownloadChapterListBean newData) {
addNewTask(newData.getData());
}
}
}
Loading…
Cancel
Save