兼容第三方书源

pull/5/head
fengyuecanzhu 4 years ago
parent db31cf2500
commit 49acebd2f9
  1. 85
      app/src/main/java/xyz/fycz/myreader/entity/StrResponse.java
  2. 43
      app/src/main/java/xyz/fycz/myreader/entity/WebChapterBean.java
  3. 4
      app/src/main/java/xyz/fycz/myreader/greendao/entity/Book.java
  4. 2
      app/src/main/java/xyz/fycz/myreader/model/SearchEngine.java
  5. 8
      app/src/main/java/xyz/fycz/myreader/model/sourceAnalyzer/ThirdAnalyzer.java
  6. 2
      app/src/main/java/xyz/fycz/myreader/model/third/analyzeRule/AnalyzeByJSonPath.java
  7. 2
      app/src/main/java/xyz/fycz/myreader/model/third/analyzeRule/AnalyzeByJSoup.java
  8. 248
      app/src/main/java/xyz/fycz/myreader/model/third/analyzeRule/AnalyzeByRegex.java
  9. 2
      app/src/main/java/xyz/fycz/myreader/model/third/analyzeRule/AnalyzeByXPath.java
  10. 2
      app/src/main/java/xyz/fycz/myreader/model/third/analyzeRule/AnalyzeRule.java
  11. 2
      app/src/main/java/xyz/fycz/myreader/model/third/analyzeRule/AnalyzeUrl.java
  12. 65
      app/src/main/java/xyz/fycz/myreader/model/third/content/AnalyzeNextUrlTask.java
  13. 358
      app/src/main/java/xyz/fycz/myreader/model/third/content/BookChapterList.java
  14. 147
      app/src/main/java/xyz/fycz/myreader/model/third/content/BookContent.java
  15. 86
      app/src/main/java/xyz/fycz/myreader/model/third/content/BookInfo.java
  16. 375
      app/src/main/java/xyz/fycz/myreader/model/third/content/BookList.java
  17. 3
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  18. 11
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  19. 12
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  20. 2
      app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java
  21. 34
      app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java
  22. 33
      app/src/main/java/xyz/fycz/myreader/util/help/UTF8BOMFighter.java
  23. 4838
      app/src/main/java/xyz/fycz/myreader/util/utils/EncodingDetect.java
  24. 10
      app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java
  25. 149
      app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
  26. 1
      app/src/main/java/xyz/fycz/myreader/util/utils/RxUtils.java
  27. 153
      app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java
  28. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/ReadCrawlerUtil.java
  29. 14
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/BaseLocalCrawler.java
  30. 37
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/BaseReadCrawler.java
  31. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/BaseSourceCrawler.java
  32. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/BaseSourceCrawlerNoInfo.java
  33. 1
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/ReadCrawler.java
  34. 9
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Ben100ReadCrawler.java
  35. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/BiJianReadCrawler.java
  36. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/BiQuGe44ReadCrawler.java
  37. 4
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/BiQuGeReadCrawler.java
  38. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/CansShu99ReadCrawler.java
  39. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChaoXingReadCrawler.java
  40. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChuanQiReadCrawler.java
  41. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/DSTQReadCrawler.java
  42. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Du1DuReadCrawler.java
  43. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/EWenXueReadCrawler.java
  44. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/FYReadCrawler.java
  45. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/HongChenReadCrawler.java
  46. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/JiuTaoReadCrawler.java
  47. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LaoYaoReadCrawler.java
  48. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LiuLangCatReadCrawler.java
  49. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/LuoQiuReadCrawler.java
  50. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiQuReadCrawler.java
  51. 9
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiaoBiReadCrawler.java
  52. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/PiaoTianReadCrawler.java
  53. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/PinShuReadCrawler.java
  54. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/PinShuReadCrawler2.java
  55. 9
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QB5ReadCrawler.java
  56. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QiQiReadCrawler.java
  57. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QianDianReadCrawler.java
  58. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/QuanNovelReadCrawler.java
  59. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ReXueReadCrawler.java
  60. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ShiGuangReadCrawler.java
  61. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ShuHaiGeReadCrawler.java
  62. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/SoNovelReadCrawler.java
  63. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/TianLaiReadCrawler.java
  64. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/WoLongReadCrawler.java
  65. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XBiQuGeReadCrawler.java
  66. 8
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XS7ReadCrawler.java
  67. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XS7ReadCrawler2.java
  68. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XiaGuReadCrawler.java
  69. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/XingXingReadCrawler.java
  70. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/YanQingLouReadCrawler.java
  71. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/YunZhongReadCrawler.java
  72. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ZW37ReadCrawler.java
  73. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ZaiShuYuanReadCrawler.java
  74. 5
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ZuoPinReadCrawler.java
  75. 72
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/source/ThirdCrawler.java
  76. 8
      app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java

@ -0,0 +1,85 @@
package xyz.fycz.myreader.entity;
import android.text.TextUtils;
import android.util.Log;
import org.mozilla.universalchardet.UniversalDetector;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.util.help.UTF8BOMFighter;
import xyz.fycz.myreader.util.utils.EncodingDetect;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
/**
* @author fengyue
* @date 2021/5/14 12:05
*/
public class StrResponse {
private String encodeType;
private Response response;
private String body;
public String getEncodeType() {
return encodeType;
}
public void setEncodeType(String encodeType) {
this.encodeType = encodeType;
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
public String body() {
if (body == null) {
ResponseBody body = response.body();
if (body != null) {
try {
byte[] responseBytes = UTF8BOMFighter.removeUTF8BOM(body.bytes());
if (!TextUtils.isEmpty(encodeType)) {
try {
this.body = new String((responseBytes), Charset.forName(encodeType));
Log.d("Http: read finish", this.body);
return this.body;
} catch (Exception ignored) {
}
}
String charsetStr;
MediaType mediaType = body.contentType();
//根据http头判断
if (mediaType != null) {
Charset charset = mediaType.charset();
if (charset != null) {
this.body = new String((responseBytes), charset);
Log.d("Http: read finish", this.body);
return this.body;
}
}
//根据内容判断
charsetStr = EncodingDetect.getEncodeInHtml(responseBytes);
this.body = new String(responseBytes, Charset.forName(charsetStr));
Log.d("Http: read finish", this.body);
return this.body;
} catch (IOException e) {
e.printStackTrace();
this.body = "";
}
}
}
return this.body;
}
}

@ -0,0 +1,43 @@
package xyz.fycz.myreader.entity;
import java.util.LinkedHashSet;
import java.util.List;
import xyz.fycz.myreader.greendao.entity.Chapter;
public class WebChapterBean {
private String url;
private List<Chapter> data;
private LinkedHashSet<String> nextUrlList;
public WebChapterBean(String url) {
this.url = url;
}
public WebChapterBean(List<Chapter> data, LinkedHashSet<String> nextUrlList) {
this.data = data;
this.nextUrlList = nextUrlList;
}
public List<Chapter> getData() {
return data;
}
public void setData(List<Chapter> data) {
this.data = data;
}
public LinkedHashSet<String> getNextUrlList() {
return nextUrlList;
}
public String getUrl() {
return url;
}
public boolean noData() {
return data == null;
}
}

@ -13,6 +13,7 @@ import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Transient;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import java.io.Serializable;
import java.util.HashMap;
@ -296,6 +297,9 @@ public class Book implements Serializable {
}
public String getInfoUrl() {
if (StringHelper.isEmpty(infoUrl)){
return chapterUrl;
}
return this.infoUrl;
}

@ -138,7 +138,7 @@ public class SearchEngine {
if (searchSiteIndex < mSourceList.size()) {
ReadCrawler crawler = mSourceList.get(searchSiteIndex);
String searchKey = keyword;
if (crawler.getSearchCharset().toLowerCase().equals("gbk")) {
if (crawler.getSearchCharset() != null && crawler.getSearchCharset().toLowerCase().equals("gbk")) {
try {
searchKey = URLEncoder.encode(keyword, crawler.getSearchCharset());
} catch (UnsupportedEncodingException e) {

@ -1,8 +0,0 @@
package xyz.fycz.myreader.model.sourceAnalyzer;
/**
* @author fengyue
* @date 2021/5/14 9:13
*/
public class ThirdAnalyzer {
}

@ -1,4 +1,4 @@
package xyz.fycz.myreader.model.analyzeRule;
package xyz.fycz.myreader.model.third.analyzeRule;
import android.text.TextUtils;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.model.analyzeRule;
package xyz.fycz.myreader.model.third.analyzeRule;
import android.text.TextUtils;

@ -0,0 +1,248 @@
package xyz.fycz.myreader.model.third.analyzeRule;
import android.os.Build;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import static android.text.TextUtils.isEmpty;
public class AnalyzeByRegex {
// 纯java模式正则表达式获取书籍详情信息
public static void getInfoOfRegex(String res, String[] regs, int index,
Book book, AnalyzeRule analyzer, BookSource source, String baseUrl, String tag) throws Exception {
Matcher resM = Pattern.compile(regs[index]).matcher(res);
// 判断规则是否有效,当搜索列表规则无效时跳过详情页处理
if (!resM.find()) {
book.setChapterUrl(baseUrl);
return;
}
// 判断索引的规则是最后一个规则
if (index + 1 == regs.length) {
// 获取规则列表
HashMap<String, String> ruleMap = new HashMap<>();
ruleMap.put("BookName", source.getInfoRule().getName());
ruleMap.put("BookAuthor", source.getInfoRule().getAuthor());
ruleMap.put("BookKind", source.getInfoRule().getType());
ruleMap.put("LastChapter", source.getInfoRule().getLastChapter());
ruleMap.put("Introduce", source.getInfoRule().getDesc());
ruleMap.put("CoverUrl", source.getInfoRule().getImgUrl());
ruleMap.put("ChapterUrl", source.getInfoRule().getTocUrl());
// 分离规则参数
List<String> ruleName = new ArrayList<>();
List<List<String>> ruleParams = new ArrayList<>(); // 创建规则参数容器
List<List<Integer>> ruleTypes = new ArrayList<>(); // 创建规则类型容器
List<Boolean> hasVarParams = new ArrayList<>(); // 创建put&get标志容器
for (String key : ruleMap.keySet()) {
String val = ruleMap.get(key);
ruleName.add(key);
hasVarParams.add(!TextUtils.isEmpty(val) && (val.contains("@put") || val.contains("@get")));
List<String> ruleParam = new ArrayList<>();
List<Integer> ruleType = new ArrayList<>();
AnalyzeByRegex.splitRegexRule(val, ruleParam, ruleType);
ruleParams.add(ruleParam);
ruleTypes.add(ruleType);
}
// 提取规则内容
HashMap<String, String> ruleVal = new HashMap<>();
StringBuilder infoVal = new StringBuilder();
for (int i = ruleParams.size(); i-- > 0; ) {
List<String> ruleParam = ruleParams.get(i);
List<Integer> ruleType = ruleTypes.get(i);
infoVal.setLength(0);
for (int j = ruleParam.size(); j-- > 0; ) {
int regType = ruleType.get(j);
if (regType > 0) {
infoVal.insert(0, resM.group(regType));
} else if (regType < 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
infoVal.insert(0, resM.group(ruleParam.get(j)));
} else {
infoVal.insert(0, ruleParam.get(j));
}
}
ruleVal.put(ruleName.get(i), hasVarParams.get(i) ? checkKeys(infoVal.toString(), analyzer) : infoVal.toString());
}
// 保存详情信息
if (!isEmpty(ruleVal.get("BookName")))
book.setName(StringUtils.formatHtml(ruleVal.get("BookName")));
if (!isEmpty(ruleVal.get("BookAuthor")))
book.setAuthor(StringUtils.formatHtml(ruleVal.get("BookAuthor")));
if (!isEmpty(ruleVal.get("LastChapter"))) book.setNewestChapterTitle(ruleVal.get("LastChapter"));
if (!isEmpty(ruleVal.get("Introduce")))
book.setDesc(StringUtils.formatHtml(ruleVal.get("Introduce")));
if (!isEmpty(ruleVal.get("CoverUrl"))) book.setImgUrl(ruleVal.get("CoverUrl"));
if (!isEmpty(ruleVal.get("ChapterUrl"))) book.setChapterUrl(NetworkUtils.getAbsoluteURL(baseUrl, ruleVal.get("ChapterUrl")));
else book.setChapterUrl(baseUrl);
} else {
StringBuilder result = new StringBuilder();
do {
result.append(resM.group());
} while (resM.find());
getInfoOfRegex(result.toString(), regs, ++index, book, analyzer, source, baseUrl, tag);
}
}
// 正则表达式解析规则数据的通用方法(暂未使用,技术储备型代码)
public static void getInfoByRegex(String res, String[] regList, int regIndex,
HashMap<String, String> ruleMap, final List<HashMap<String, String>> ruleVals) throws Exception {
Matcher resM = Pattern.compile(regList[regIndex]).matcher(res);
// 判断规则是否有效
if (!resM.find()) {
return;
}
// 判断索引规则是否为最后一个
if (regIndex + 1 == regList.length) {
// 分离规则参数
List<String> ruleName = new ArrayList<>();
List<List<String>> ruleParams = new ArrayList<>(); // 创建规则参数容器
List<List<Integer>> ruleTypes = new ArrayList<>(); // 创建规则类型容器
List<Boolean> hasVarParams = new ArrayList<>(); // 创建put&get标志容器
for (String key : ruleMap.keySet()) {
String val = ruleMap.get(key);
ruleName.add(key);
hasVarParams.add(val.contains("@put") || val.contains("@get"));
List<String> ruleParam = new ArrayList<>();
List<Integer> ruleType = new ArrayList<>();
splitRegexRule(val, ruleParam, ruleType);
ruleParams.add(ruleParam);
ruleTypes.add(ruleType);
}
// 提取规则结果
do {
HashMap<String, String> ruleVal = new HashMap<>();
StringBuilder infoVal = new StringBuilder();
for (int i = ruleParams.size(); i-- > 0; ) {
List<String> ruleParam = ruleParams.get(i);
List<Integer> ruleType = ruleTypes.get(i);
infoVal.setLength(0);
for (int j = ruleParam.size(); j-- > 0; ) {
int regType = ruleType.get(j);
if (regType > 0) {
if (j == 0 && Objects.equals(ruleName.get(0), "ruleChapterName")) {
infoVal.insert(0, resM.group(regType) == null ? "" : "\uD83D\uDD12");
} else {
infoVal.insert(0, resM.group(regType));
}
} else if (regType < 0) {
if (j == 0 && Objects.equals(ruleName.get(0), "ruleChapterName")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
infoVal.insert(0, resM.group(ruleParam.get(j)) == null ? "" : "\uD83D\uDD12");
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
infoVal.insert(0, resM.group(ruleParam.get(j)));
}
}
} else {
infoVal.insert(0, ruleParam.get(j));
}
}
ruleVal.put(ruleName.get(i), infoVal.toString());
}
ruleVals.add(ruleVal);
} while (resM.find());
} else {
StringBuilder result = new StringBuilder();
do {
result.append(resM.group(0));
} while (resM.find());
getInfoByRegex(result.toString(), regList, ++regIndex, ruleMap, ruleVals);
}
}
// 拆分正则表达式替换规则(如:$\d{1,2}或${name}) /*注意:千万别用正则表达式拆分字符串,效率太低了!*/
public static void splitRegexRule(String str, final List<String> ruleParam, final List<Integer> ruleType) throws Exception {
if (TextUtils.isEmpty(str)) {
ruleParam.add("");
ruleType.add(0);
return;
}
int index = 0, start = 0, len = str.length();
while (index < len) {
if (str.charAt(index) == '$') {
if (str.charAt(index + 1) == '{') {
if (index > start) {
ruleParam.add(str.substring(start, index));
ruleType.add(0);
start = index;
}
for (index += 2; index < len; index++) {
if (str.charAt(index) == '}') {
ruleParam.add(str.substring(start + 2, index));
ruleType.add(-1);
start = ++index;
break;
} else if (str.charAt(index) == '$' || str.charAt(index) == '@') {
break;
}
}
} else if ((str.charAt(index + 1) >= '0') && (str.charAt(index + 1) <= '9')) {
if (index > start) {
ruleParam.add(str.substring(start, index));
ruleType.add(0);
start = index;
}
if ((index + 2 < len) && (str.charAt(index + 2) >= '0') && (str.charAt(index + 2) <= '9')) {
ruleParam.add(str.substring(start, index + 3));
ruleType.add(string2Int(ruleParam.get(ruleParam.size() - 1)));
start = index += 3;
} else {
ruleParam.add(str.substring(start, index + 2));
ruleType.add(string2Int(ruleParam.get(ruleParam.size() - 1)));
start = index += 2;
}
} else {
++index;
}
} else {
++index;
}
}
if (index > start) {
ruleParam.add(str.substring(start, index));
ruleType.add(0);
}
}
// 存取字符串中的put&get参数
public static String checkKeys(String str, AnalyzeRule analyzer) throws Exception {
if (str.contains("@put:{")) {
Matcher putMatcher = Pattern.compile("@put:\\{([^,]*):([^\\}]*)\\}").matcher(str);
while (putMatcher.find()) {
str = str.replace(putMatcher.group(0), "");
analyzer.put(putMatcher.group(1), putMatcher.group(2));
}
}
if (str.contains("@get:{")) {
Matcher getMatcher = Pattern.compile("@get:\\{([^\\}]*)\\}").matcher(str);
while (getMatcher.find()) {
str = str.replace(getMatcher.group(), analyzer.get(getMatcher.group(1)));
}
}
return str;
}
// String数字转int数字的高效方法(利用ASCII值判断)
public static int string2Int(String s) {
int r = 0;
char n;
for (int i = 0, l = s.length(); i < l; i++) {
n = s.charAt(i);
if (n >= '0' && n <= '9') {
r = r * 10 + (n - 0x30); //'0-9'的ASCII值为0x30-0x39
}
}
return r;
}
}

@ -1,4 +1,4 @@
package xyz.fycz.myreader.model.analyzeRule;
package xyz.fycz.myreader.model.third.analyzeRule;
import android.text.TextUtils;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.model.analyzeRule;
package xyz.fycz.myreader.model.third.analyzeRule;
import android.annotation.SuppressLint;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.model.analyzeRule;
package xyz.fycz.myreader.model.third.analyzeRule;
import android.annotation.SuppressLint;
import android.text.TextUtils;

@ -0,0 +1,65 @@
package xyz.fycz.myreader.model.third.content;
import java.util.List;
import java.util.Map;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import xyz.fycz.myreader.base.observer.MyObserver;
import xyz.fycz.myreader.entity.WebChapterBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeUrl;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
public class AnalyzeNextUrlTask {
private WebChapterBean webChapterBean;
private Callback callback;
private Book bookShelfBean;
private Map<String, String> headerMap;
private BookChapterList bookChapterList;
public AnalyzeNextUrlTask(BookChapterList bookChapterList, WebChapterBean webChapterBean, Book bookShelfBean, Map<String, String> headerMap) {
this.bookChapterList = bookChapterList;
this.webChapterBean = webChapterBean;
this.bookShelfBean = bookShelfBean;
this.headerMap = headerMap;
}
public AnalyzeNextUrlTask setCallback(Callback callback) {
this.callback = callback;
return this;
}
public void analyzeUrl(AnalyzeUrl analyzeUrl) {
OkHttpUtils.getStrResponse(analyzeUrl)
.flatMap(stringResponse ->
bookChapterList.analyzeChapterList(stringResponse.body(), bookShelfBean, headerMap))
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new MyObserver<List<Chapter>>() {
@Override
public void onSubscribe(Disposable d) {
callback.addDisposable(d);
}
@Override
public void onNext(List<Chapter> bookChapterBeans) {
callback.analyzeFinish(webChapterBean, bookChapterBeans);
}
@Override
public void onError(Throwable throwable) {
callback.onError(throwable);
}
});
}
public interface Callback {
void addDisposable(Disposable disposable);
void analyzeFinish(WebChapterBean bean, List<Chapter> bookChapterBeans);
void onError(Throwable throwable);
}
}

@ -0,0 +1,358 @@
package xyz.fycz.myreader.model.third.content;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import org.jsoup.nodes.Element;
import org.mozilla.javascript.NativeObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.Emitter;
import io.reactivex.Observable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.entity.StrResponse;
import xyz.fycz.myreader.entity.WebChapterBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.greendao.entity.rule.TocRule;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeByRegex;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeRule;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeUrl;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
public class BookChapterList {
private String tag;
private BookSource source;
private TocRule tocRule;
private List<WebChapterBean> webChapterBeans;
private boolean dx = false;
private boolean analyzeNextUrl;
private CompositeDisposable compositeDisposable;
private String chapterListUrl;
public BookChapterList(String tag, BookSource source, boolean analyzeNextUrl) {
this.tag = tag;
this.source = source;
this.analyzeNextUrl = analyzeNextUrl;
tocRule = source.getTocRule();
}
public Observable<List<Chapter>> analyzeChapterList(final String s, final Book book, Map<String, String> headerMap) {
return Observable.create(e -> {
if (TextUtils.isEmpty(s)) {
e.onError(new Throwable("目录获取失败" + book.getChapterUrl()));
return;
} else {
Log.d(tag, "┌成功获取目录页" + analyzeNextUrl);
Log.d(tag, "└" + book.getChapterUrl() + analyzeNextUrl);
}
book.setTag(tag);
AnalyzeRule analyzer = new AnalyzeRule(book);
String ruleChapterList = tocRule.getChapterList();
if (ruleChapterList != null && ruleChapterList.startsWith("-")) {
dx = true;
ruleChapterList = ruleChapterList.substring(1);
}
chapterListUrl = book.getChapterUrl();
WebChapterBean webChapterBean = analyzeChapterList(s, chapterListUrl, ruleChapterList, analyzeNextUrl, analyzer, dx);
final List<Chapter> chapterList = webChapterBean.getData();
final List<String> chapterUrlS = new ArrayList<>(webChapterBean.getNextUrlList());
if (chapterUrlS.isEmpty() || !analyzeNextUrl) {
finish(chapterList, e);
}
//下一页为单页
else if (chapterUrlS.size() == 1) {
List<String> usedUrl = new ArrayList<>();
usedUrl.add(book.getChapterUrl());
//循环获取直到下一页为空
Log.d(tag, "正在加载下一页");
while (!chapterUrlS.isEmpty() && !usedUrl.contains(chapterUrlS.get(0))) {
usedUrl.add(chapterUrlS.get(0));
AnalyzeUrl analyzeUrl = new AnalyzeUrl(chapterUrlS.get(0), headerMap, tag);
try {
String body;
StrResponse response = OkHttpUtils.getStrResponse(analyzeUrl)
.blockingFirst();
body = response.body();
webChapterBean = analyzeChapterList(body, chapterUrlS.get(0), ruleChapterList, false, analyzer, dx);
chapterList.addAll(webChapterBean.getData());
chapterUrlS.clear();
chapterUrlS.addAll(webChapterBean.getNextUrlList());
} catch (Exception exception) {
if (!e.isDisposed()) {
e.onError(exception);
}
}
}
Log.d(tag, "下一页加载完成共" + usedUrl.size() + "页");
finish(chapterList, e);
}
//下一页为多页
else {
Log.d(tag, "正在加载其它" + chapterUrlS.size() + "页");
compositeDisposable = new CompositeDisposable();
webChapterBeans = new ArrayList<>();
AnalyzeNextUrlTask.Callback callback = new AnalyzeNextUrlTask.Callback() {
@Override
public void addDisposable(Disposable disposable) {
compositeDisposable.add(disposable);
}
@Override
public void analyzeFinish(WebChapterBean bean, List<Chapter> chapterListBeans) {
if (nextUrlFinish(bean, chapterListBeans)) {
for (WebChapterBean chapterBean : webChapterBeans) {
chapterList.addAll(chapterBean.getData());
}
Log.d(tag, "其它页加载完成,目录共" + chapterList.size() + "条");
finish(chapterList, e);
}
}
@Override
public void onError(Throwable throwable) {
compositeDisposable.dispose();
e.onError(throwable);
}
};
for (String url : chapterUrlS) {
final WebChapterBean bean = new WebChapterBean(url);
webChapterBeans.add(bean);
}
for (WebChapterBean bean : webChapterBeans) {
BookChapterList bookChapterList = new BookChapterList(tag, source, false);
AnalyzeUrl analyzeUrl = new AnalyzeUrl(bean.getUrl(), headerMap, tag);
new AnalyzeNextUrlTask(bookChapterList, bean, book, headerMap)
.setCallback(callback)
.analyzeUrl(analyzeUrl);
}
}
});
}
private synchronized boolean nextUrlFinish(WebChapterBean webChapterBean, List<Chapter> bookChapterBeans) {
webChapterBean.setData(bookChapterBeans);
for (WebChapterBean bean : webChapterBeans) {
if (bean.noData()) return false;
}
return true;
}
private void finish(List<Chapter> chapterList, Emitter<List<Chapter>> emitter) {
//去除重复,保留后面的,先倒序,从后面往前判断
if (!dx) {
Collections.reverse(chapterList);
}
LinkedHashSet<Chapter> lh = new LinkedHashSet<>(chapterList);
chapterList = new ArrayList<>(lh);
Collections.reverse(chapterList);
Log.d(tag, "-目录解析完成" + analyzeNextUrl);
emitter.onNext(chapterList);
emitter.onComplete();
}
private WebChapterBean analyzeChapterList(String s, String chapterUrl, String ruleChapterList,
boolean printLog, AnalyzeRule analyzer, boolean dx) throws Exception {
List<String> nextUrlList = new ArrayList<>();
analyzer.setContent(s, chapterUrl);
if (!TextUtils.isEmpty(tocRule.getTocUrlNext()) && analyzeNextUrl) {
if (printLog) Log.d(tag, "┌获取目录下一页网址");
nextUrlList = analyzer.getStringList(tocRule.getTocUrlNext(), true);
int thisUrlIndex = nextUrlList.indexOf(chapterUrl);
if (thisUrlIndex != -1) {
nextUrlList.remove(thisUrlIndex);
}
if (printLog) Log.d(tag, "└" + nextUrlList.toString());
}
List<Chapter> chapterBeans = new ArrayList<>();
if (printLog) Log.d(tag, "┌解析目录列表");
// 仅使用java正则表达式提取目录列表
if (ruleChapterList.startsWith(":")) {
ruleChapterList = ruleChapterList.substring(1);
regexChapter(s, ruleChapterList.split("&&"), 0, analyzer, chapterBeans);
if (chapterBeans.size() == 0) {
if (printLog) Log.d(tag, "└找到 0 个章节");
return new WebChapterBean(chapterBeans, new LinkedHashSet<>(nextUrlList));
}
}
// 使用AllInOne规则模式提取目录列表
else if (ruleChapterList.startsWith("+")) {
ruleChapterList = ruleChapterList.substring(1);
List<Object> collections = analyzer.getElements(ruleChapterList);
if (collections.size() == 0) {
Log.d(tag, "└找到 0 个章节");
return new WebChapterBean(chapterBeans, new LinkedHashSet<>(nextUrlList));
}
String nameRule = tocRule.getChapterName();
String linkRule = tocRule.getChapterUrl();
String name = "";
String link = "";
for (Object object : collections) {
if (object instanceof NativeObject) {
name = String.valueOf(((NativeObject) object).get(nameRule));
link = String.valueOf(((NativeObject) object).get(linkRule));
} else if (object instanceof Element) {
name = ((Element) object).text();
link = ((Element) object).attr(linkRule);
}
addChapter(chapterBeans, name, link);
}
}
// 使用默认规则解析流程提取目录列表
else {
List<Object> collections = analyzer.getElements(ruleChapterList);
if (collections.size() == 0) {
Log.d(tag, "└找到 0 个章节");
return new WebChapterBean(chapterBeans, new LinkedHashSet<>(nextUrlList));
}
List<AnalyzeRule.SourceRule> nameRule = analyzer.splitSourceRule(tocRule.getChapterName());
List<AnalyzeRule.SourceRule> linkRule = analyzer.splitSourceRule(tocRule.getChapterUrl());
for (Object object : collections) {
analyzer.setContent(object, chapterUrl);
addChapter(chapterBeans, analyzer.getString(nameRule), analyzer.getString(linkRule));
}
}
if (printLog) Log.d(tag,"└找到 " + chapterBeans.size() + " 个章节");
Chapter firstChapter;
if (dx) {
if (printLog) Log.d(tag, "-倒序");
firstChapter = chapterBeans.get(chapterBeans.size() - 1);
} else {
firstChapter = chapterBeans.get(0);
}
if (printLog) Log.d(tag, "┌获取章节名称");
if (printLog) Log.d(tag, "└" + firstChapter.getTitle());
if (printLog) Log.d(tag, "┌获取章节网址");
if (printLog) Log.d(tag, "└" + firstChapter.getUrl());
return new WebChapterBean(chapterBeans, new LinkedHashSet<>(nextUrlList));
}
private void addChapter(final List<Chapter> chapterBeans, String name, String link) {
if (TextUtils.isEmpty(name)) return;
if (TextUtils.isEmpty(link)) link = chapterListUrl;
Chapter chapter = new Chapter();
chapter.setTitle(name);
chapter.setUrl(link);
chapterBeans.add(chapter);
}
// region 纯java模式正则表达式获取目录列表
private void regexChapter(String str, String[] regex, int index, AnalyzeRule analyzer, final List<Chapter> chapterBeans) throws Exception {
Matcher resM = Pattern.compile(regex[index]).matcher(str);
if (!resM.find()) {
return;
}
if (index + 1 == regex.length) {
// 获取解析规则
String nameRule = tocRule.getChapterName();
String linkRule = tocRule.getChapterUrl();
if (TextUtils.isEmpty(nameRule) || TextUtils.isEmpty(linkRule)) return;
// 替换@get规则
nameRule = analyzer.replaceGet(tocRule.getChapterName());
linkRule = analyzer.replaceGet(tocRule.getChapterUrl());
// 分离规则参数
List<String> nameParams = new ArrayList<>();
List<Integer> nameGroups = new ArrayList<>();
AnalyzeByRegex.splitRegexRule(nameRule, nameParams, nameGroups);
List<String> linkParams = new ArrayList<>();
List<Integer> linkGroups = new ArrayList<>();
AnalyzeByRegex.splitRegexRule(linkRule, linkParams, linkGroups);
// 是否包含VIP规则(hasVipRule>1 时视为包含vip规则)
int hasVipRule = 0;
for (int i = nameGroups.size(); i-- > 0; ) {
if (nameGroups.get(i) != 0) {
++hasVipRule;
}
}
String vipNameGroup = "";
int vipNumGroup = 0;
if ((nameGroups.get(0) != 0) && (hasVipRule > 1)) {
vipNumGroup = nameGroups.remove(0);
vipNameGroup = nameParams.remove(0);
}
// 创建结果缓存
StringBuilder cName = new StringBuilder();
StringBuilder cLink = new StringBuilder();
// 提取书籍目录
if (vipNumGroup != 0) {
do {
cName.setLength(0);
cLink.setLength(0);
for (int i = nameParams.size(); i-- > 0; ) {
if (nameGroups.get(i) > 0) {
cName.insert(0, resM.group(nameGroups.get(i)));
} else if (nameGroups.get(i) < 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
cName.insert(0, resM.group(nameParams.get(i)));
} else {
cName.insert(0, nameParams.get(i));
}
}
if (vipNumGroup > 0) {
cName.insert(0, resM.group(vipNumGroup) == null ? "" : "\uD83D\uDD12");
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
cName.insert(0, resM.group(vipNameGroup) == null ? "" : "\uD83D\uDD12");
} else {
cName.insert(0, vipNameGroup);
}
for (int i = linkParams.size(); i-- > 0; ) {
if (linkGroups.get(i) > 0) {
cLink.insert(0, resM.group(linkGroups.get(i)));
} else if (linkGroups.get(i) < 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
cLink.insert(0, resM.group(linkParams.get(i)));
} else {
cLink.insert(0, linkParams.get(i));
}
}
addChapter(chapterBeans, cName.toString(), cLink.toString());
} while (resM.find());
} else {
do {
cName.setLength(0);
cLink.setLength(0);
for (int i = nameParams.size(); i-- > 0; ) {
if (nameGroups.get(i) > 0) {
cName.insert(0, resM.group(nameGroups.get(i)));
} else if (nameGroups.get(i) < 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
cName.insert(0, resM.group(nameParams.get(i)));
} else {
cName.insert(0, nameParams.get(i));
}
}
for (int i = linkParams.size(); i-- > 0; ) {
if (linkGroups.get(i) > 0) {
cLink.insert(0, resM.group(linkGroups.get(i)));
} else if (linkGroups.get(i) < 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
cLink.insert(0, resM.group(linkParams.get(i)));
} else {
cLink.insert(0, linkParams.get(i));
}
}
addChapter(chapterBeans, cName.toString(), cLink.toString());
} while (resM.find());
}
} else {
StringBuilder result = new StringBuilder();
do {
result.append(resM.group(0));
} while (resM.find());
regexChapter(result.toString(), regex, ++index, analyzer, chapterBeans);
}
}
// endregion
}

@ -0,0 +1,147 @@
package xyz.fycz.myreader.model.third.content;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import io.reactivex.Observable;
import xyz.fycz.myreader.entity.StrResponse;
import xyz.fycz.myreader.greendao.GreenDaoManager;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.greendao.entity.rule.ContentRule;
import xyz.fycz.myreader.greendao.gen.ChapterDao;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeRule;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeUrl;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import static xyz.fycz.myreader.common.APPCONST.JS_PATTERN;
public class BookContent {
private String tag;
private ContentRule contentRule;
private String ruleBookContent;
private String baseUrl;
public BookContent(String tag, BookSource bookSource) {
this.tag = tag;
this.contentRule = bookSource.getContentRule();
ruleBookContent = contentRule.getContent();
if (ruleBookContent.startsWith("$") && !ruleBookContent.startsWith("$.")) {
ruleBookContent = ruleBookContent.substring(1);
Matcher jsMatcher = JS_PATTERN.matcher(ruleBookContent);
if (jsMatcher.find()) {
ruleBookContent = ruleBookContent.replace(jsMatcher.group(), "");
}
}
}
public Observable<String> analyzeBookContent(final StrResponse response, final Chapter chapterBean, final Chapter nextChapterBean, Book book, Map<String, String> headerMap) {
baseUrl = NetworkUtils.getUrl(response.getResponse());
return analyzeBookContent(response.body(), chapterBean, nextChapterBean, book, headerMap);
}
public Observable<String> analyzeBookContent(final String s, final Chapter chapterBean, final Chapter nextChapterBean, Book book, Map<String, String> headerMap) {
return Observable.create(e -> {
if (TextUtils.isEmpty(s)) {
e.onError(new Throwable("内容获取失败" + chapterBean.getUrl()));
return;
}
if (TextUtils.isEmpty(baseUrl)) {
baseUrl = NetworkUtils.getAbsoluteURL(book.getChapterUrl(), chapterBean.getUrl());
}
Log.d(tag, "┌成功获取正文页");
Log.d(tag, "└" + baseUrl);
AnalyzeRule analyzer = new AnalyzeRule(book);
WebContentBean webContentBean = analyzeBookContent(analyzer, s, chapterBean.getUrl(), baseUrl);
StringBuilder content = new StringBuilder();
content.append(webContentBean.content);
/* 处理分页
*/
if (!TextUtils.isEmpty(webContentBean.nextUrl)) {
List<String> usedUrlList = new ArrayList<>();
usedUrlList.add(chapterBean.getUrl());
Chapter nextChapter;
if (nextChapterBean != null) {
nextChapter = nextChapterBean;
} else {
nextChapter = GreenDaoManager.getDaoSession().getChapterDao().queryBuilder()
.where(ChapterDao.Properties.Url.eq(chapterBean.getUrl()),
ChapterDao.Properties.Number.eq(chapterBean.getNumber() + 1))
.build().unique();
}
while (!TextUtils.isEmpty(webContentBean.nextUrl) && !usedUrlList.contains(webContentBean.nextUrl)) {
usedUrlList.add(webContentBean.nextUrl);
if (nextChapter != null && NetworkUtils.getAbsoluteURL(baseUrl, webContentBean.nextUrl).equals(NetworkUtils.getAbsoluteURL(baseUrl, nextChapter.getUrl()))) {
break;
}
AnalyzeUrl analyzeUrl = new AnalyzeUrl(webContentBean.nextUrl, headerMap, tag);
try {
String body;
StrResponse response = OkHttpUtils.getStrResponse(analyzeUrl).blockingFirst();
body = response.body();
webContentBean = analyzeBookContent(analyzer, body, webContentBean.nextUrl, baseUrl);
if (!TextUtils.isEmpty(webContentBean.content)) {
content.append("\n").append(webContentBean.content);
}
} catch (Exception exception) {
if (!e.isDisposed()) {
e.onError(exception);
}
}
}
}
/*String replaceRule = bookSourceBean.getRuleBookContentReplace();
if (replaceRule != null && replaceRule.trim().length() > 0) {
analyzer.setContent(bookContentBean.getDurChapterContent());
bookContentBean.setDurChapterContent(analyzer.getString(replaceRule));
}*/
e.onNext(content.toString());
e.onComplete();
});
}
public WebContentBean analyzeBookContent(AnalyzeRule analyzer, final String s, final String chapterUrl, String baseUrl) throws Exception {
WebContentBean webContentBean = new WebContentBean();
analyzer.setContent(s, NetworkUtils.getAbsoluteURL(baseUrl, chapterUrl));
Log.d(tag, "┌解析正文内容");
if (ruleBookContent.equals("all") || ruleBookContent.contains("@all")) {
webContentBean.content = analyzer.getString(ruleBookContent);
}
else {
webContentBean.content = StringUtils.formatHtml(analyzer.getString(ruleBookContent));
}
Log.d(tag, "└" + webContentBean.content);
String nextUrlRule = contentRule.getContentUrlNext();
if (!TextUtils.isEmpty(nextUrlRule)) {
Log.d(tag, "┌解析下一页url");
webContentBean.nextUrl = analyzer.getString(nextUrlRule, true);
Log.d(tag, "└" + webContentBean.nextUrl);
}
return webContentBean;
}
private static class WebContentBean {
private String content;
private String nextUrl;
private WebContentBean() {
}
}
}

@ -0,0 +1,86 @@
package xyz.fycz.myreader.model.third.content;
import android.text.TextUtils;
import android.util.Log;
import io.reactivex.Observable;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.greendao.entity.rule.InfoRule;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeRule;
import xyz.fycz.myreader.util.utils.StringUtils;
import static android.text.TextUtils.isEmpty;
public class BookInfo {
private String tag;
private String sourceName;
private BookSource source;
public BookInfo(String tag, String sourceName, BookSource source) {
this.tag = tag;
this.sourceName = sourceName;
this.source = source;
}
public Observable<Book> analyzeBookInfo(String s, final Book book) {
return Observable.create(e -> {
String baseUrl = book.getInfoUrl();
InfoRule infoRule = source.getInfoRule();
if (TextUtils.isEmpty(s)) {
e.onError(new Throwable("书籍信息获取失败" + baseUrl));
return;
} else {
Log.d(tag, "┌成功获取详情页");
Log.d(tag, "└" + baseUrl);
}
book.setTag(tag);
book.setSource(source.getSourceUrl());
AnalyzeRule analyzer = new AnalyzeRule(book);
analyzer.setContent(s, baseUrl);
Log.d(tag, "┌获取书名");
String bookName = StringUtils.formatHtml(analyzer.getString(infoRule.getName()));
if (!isEmpty(bookName)) book.setName(bookName);
Log.d(tag, "└" + bookName);
Log.d(tag, "┌获取作者");
String bookAuthor = StringUtils.formatHtml(analyzer.getString(infoRule.getAuthor()));
if (!isEmpty(bookAuthor)) book.setAuthor(bookAuthor);
Log.d(tag, "└" + bookAuthor);
Log.d(tag, "┌获取分类");
String bookKind = analyzer.getString(infoRule.getType());
Log.d(tag, "└" + bookKind);
Log.d(tag, "┌获取最新章节");
String bookLastChapter = analyzer.getString(infoRule.getLastChapter());
if (!isEmpty(bookLastChapter)) book.setNewestChapterTitle(bookLastChapter);
Log.d(tag, "└" + bookLastChapter);
Log.d(tag, "┌获取简介");
String bookIntroduce = analyzer.getString(infoRule.getDesc());
if (!isEmpty(bookIntroduce)) book.setDesc(bookIntroduce);
Log.d(tag, "└" + bookIntroduce);
Log.d(tag, "┌获取封面");
String bookCoverUrl = analyzer.getString(infoRule.getImgUrl(), true);
if (!isEmpty(bookCoverUrl)) book.setImgUrl(bookCoverUrl);
Log.d(tag, "└" + bookCoverUrl);
Log.d(tag, "┌获取目录网址");
String bookCatalogUrl = analyzer.getString(infoRule.getTocUrl(), true);
if (isEmpty(bookCatalogUrl)) bookCatalogUrl = baseUrl;
book.setChapterUrl(bookCatalogUrl);
//如果目录页和详情页相同,暂存页面内容供获取目录用
//if (bookCatalogUrl.equals(baseUrl)) bookInfoBean.setChapterListHtml(s);
Log.d(tag, "└" + book.getChapterUrl());
Log.d(tag, "-详情页解析完成");
e.onNext(book);
e.onComplete();
});
}
}

@ -0,0 +1,375 @@
package xyz.fycz.myreader.model.third.content;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import org.mozilla.javascript.NativeObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.Observable;
import xyz.fycz.myreader.entity.StrResponse;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.greendao.entity.rule.FindRule;
import xyz.fycz.myreader.greendao.entity.rule.InfoRule;
import xyz.fycz.myreader.greendao.entity.rule.SearchRule;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeByRegex;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeRule;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import static android.text.TextUtils.isEmpty;
public class BookList {
private String tag;
private String sourceName;
private BookSource bookSource;
private boolean isFind;
//规则
private String ruleList;
private String ruleName;
private String ruleAuthor;
private String ruleKind;
private String ruleIntroduce;
private String ruleLastChapter;
private String ruleCoverUrl;
private String ruleNoteUrl;
public BookList(String tag, String sourceName, BookSource bookSource, boolean isFind) {
this.tag = tag;
this.sourceName = sourceName;
this.bookSource = bookSource;
this.isFind = isFind;
}
public Observable<List<Book>> analyzeSearchBook(final StrResponse strResponse) {
return Observable.create(e -> {
String baseUrl;
baseUrl = NetworkUtils.getUrl(strResponse.getResponse());
String body = strResponse.body();
if (TextUtils.isEmpty(body)) {
e.onError(new Throwable(String.format("访问网站失败%s", baseUrl)));
return;
} else {
Log.d(tag, "┌成功获取搜索结果");
Log.d(tag, "└" + baseUrl);
}
List<Book> books = new ArrayList<>();
AnalyzeRule analyzer = new AnalyzeRule(null);
analyzer.setContent(body, baseUrl);
//如果符合详情页url规则
initRule();
List<Object> collections;
boolean reverse = false;
boolean allInOne = false;
if (ruleList.startsWith("-")) {
reverse = true;
ruleList = ruleList.substring(1);
}
// 仅使用java正则表达式提取书籍列表
if (ruleList.startsWith(":")) {
ruleList = ruleList.substring(1);
Log.d(tag, "┌解析搜索列表");
getBooksOfRegex(body, ruleList.split("&&"), 0, analyzer, books);
} else {
if (ruleList.startsWith("+")) {
allInOne = true;
ruleList = ruleList.substring(1);
}
//获取列表
Log.d(tag, "┌解析搜索列表");
collections = analyzer.getElements(ruleList);
if (collections.size() == 0) {
Log.d(tag, "└搜索列表为空,当做详情页处理");
Book item = getItem(analyzer, baseUrl);
if (item != null) {
//item.setBookInfoHtml(body);
books.add(item);
}
} else {
Log.d(tag, "└找到 " + collections.size() + " 个匹配的结果");
if (allInOne) {
for (int i = 0; i < collections.size(); i++) {
Object object = collections.get(i);
Book item = getItemAllInOne(analyzer, object, baseUrl, i == 0);
if (item != null) {
//如果网址相同则缓存
/*if (baseUrl.equals(item.getNoteUrl())) {
item.setBookInfoHtml(body);
}*/
books.add(item);
}
}
} else {
for (int i = 0; i < collections.size(); i++) {
Object object = collections.get(i);
analyzer.setContent(object, baseUrl);
Book item = getItemInList(analyzer, baseUrl, i == 0);
if (item != null) {
//如果网址相同则缓存
/*if (baseUrl.equals(item.getNoteUrl())) {
item.setBookInfoHtml(body);
}*/
books.add(item);
}
}
}
}
}
if (books.size() > 1 && reverse) {
Collections.reverse(books);
}
if (books.isEmpty()) {
e.onError(new Throwable("未获取到书名"));
return;
}
Log.d(tag, "-书籍列表解析结束");
e.onNext(books);
e.onComplete();
});
}
private void initRule() {
if (isFind && !TextUtils.isEmpty(bookSource.getFindRule().getBookList())) {
FindRule findRule = bookSource.getFindRule();
ruleList = findRule.getBookList();
ruleName = findRule.getName();
ruleAuthor = findRule.getAuthor();
ruleKind = findRule.getType();
ruleIntroduce = findRule.getDesc();
ruleCoverUrl = findRule.getImgUrl();
ruleLastChapter = findRule.getLastChapter();
ruleNoteUrl = findRule.getTocUrl();
} else {
SearchRule searchRule = bookSource.getSearchRule();
ruleList = searchRule.getList();
ruleName = searchRule.getName();
ruleAuthor = searchRule.getAuthor();
ruleKind = searchRule.getType();
ruleIntroduce = searchRule.getDesc();
ruleCoverUrl = searchRule.getImgUrl();
ruleLastChapter = searchRule.getLastChapter();
ruleNoteUrl = searchRule.getTocUrl();
}
}
/**
* 详情页
*/
private Book getItem(AnalyzeRule analyzer, String baseUrl) throws Exception {
InfoRule infoRule = bookSource.getInfoRule();
Book item = new Book();
analyzer.setBook(item);
item.setTag(tag);
item.setSource(bookSource.getSourceUrl());
item.setChapterUrl(baseUrl);
Log.d(tag, ">书籍网址:" + baseUrl);
Log.d(tag, "┌获取书名");
String bookName = StringUtils.formatHtml(analyzer.getString(infoRule.getName()));
Log.d(tag, "└" + bookName);
if (!TextUtils.isEmpty(bookName)) {
item.setName(bookName);
Log.d(tag, "┌获取作者");
item.setAuthor(StringUtils.formatHtml(analyzer.getString(infoRule.getAuthor())));
Log.d(tag, "└" + item.getAuthor());
Log.d(tag, "┌获取分类");
item.setType(analyzer.getString(infoRule.getType()));
Log.d(tag, "└" + item.getType());
Log.d(tag, "┌获取最新章节");
item.setNewestChapterTitle(analyzer.getString(infoRule.getLastChapter()));
Log.d(tag, "└" + item.getNewestChapterId());
Log.d(tag, "┌获取简介");
item.setDesc(analyzer.getString(infoRule.getDesc()));
Log.d(tag, "└" + item.getDesc());
Log.d(tag, "┌获取封面");
item.setImgUrl(analyzer.getString(infoRule.getImgUrl(), true));
Log.d(tag, "└" + item.getImgUrl());
return item;
}
return null;
}
private Book getItemAllInOne(AnalyzeRule analyzer, Object object, String baseUrl, boolean printLog) {
Book item = new Book();
analyzer.setBook(item);
NativeObject nativeObject = (NativeObject) object;
if (printLog) Log.d(tag, "┌获取书名");
String bookName = StringUtils.formatHtml(String.valueOf(nativeObject.get(ruleName)));
if (printLog) Log.d(tag, "└" + bookName);
if (!isEmpty(bookName)) {
item.setTag(tag);
item.setSource(bookSource.getSourceUrl());
item.setName(bookName);
if (printLog) Log.d(tag, "┌获取作者");
item.setAuthor(StringUtils.formatHtml(String.valueOf(nativeObject.get(ruleAuthor))));
if (printLog) Log.d(tag, "└" + item.getAuthor());
if (printLog) Log.d(tag, "┌获取分类");
item.setType(String.valueOf(nativeObject.get(ruleKind)));
if (printLog) Log.d(tag,"└" + item.getType());
if (printLog) Log.d(tag, "┌获取最新章节");
item.setNewestChapterTitle(String.valueOf(nativeObject.get(ruleLastChapter)));
if (printLog) Log.d(tag, "└" + item.getNewestChapterId());
if (printLog) Log.d(tag, "┌获取简介");
item.setDesc(String.valueOf(nativeObject.get(ruleIntroduce)));
if (printLog) Log.d(tag, "└" + item.getDesc());
if (printLog) Log.d(tag, "┌获取封面");
if (!isEmpty(ruleCoverUrl))
item.setImgUrl(NetworkUtils.getAbsoluteURL(baseUrl, String.valueOf(nativeObject.get(ruleCoverUrl))));
if (printLog) Log.d(tag, "└" + item.getImgUrl());
if (printLog) Log.d(tag, "┌获取书籍网址");
String resultUrl = String.valueOf(nativeObject.get(ruleNoteUrl));
if (isEmpty(resultUrl)) resultUrl = baseUrl;
item.setChapterUrl(resultUrl);
if (printLog) Log.d(tag, "└" + item.getChapterUrl());
return item;
}
return null;
}
private Book getItemInList(AnalyzeRule analyzer, String baseUrl, boolean printLog) throws
Exception {
Book item = new Book();
analyzer.setBook(item);
if (printLog) Log.d(tag, "┌获取书名");
String bookName = StringUtils.formatHtml(analyzer.getString(ruleName));
if (printLog) Log.d(tag, "└" + bookName);
if (!TextUtils.isEmpty(bookName)) {
item.setTag(tag);
item.setSource(bookSource.getSourceUrl());
item.setName(bookName);
if (printLog) Log.d(tag, "┌获取作者");
item.setAuthor(StringUtils.formatHtml(analyzer.getString(ruleAuthor)));
if (printLog) Log.d(tag, "└" + item.getAuthor());
if (printLog) Log.d(tag, "┌获取分类");
item.setType(analyzer.getString(ruleKind));
if (printLog) Log.d(tag, "└" + item.getType());
if (printLog) Log.d(tag, "┌获取最新章节");
item.setNewestChapterTitle(analyzer.getString(ruleLastChapter));
if (printLog) Log.d(tag, "└" + item.getNewestChapterTitle());
if (printLog) Log.d(tag, "┌获取简介");
item.setDesc(analyzer.getString(ruleIntroduce));
if (printLog) Log.d(tag, "└" + item.getDesc());
if (printLog) Log.d(tag, "┌获取封面");
item.setImgUrl(analyzer.getString(ruleCoverUrl, true));
if (printLog) Log.d(tag, "└" + item.getImgUrl());
Log.d(tag, "┌获取书籍网址");
String resultUrl = analyzer.getString(ruleNoteUrl, true);
if (isEmpty(resultUrl)) resultUrl = baseUrl;
item.setChapterUrl(resultUrl);
if (printLog) Log.d(tag, "└" + item.getChapterUrl());
return item;
}
return null;
}
// 纯java模式正则表达式获取书籍列表
private void getBooksOfRegex(String res, String[] regs,
int index, AnalyzeRule analyzer, final List<Book> books) throws Exception {
Matcher resM = Pattern.compile(regs[index]).matcher(res);
String baseUrl = analyzer.getBaseUrl();
// 判断规则是否有效,当搜索列表规则无效时当作详情页处理
if (!resM.find()) {
books.add(getItem(analyzer, baseUrl));
return;
}
// 判断索引的规则是最后一个规则
if (index + 1 == regs.length) {
// 获取规则列表
HashMap<String, String> ruleMap = new HashMap<>();
ruleMap.put("ruleName", ruleName);
ruleMap.put("ruleAuthor", ruleAuthor);
ruleMap.put("ruleKind", ruleKind);
ruleMap.put("ruleLastChapter", ruleLastChapter);
ruleMap.put("ruleIntroduce", ruleIntroduce);
ruleMap.put("ruleCoverUrl", ruleCoverUrl);
ruleMap.put("ruleNoteUrl", ruleNoteUrl);
// 分离规则参数
List<String> ruleName = new ArrayList<>();
List<List<String>> ruleParams = new ArrayList<>(); // 创建规则参数容器
List<List<Integer>> ruleTypes = new ArrayList<>(); // 创建规则类型容器
List<Boolean> hasVarParams = new ArrayList<>(); // 创建put&get标志容器
for (String key : ruleMap.keySet()) {
String val = ruleMap.get(key);
ruleName.add(key);
hasVarParams.add(!TextUtils.isEmpty(val) && (val.contains("@put") || val.contains("@get")));
List<String> ruleParam = new ArrayList<>();
List<Integer> ruleType = new ArrayList<>();
AnalyzeByRegex.splitRegexRule(val, ruleParam, ruleType);
ruleParams.add(ruleParam);
ruleTypes.add(ruleType);
}
// 提取书籍列表
do {
// 新建书籍容器
Book item = new Book();
item.setTag(tag);
item.setSource(bookSource.getSourceUrl());
analyzer.setBook(item);
// 提取规则内容
HashMap<String, String> ruleVal = new HashMap<>();
StringBuilder infoVal = new StringBuilder();
for (int i = ruleParams.size(); i-- > 0; ) {
List<String> ruleParam = ruleParams.get(i);
List<Integer> ruleType = ruleTypes.get(i);
infoVal.setLength(0);
for (int j = ruleParam.size(); j-- > 0; ) {
int regType = ruleType.get(j);
if (regType > 0) {
infoVal.insert(0, resM.group(regType));
} else if (regType < 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
infoVal.insert(0, resM.group(ruleParam.get(j)));
} else {
infoVal.insert(0, ruleParam.get(j));
}
}
ruleVal.put(ruleName.get(i), hasVarParams.get(i) ? AnalyzeByRegex.checkKeys(infoVal.toString(), analyzer) : infoVal.toString());
}
// 保存当前节点的书籍信息
item.setName(StringUtils.formatHtml(ruleVal.get("ruleName")));
item.setAuthor(StringUtils.formatHtml(ruleVal.get("ruleAuthor")));
item.setType(ruleVal.get("ruleKind"));
item.setNewestChapterTitle(ruleVal.get("ruleLastChapter"));
item.setDesc(ruleVal.get("ruleIntroduce"));
item.setImgUrl(ruleVal.get("ruleCoverUrl"));
item.setChapterUrl(NetworkUtils.getAbsoluteURL(baseUrl, ruleVal.get("ruleNoteUrl")));
books.add(item);
// 判断搜索结果是否为详情页
if (books.size() == 1 && (isEmpty(ruleVal.get("ruleNoteUrl")) || ruleVal.get("ruleNoteUrl").equals(baseUrl))) {
books.get(0).setChapterUrl(baseUrl);
//books.get(0).setBookInfoHtml(res);
return;
}
} while (resM.find());
// 输出调试信息
Log.d(tag, "└找到 " + books.size() + " 个匹配的结果");
Log.d(tag, "┌获取书名");
Log.d(tag, "└" + books.get(0).getName());
Log.d(tag, "┌获取作者");
Log.d(tag, "└" + books.get(0).getAuthor());
Log.d(tag, "┌获取分类");
Log.d(tag, "└" + books.get(0).getType());
Log.d(tag, "┌获取最新章节");
Log.d(tag, "└" + books.get(0).getNewestChapterTitle());
Log.d(tag, "┌获取简介");
Log.d(tag, "└" + books.get(0).getDesc());
Log.d(tag, "┌获取封面");
Log.d(tag, "└" + books.get(0).getImgUrl());
Log.d(tag, "┌获取书籍");
Log.d(tag, "└" + books.get(0).getChapterUrl());
} else {
StringBuilder result = new StringBuilder();
do {
result.append(resM.group());
} while (resM.find());
getBooksOfRegex(result.toString(), regs, ++index, analyzer, books);
}
}
}

@ -81,6 +81,7 @@ import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.source.ThirdCrawler;
/**
* @author fengyue
@ -460,7 +461,7 @@ public class BookDetailedActivity extends BaseActivity {
mChapters = mChapterService.findBookAllChapterByBookId(mBook.getId());
}
if (chaptersDis != null) chaptersDis.dispose();
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler)
CommonApi.getBookChapters(mBook, mReadCrawler)
.flatMap((Function<List<Chapter>, ObservableSource<Boolean>>) chapters -> saveChapters(chapters, isChangeSource)).compose(RxUtils::toSimpleSingle)
.subscribe(new MyObserver<Boolean>() {
@Override

@ -1008,19 +1008,16 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
});
} else {
mPageLoader.setmStatus(PageLoader.STATUS_LOADING_CHAPTER);
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler).flatMap(chapters -> Observable.create(emitter -> {
CommonApi.getBookChapters(mBook, mReadCrawler).flatMap(chapters -> Observable.create(emitter -> {
updateAllOldChapterData(chapters);
initChapters();
emitter.onNext(chapters);
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
public void onComplete() {
mPageLoader.setmStatus(PageLoader.STATUS_LOADING);
}
@Override
public void onNext(@NotNull Object o) {
mPageLoader.setmStatus(PageLoader.STATUS_LOADING);
}
@Override
@ -1588,7 +1585,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
if (StringHelper.isEmpty(chapter.getBookId())) {
chapter.setId(mBook.getId());
}
CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler)
CommonApi.getChapterContent(chapter, mBook, mReadCrawler)
.subscribeOn(Schedulers.from(App.getApplication().getmFixedThreadPool()))
.subscribe(new MyObserver<String>() {
@Override

@ -406,22 +406,18 @@ public class BookcasePresenter implements BasePresenter {
Thread update = new Thread(() -> {
final ArrayList<Chapter> mChapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(book.getId());
final ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(book.getSource());
CommonApi.getBookChapters(book.getChapterUrl(), mReadCrawler).flatMap(chapters -> Observable.create(emitter -> {
CommonApi.getBookChapters(book, mReadCrawler).flatMap(chapters -> Observable.create(emitter -> {
int noReadNum = chapters.size() - book.getChapterTotalNum();
book.setNoReadNum(Math.max(noReadNum, 0));
book.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle());
mBookcaseAdapter.getIsLoading().put(book.getId(), false);
mChapterService.updateAllOldChapterData(mChapters, chapters, book.getId());
mBookService.updateEntity(book);
emitter.onNext(book);
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
public void onNext(@NotNull Object o) {
}
@Override
public void onComplete() {
mHandler.sendMessage(mHandler.obtainMessage(1));
}
@ -784,11 +780,13 @@ public class BookcasePresenter implements BasePresenter {
chapter.setBookId(book.getId());
}
ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(book.getSource());
CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler).flatMap(s -> Observable.create(emitter -> {
CommonApi.getChapterContent(chapter, book, mReadCrawler).flatMap(s -> Observable.create(emitter -> {
downloadingChapter = chapter.getTitle();
mChapterService.saveOrUpdateChapter(chapter, s);
successCathe++;
curCacheChapterNum++;
emitter.onNext(chapter);
emitter.onComplete();
})).subscribeOn(Schedulers.from(App.getApplication().getmFixedThreadPool())).subscribe(new MyObserver<Object>() {
@Override
public void onNext(@NotNull Object o) {

@ -67,7 +67,7 @@ public class CatalogPresenter implements BasePresenter {
return;
}
mCatalogFragment.getPbLoading().setVisibility(View.VISIBLE);
CommonApi.getBookChapters(mBook.getChapterUrl(), ReadCrawlerUtil.getReadCrawler(mBook.getSource()))
CommonApi.getBookChapters(mBook, ReadCrawlerUtil.getReadCrawler(mBook.getSource()))
.compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<List<Chapter>>() {
@Override
public void onNext(@NotNull List<Chapter> chapters) {

@ -9,11 +9,7 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import xyz.fycz.myreader.model.analyzeRule.AnalyzeUrl;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeUrl;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
@ -26,33 +22,7 @@ public interface JsExtensions {
default String ajax(String urlStr) {
try {
AnalyzeUrl analyzeUrl = new AnalyzeUrl(urlStr);
String res = Observable.create((ObservableOnSubscribe<String>) emitter -> {
StringBuilder sb = new StringBuilder();
for (String key : analyzeUrl.getQueryMap().keySet()){
sb.append(key).append("=").append(analyzeUrl.getQueryMap().get(key));
sb.append("&");
}
sb.deleteCharAt(sb.lastIndexOf("&"));
String body = sb.toString();
switch (analyzeUrl.getUrlMode()){
case GET:
String url = analyzeUrl.getUrl() + "?" + body;
emitter.onNext(OkHttpUtils.getHtml(url, analyzeUrl.getCharCode(),
analyzeUrl.getHeaderMap()));
break;
case POST:
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody requestBody = RequestBody.create(mediaType, body);
emitter.onNext(OkHttpUtils.getHtml(analyzeUrl.getUrl(), requestBody,
analyzeUrl.getCharCode(), analyzeUrl.getHeaderMap()));
break;
default:
emitter.onNext(OkHttpUtils.getHtml(analyzeUrl.getUrl(),
analyzeUrl.getCharCode(), analyzeUrl.getHeaderMap()));
break;
}
}).blockingFirst();
return res;
return OkHttpUtils.getStrResponse(analyzeUrl).blockingFirst().body();
} catch (Exception e) {
return e.getLocalizedMessage();
}

@ -0,0 +1,33 @@
package xyz.fycz.myreader.util.help;
public class UTF8BOMFighter {
private static final byte[] UTF8_BOM_BYTES = new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF};
private UTF8BOMFighter() {
}
static public String removeUTF8BOM(String xmlText) {
byte[] bytes = xmlText.getBytes();
boolean containsBOM = bytes.length > 3
&& bytes[0] == UTF8_BOM_BYTES[0]
&& bytes[1] == UTF8_BOM_BYTES[1]
&& bytes[2] == UTF8_BOM_BYTES[2];
if (containsBOM) {
xmlText = new String(bytes, 3, bytes.length - 3);
}
return xmlText;
}
static public byte[] removeUTF8BOM(byte[] bytes) {
boolean containsBOM = bytes.length > 3
&& bytes[0] == UTF8_BOM_BYTES[0]
&& bytes[1] == UTF8_BOM_BYTES[1]
&& bytes[2] == UTF8_BOM_BYTES[2];
if (containsBOM) {
byte[] copy = new byte[bytes.length - 3];
System.arraycopy(bytes, 3, copy, 0, bytes.length - 3);
return copy;
}
return bytes;
}
}

@ -6,6 +6,7 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import okhttp3.Response;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.util.StringHelper;
@ -49,6 +50,15 @@ public class NetworkUtils {
return false;
}
}
public static String getUrl(Response response) {
okhttp3.Response networkResponse = response.networkResponse();
if (networkResponse != null) {
return networkResponse.request().url().toString();
} else {
return response.request().url().toString();
}
}
/**
* 获取绝对地址
*/

@ -1,14 +1,26 @@
package xyz.fycz.myreader.util.utils;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import org.apache.commons.lang3.StringEscapeUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import io.reactivex.Observable;
import okhttp3.*;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.entity.StrResponse;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeUrl;
import java.io.IOException;
import java.io.InputStream;
@ -61,7 +73,32 @@ public class OkHttpUtils {
}
public static String getHtml(String url, RequestBody requestBody, String encodeType, Map<String, String> headers) throws IOException {
Response response = getResponse(url, requestBody, headers);
ResponseBody body = response.body();
if (body == null) {
return "";
} else {
String bodyStr = new String(body.bytes(), encodeType);
Log.d("Http: read finish", bodyStr);
return bodyStr;
}
}
/**
* 同步获取StrResponse
*/
public static StrResponse getStrResponse(String url, String encodeType, Map<String, String> headers) throws IOException {
return getStrResponse(url, null, encodeType, headers);
}
public static StrResponse getStrResponse(String url, RequestBody requestBody, String encodeType, Map<String, String> headers) throws IOException {
StrResponse strResponse = new StrResponse();
strResponse.setEncodeType(encodeType);
strResponse.setResponse(getResponse(url, requestBody, headers));
return strResponse;
}
public static Response getResponse(String url, RequestBody requestBody, Map<String, String> headers) throws IOException {
Request.Builder builder = new Request.Builder()
.addHeader("Accept", "*/*")
.addHeader("Connection", "Keep-Alive")
@ -82,17 +119,9 @@ public class OkHttpUtils {
Request request = builder
.url(url)
.build();
Response response = getOkHttpClient()
return getOkHttpClient()
.newCall(request)
.execute();
ResponseBody body = response.body();
if (body == null) {
return "";
} else {
String bodyStr = new String(body.bytes(), encodeType);
Log.d("Http: read finish", bodyStr);
return bodyStr;
}
}
public static InputStream getInputStream(String url) throws IOException {
@ -141,4 +170,106 @@ public class OkHttpUtils {
return content;
}
}
public static Observable<StrResponse> getStrResponse(AnalyzeUrl analyzeUrl) {
return Observable.create(emitter -> {
StringBuilder sb = new StringBuilder();
for (String key : analyzeUrl.getQueryMap().keySet()) {
sb.append(key).append("=").append(analyzeUrl.getQueryMap().get(key));
sb.append("&");
}
int index = sb.lastIndexOf("&");
if (index != -1) {
sb.deleteCharAt(index);
}
String body = sb.toString();
StrResponse strResponse = new StrResponse();
//strResponse.setEncodeType(analyzeUrl.getCharCode() != null ? analyzeUrl.getCharCode() : "UTF-8");
switch (analyzeUrl.getUrlMode()) {
case GET:
String url = analyzeUrl.getUrl() + "?" + body;
strResponse.setResponse(OkHttpUtils.getResponse(url, null, analyzeUrl.getHeaderMap()));
break;
case POST:
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody requestBody = RequestBody.create(mediaType, body);
strResponse.setResponse(OkHttpUtils.getResponse(analyzeUrl.getUrl(), requestBody, analyzeUrl.getHeaderMap()));
break;
default:
strResponse.setResponse(OkHttpUtils.getResponse(analyzeUrl.getUrl(), null, analyzeUrl.getHeaderMap()));
break;
}
emitter.onNext(strResponse);
});
}
@SuppressLint({"AddJavascriptInterface", "SetJavaScriptEnabled"})
public static Observable<String> getAjaxString(AnalyzeUrl analyzeUrl, String tag, String js) {
final Web web = new Web("加载超时");
if (!TextUtils.isEmpty(js)) {
web.js = js;
}
return Observable.create(e -> {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
Runnable timeoutRunnable;
WebView webView = new WebView(App.getmContext());
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setUserAgentString(analyzeUrl.getHeaderMap().get("User-Agent"));
//CookieManager cookieManager = CookieManager.getInstance();
Runnable retryRunnable = new Runnable() {
@Override
public void run() {
webView.evaluateJavascript(web.js, value -> {
if (!TextUtils.isEmpty(value)) {
web.content = StringEscapeUtils.unescapeJson(value);
e.onNext(web.content);
e.onComplete();
webView.destroy();
handler.removeCallbacks(this);
} else {
handler.postDelayed(this, 1000);
}
});
}
};
timeoutRunnable = () -> {
if (!e.isDisposed()) {
handler.removeCallbacks(retryRunnable);
e.onNext(web.content);
e.onComplete();
webView.destroy();
}
};
handler.postDelayed(timeoutRunnable, 30000);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
/*DbHelper.getDaoSession().getCookieBeanDao()
.insertOrReplace(new CookieBean(tag, cookieManager.getCookie(webView.getUrl())));*/
handler.postDelayed(retryRunnable, 1000);
}
});
switch (analyzeUrl.getUrlMode()) {
case POST:
webView.postUrl(analyzeUrl.getUrl(), analyzeUrl.getPostData());
break;
case GET:
webView.loadUrl(String.format("%s?%s", analyzeUrl.getUrl(), analyzeUrl.getQueryStr()), analyzeUrl.getHeaderMap());
break;
default:
webView.loadUrl(analyzeUrl.getUrl(), analyzeUrl.getHeaderMap());
}
});
});
}
private static class Web {
private String content;
private String js = "document.documentElement.outerHTML";
Web(String content) {
this.content = content;
}
}
}

@ -24,6 +24,7 @@ public class RxUtils {
.observeOn(AndroidSchedulers.mainThread());
}
@NotNull
public static <T> ObservableSource<T> toSimpleSingle(Observable<T> upstream){
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());

@ -1,33 +1,51 @@
package xyz.fycz.myreader.webapi;
import java.util.HashMap;
import android.text.TextUtils;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.functions.Function;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.StrResponse;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.model.third.analyzeRule.AnalyzeUrl;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.model.third.content.BookChapterList;
import xyz.fycz.myreader.model.third.content.BookContent;
import xyz.fycz.myreader.model.third.content.BookInfo;
import xyz.fycz.myreader.model.third.content.BookList;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.read.TianLaiReadCrawler;
import xyz.fycz.myreader.webapi.crawler.source.ThirdCrawler;
import static xyz.fycz.myreader.common.APPCONST.JS_PATTERN;
public class CommonApi {
public class CommonApi {
/**
* 获取章节列表
*
* @param url
* @param book
*/
public static Observable<List<Chapter>> getBookChapters(String url, final ReadCrawler rc) {
public static Observable<List<Chapter>> getBookChapters(Book book, final ReadCrawler rc) {
if (rc instanceof ThirdCrawler) {
return getBookChaptersByTC(book, (ThirdCrawler) rc);
}
String url = book.getChapterUrl();
String charset = rc.getCharset();
url = NetworkUtils.getAbsoluteURL(rc.getNameSpace(), url);
String finalUrl = url;
@ -37,13 +55,40 @@ public class CommonApi {
});
}
private static Observable<List<Chapter>> getBookChaptersByTC(Book book, ThirdCrawler rc) {
BookSource source = rc.getSource();
BookChapterList bookChapterList = new BookChapterList(source.getSourceUrl(), source, true);
/*if (!TextUtils.isEmpty(bookShelfBean.getBookInfoBean().getChapterListHtml())) {
return bookChapterList.analyzeChapterList(bookShelfBean.getBookInfoBean().getChapterListHtml(), bookShelfBean, headerMap);
}*/
try {
AnalyzeUrl analyzeUrl = new AnalyzeUrl(book.getChapterUrl(), null, book.getInfoUrl());
return OkHttpUtils.getStrResponse(analyzeUrl)
//.flatMap(response -> setCookie(response, tag))
.flatMap(response -> bookChapterList.analyzeChapterList(response.body(), book, null))
.flatMap(chapters -> Observable.create(emitter -> {
for (int i = 0; i < chapters.size(); i++) {
Chapter chapter = chapters.get(i);
chapter.setNumber(i);
}
emitter.onNext(chapters);
emitter.onComplete();
}));
} catch (Exception e) {
return Observable.error(new Throwable(String.format("url错误:%s", book.getChapterUrl())));
}
}
/**
* 获取章节正文
*
* @param url
*/
public static Observable<String> getChapterContent(String url, final ReadCrawler rc) {
public static Observable<String> getChapterContent(Chapter chapter, Book book, final ReadCrawler rc) {
if (rc instanceof ThirdCrawler) {
return getChapterContentByTC(chapter, book, (ThirdCrawler) rc);
}
String url = chapter.getUrl();
String charset = rc.getCharset();
url = NetworkUtils.getAbsoluteURL(rc.getNameSpace(), url);
String finalUrl = url;
@ -53,14 +98,51 @@ public class CommonApi {
});
}
private static Observable<String> getChapterContentByTC(Chapter chapter, Book book, ThirdCrawler rc) {
BookSource source = rc.getSource();
BookContent bookContent = new BookContent(source.getSourceUrl(), source);
/*if (Objects.equals(chapterBean.getDurChapterUrl(), bookShelfBean.getBookInfoBean().getChapterUrl())
&& !TextUtils.isEmpty(bookShelfBean.getBookInfoBean().getChapterListHtml())) {
return bookContent.analyzeBookContent(bookShelfBean.getBookInfoBean().getChapterListHtml(), chapterBean, nextChapterBean, bookShelfBean, headerMap);
}*/
try {
AnalyzeUrl analyzeUrl = new AnalyzeUrl(chapter.getUrl(), null, book.getChapterUrl());
String contentRule = source.getContentRule().getContent();
if (contentRule.startsWith("$") && !contentRule.startsWith("$.")) {
//动态网页第一个js放到webView里执行
contentRule = contentRule.substring(1);
String js = null;
Matcher jsMatcher = JS_PATTERN.matcher(contentRule);
if (jsMatcher.find()) {
js = jsMatcher.group();
if (js.startsWith("<js>")) {
js = js.substring(4, js.lastIndexOf("<"));
} else {
js = js.substring(4);
}
}
return OkHttpUtils.getAjaxString(analyzeUrl, source.getSourceUrl(), js)
.flatMap(response -> bookContent.analyzeBookContent(response, chapter, null, book, null));
} else {
return OkHttpUtils.getStrResponse(analyzeUrl)
//.flatMap(response -> setCookie(response, tag))
.flatMap(response -> bookContent.analyzeBookContent(response, chapter, null, book, null));
}
} catch (Exception e) {
return Observable.error(new Throwable(String.format("url错误:%s", chapter.getUrl())));
}
}
/**
* 搜索小说
*
* @param key
*/
public static Observable<ConcurrentMultiValueMap<SearchBookBean, Book>> search(String key, final ReadCrawler rc) {
if (rc instanceof ThirdCrawler) {
return searchByTC(key, (ThirdCrawler) rc);
}
String charset = "utf-8";
if (rc instanceof TianLaiReadCrawler) {
charset = "utf-8";
@ -74,13 +156,6 @@ public class CommonApi {
String url = rc.getSearchLink();
String[] urlInfo = url.split(",");
url = urlInfo[0];
/*String[] bodies = makeSearchUrl(urlInfo[1], key).split("&");
FormBody.Builder formBody = new FormBody.Builder();
for (String body : bodies) {
String[] kv = body.split("=");
formBody.add(kv[0], kv[1]);
}
RequestBody requestBody = formBody.build();*/
String body = makeSearchUrl(urlInfo[1], key);
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody requestBody = RequestBody.create(mediaType, body);
@ -100,18 +175,40 @@ public class CommonApi {
return url.replace("{key}", key);
}
/**
* 第三方书源搜索
*
* @param key
* @param rc
* @return
*/
public static Observable<ConcurrentMultiValueMap<SearchBookBean, Book>> searchByTC(String key, final ThirdCrawler rc) {
try {
BookSource source = rc.getSource();
AnalyzeUrl analyzeUrl = new AnalyzeUrl(source.getSearchRule().getSearchUrl(),
key, 1, null, source.getSourceUrl());
BookList bookList = new BookList(source.getSourceUrl(), source.getSourceName(), source, false);
return OkHttpUtils.getStrResponse(analyzeUrl).flatMap(bookList::analyzeSearchBook)
.flatMap((Function<List<Book>, ObservableSource<ConcurrentMultiValueMap<SearchBookBean, Book>>>) books -> Observable.create((ObservableOnSubscribe<ConcurrentMultiValueMap<SearchBookBean, Book>>) emitter -> {
emitter.onNext(rc.getBooks(books));
emitter.onComplete();
}));
} catch (Exception e) {
return Observable.error(e);
}
}
/**
* 获取小说详细信息
*
* @param book
*/
public static Observable<Book> getBookInfo(final Book book, final BookInfoCrawler bic) {
String url;
if (StringHelper.isEmpty(book.getInfoUrl())) {
url = book.getChapterUrl();
} else {
url = book.getInfoUrl();
if (bic instanceof ThirdCrawler){
return getBookInfoByTC(book, (ThirdCrawler) bic);
}
String url;
url = book.getInfoUrl();
url = NetworkUtils.getAbsoluteURL(bic.getNameSpace(), url);
String finalUrl = url;
return Observable.create(emitter -> {
@ -121,6 +218,22 @@ public class CommonApi {
});
}
private static Observable<Book> getBookInfoByTC(Book book, ThirdCrawler rc) {
BookSource source = rc.getSource();
BookInfo bookInfo = new BookInfo(source.getSourceUrl(), source.getSourceName(), source);
/*if (!TextUtils.isEmpty(book.getBookInfoBean().getBookInfoHtml())) {
return bookInfo.analyzeBookInfo(book.getBookInfoBean().getBookInfoHtml(), bookShelfBean);
}*/
try {
AnalyzeUrl analyzeUrl = new AnalyzeUrl(book.getInfoUrl(), null, source.getSourceUrl());
return OkHttpUtils.getStrResponse(analyzeUrl)
//.flatMap(response -> setCookie(response, tag))
.flatMap(response -> bookInfo.analyzeBookInfo(response.body(), book));
} catch (Exception e) {
return Observable.error(new Throwable(String.format("url错误:%s", book.getInfoUrl())));
}
}
/**
* 通过api获取蓝奏云可下载直链
*

@ -16,6 +16,7 @@ import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.read.FYReadCrawler;
import xyz.fycz.myreader.webapi.crawler.source.JsonPathCrawler;
import xyz.fycz.myreader.webapi.crawler.source.MatcherCrawler;
import xyz.fycz.myreader.webapi.crawler.source.ThirdCrawler;
import xyz.fycz.myreader.webapi.crawler.source.XpathCrawler;
import java.util.ArrayList;
@ -194,8 +195,7 @@ public class ReadCrawlerUtil {
crawler = new JsonPathCrawler(source);
break;
case THIRD_SOURCE:
crawler = new JsonPathCrawler(source);
break;
return new ThirdCrawler(source);
}
if (source.getSearchRule().isRelatedWithInfo() || isInfo) {
return crawler;
@ -203,7 +203,6 @@ public class ReadCrawlerUtil {
return new BaseSourceCrawlerNoInfo(crawler);
}
} else {
Class clz = Class.forName(source.getSourceUrl());
return (ReadCrawler) clz.newInstance();
}

@ -1,14 +0,0 @@
package xyz.fycz.myreader.webapi.crawler.base;
import java.util.Map;
/**
* @author fengyue
* @date 2021/5/13 22:29
*/
public abstract class BaseLocalCrawler implements ReadCrawler {
@Override
public Map<String, String> getHeaders() {
return null;
}
}

@ -0,0 +1,37 @@
package xyz.fycz.myreader.webapi.crawler.base;
import java.util.ArrayList;
import java.util.Map;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.StrResponse;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
/**
* 因新版书源使用StrResponse为了兼容旧版本书源全部继承自此类
* @author fengyue
* @date 2021/5/13 22:29
*/
public abstract class BaseReadCrawler implements ReadCrawler {
@Override
public Map<String, String> getHeaders() {
return null;
}
@Override
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
return null;
}
@Override
public ArrayList<Chapter> getChaptersFromHtml(String html) {
return null;
}
@Override
public String getContentFormHtml(String html) {
return null;
}
}

@ -31,7 +31,7 @@ import xyz.fycz.myreader.util.utils.StringUtils;
* @author fengyue
* @date 2021/2/14 18:28
*/
public abstract class BaseSourceCrawler implements ReadCrawler, BookInfoCrawler {
public abstract class BaseSourceCrawler extends BaseReadCrawler implements BookInfoCrawler {
protected final BookSource source;
protected BaseAnalyzer analyzer;

@ -13,7 +13,7 @@ import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
* @author fengyue
* @date 2021/2/14 18:28
*/
public class BaseSourceCrawlerNoInfo implements ReadCrawler {
public class BaseSourceCrawlerNoInfo extends BaseReadCrawler {
protected final BaseSourceCrawler crawler;
public BaseSourceCrawlerNoInfo(BaseSourceCrawler crawler) {

@ -1,6 +1,7 @@
package xyz.fycz.myreader.webapi.crawler.base;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.StrResponse;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;

@ -8,22 +8,17 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@Deprecated
public class Ben100ReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class Ben100ReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.100ben.net";
public static final String NOVEL_SEARCH = "https://www.100ben.net/plus/search.php?keyword={key}";
public static final String CHARSET = "UTF-8";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class BiJianReadCrawler extends BaseLocalCrawler {
public class BiJianReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "http://www.bjcan.com";
public static final String NOVEL_SEARCH = "http://www.bjcan.com/home/search/index.html?keyword={key}";
public static final String CHARSET = "UTF-8";

@ -13,14 +13,13 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class BiQuGe44ReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class BiQuGe44ReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
// public static final String NAME_SPACE = "http://www.wqge.net";
public static final String NAME_SPACE = "https://www.wqge.cc";
public static final String NOVEL_SEARCH = "https://www.wqge.cc/modules/article/search.php?searchkey={key}";

@ -12,7 +12,7 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
@ -22,7 +22,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BiQuGeReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class BiQuGeReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
private static final String NAME_SPACE = "https://www.52bqg.net";
private static final String NOVEL_SEARCH = "https://www.52bqg.net/modules/article/search.php?searchkey={key}";
private static final String CHARSET = "GBK";

@ -12,13 +12,12 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import java.util.ArrayList;
public class CansShu99ReadCrawler extends BaseLocalCrawler {
public class CansShu99ReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "http://www.99csw.com";
public static final String NOVEL_SEARCH = "http://www.99csw.com/book/search.php?type=all&keyword={key}";
public static final String CHARSET = "UTF-8";

@ -13,13 +13,12 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import java.util.ArrayList;
public class ChaoXingReadCrawler extends BaseLocalCrawler {
public class ChaoXingReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "http://yz4.chaoxing.com";
public static final String NOVEL_SEARCH = "http://yz4.chaoxing.com/circlemarket/getsearch,start=0&size=25&sw={key}&channelId=52";
public static final String CHAPTERS_URL = "https://special.zhexuezj.cn/mobile/mooc/tocourse/";

@ -15,11 +15,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
@Deprecated
public class ChuanQiReadCrawler extends BaseLocalCrawler {
public class ChuanQiReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.xs86.com";
public static final String NOVEL_SEARCH = "https://www.xs86.com/search.php?key={key}";
public static final String CHARSET = "UTF-8";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class DSTQReadCrawler extends BaseLocalCrawler {
public class DSTQReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.dstiejuan.com";
public static final String NOVEL_SEARCH = "https://www.dstiejuan.com/search.html,searchkey={key}";
public static final String CHARSET = "UTF-8";

@ -13,14 +13,13 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
/**
* 此小说源书籍详情页可添加推荐书籍暂未开始做
*/
public class Du1DuReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class Du1DuReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "http://du1du.org";
public static final String NOVEL_SEARCH = "http://du1du.org/search.htm?keyword={key}";
public static final String CHARSET = "GBK";

@ -15,12 +15,11 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
public class EWenXueReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class EWenXueReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "http://ewenxue.org";
public static final String NOVEL_SEARCH = "http://ewenxue.org/search.htm?keyword={key}";
public static final String CHARSET = "GBK";

@ -12,8 +12,7 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import java.util.ArrayList;
import java.util.regex.Matcher;
@ -23,7 +22,7 @@ import java.util.regex.Pattern;
* 风月小说网html解析工具
*/
public class FYReadCrawler extends BaseLocalCrawler {
public class FYReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://novel.fycz.xyz";
public static final String NOVEL_SEARCH = "https://novel.fycz.xyz/search.html?keyword={key}";
public static final String CHARSET = "utf-8";

@ -15,11 +15,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class HongChenReadCrawler extends BaseLocalCrawler {
public class HongChenReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.zuxs.net";
public static final String NOVEL_SEARCH = "https://www.zuxs.net/search.php?key={key}";
public static final String CHARSET = "gb2312";

@ -12,13 +12,12 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import java.util.ArrayList;
public class JiuTaoReadCrawler extends BaseLocalCrawler {
public class JiuTaoReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.9txs.org";
public static final String NOVEL_SEARCH = "https://so.9txs.org/www/,searchkey={key}";
public static final String CHARSET = "UTF-8";

@ -15,11 +15,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
@Deprecated
public class LaoYaoReadCrawler extends BaseLocalCrawler {
public class LaoYaoReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.laoyao.org";
public static final String NOVEL_SEARCH = "https://www.laoyao.org/search.php?key={key}";
public static final String CHARSET = "UTF-8";

@ -16,14 +16,13 @@ import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
/**
* @author fengyue
* @date 2020/11/27 14:09
*/
public class LiuLangCatReadCrawler extends BaseLocalCrawler {
public class LiuLangCatReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "http://m.liulangcat.com";
public static final String NOVEL_SEARCH = "http://m.liulangcat.com/get/get_search_result.php?page=0&keyword={key}";
// public static final String NOVEL_SEARCH = "http://www.liulangcat.com/search.php?k={key}&submit=搜索&wgxojg=wc0yz&uwzgzw=yi1p7&amlmvy=mcp50&rybwbm=1s0y7";

@ -15,12 +15,11 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
public class LuoQiuReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class LuoQiuReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.lqbook.com";
public static final String NOVEL_SEARCH = "https://www.lqbook.com/modules/article/search.php?searchkey={key}&submit=%CB%D1%CB%F7";
public static final String CHARSET = "GBK";

@ -12,14 +12,13 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
@Deprecated
public class MiQuReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class MiQuReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.meegoq.com";
public static final String NOVEL_SEARCH = "https://www.meegoq.com/search.htm?keyword={key}";
public static final String CHARSET = "UTF-8";

@ -8,21 +8,16 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class MiaoBiReadCrawler extends BaseLocalCrawler {
public class MiaoBiReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.imiaobige.com";
public static final String NOVEL_SEARCH = "https://www.imiaobige.com/search.html,searchkey={key}";
public static final String CHARSET = "UTF-8";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
public class PiaoTianReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class PiaoTianReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.piaotian.org";
public static final String NOVEL_SEARCH = "https://www.piaotian.org/modules/article/search.php?searchkey={key}&submit=%CB%D1%CB%F7";
public static final String CHARSET = "GBK";

@ -12,9 +12,8 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
@ -24,7 +23,7 @@ import java.util.ArrayList;
* 已失效
*/
@Deprecated
public class PinShuReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class PinShuReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.vodtw.com";
public static final String NOVEL_SEARCH = "https://www.vodtw.com/Book/Search.aspx?SearchKey={key}&SearchClass=1";
public static final String CHARSET = "gbk";

@ -12,9 +12,8 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
@ -22,7 +21,7 @@ import java.util.ArrayList;
* @author fengyue
* @date 2020/5/19 19:50
*/
public class PinShuReadCrawler2 extends BaseLocalCrawler implements BookInfoCrawler {
public class PinShuReadCrawler2 extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.vodtw.la";
public static final String NOVEL_SEARCH = "https://www.vodtw.la/search.html";
public static final String SEARCH_KEY = "q";

@ -8,22 +8,17 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
import java.util.List;
public class QB5ReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class QB5ReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
private static final String NAME_SPACE = "https://www.qb50.com";
private static final String NOVEL_SEARCH = "https://www.qb50.com/modules/article/search.php?searchkey={key}&submit=%CB%D1%CB%F7";
private static final String CHARSET = "GBK";

@ -10,8 +10,7 @@ import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import java.util.ArrayList;
@ -19,7 +18,7 @@ import java.util.ArrayList;
* 已失效
*/
@Deprecated
public class QiQiReadCrawler extends BaseLocalCrawler {
public class QiQiReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.qq717.com";
public static final String NOVEL_SEARCH = "https://www.qq717.com/search.php?keyword={key}";
public static final String CHARSET = "UTF-8";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class QianDianReadCrawler extends BaseLocalCrawler {
public class QianDianReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.qidian.com";
public static final String NOVEL_SEARCH = "https://www.qidian.com/search?kw={key}";
public static final String CHARSET = "UTF-8";

@ -11,14 +11,13 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class QuanNovelReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class QuanNovelReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://qxs.la";
public static final String NOVEL_SEARCH = "https://qxs.la/s_{key}";
public static final String CHARSET = "GBK";

@ -14,14 +14,13 @@ import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
/**
* 已失效
*/
@Deprecated
public class ReXueReadCrawler extends BaseLocalCrawler {
public class ReXueReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.rexue.org";
public static final String NOVEL_SEARCH = "https://www.rexue.org/search.php?key={key}";
public static final String CHARSET = "UTF-8";

@ -15,11 +15,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class ShiGuangReadCrawler extends BaseLocalCrawler {
public class ShiGuangReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.youxs.org";
public static final String NOVEL_SEARCH = "https://www.youxs.org/search.php?key={key}";
public static final String CHARSET = "gbk";

@ -19,12 +19,11 @@ import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
public class ShuHaiGeReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class ShuHaiGeReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.xinshuhaige.org";
public static final String NOVEL_SEARCH = "https://www.xinshuhaige.org/search.html,searchkey={key}&searchtype=all";
public static final String CHARSET = "utf-8";

@ -11,9 +11,8 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
import java.util.HashMap;
@ -21,7 +20,7 @@ import java.util.List;
import java.util.Map;
public class SoNovelReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class SoNovelReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.soxs.cc";
public static final String NOVEL_SEARCH = "https://www.soxs.cc/search.html,searchtype=all&searchkey={key}&action=search&submit= 搜 索 ";
public static final String CHARSET = "UTF-8";

@ -13,8 +13,7 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import java.util.ArrayList;
@ -22,7 +21,7 @@ import java.util.ArrayList;
* 天籁小说网html解析工具
*/
public class TianLaiReadCrawler extends BaseLocalCrawler {
public class TianLaiReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.23txt.com";
public static final String NOVEL_SEARCH = "https://www.23txt.com/search.php?q={key}";
public static final String CHARSET = "GBK";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class WoLongReadCrawler extends BaseLocalCrawler {
public class WoLongReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "http://www.paper027.com";
public static final String NOVEL_SEARCH = "http://www.paper027.com/search.html?keyword={key}";
public static final String CHARSET = "UTF-8";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class XBiQuGeReadCrawler extends BaseLocalCrawler {
public class XBiQuGeReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.xquge.com";
public static final String NOVEL_SEARCH = "https://www.xquge.com/search?keyword={key}&sign=";
public static final String CHARSET = "UTF-8";

@ -8,20 +8,16 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.List;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class XS7ReadCrawler extends BaseLocalCrawler {
public class XS7ReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.xs7.la";
public static final String NOVEL_SEARCH = "https://www.xs7.la/modules/article/search.php,searchkey={key}";
public static final String CHARSET = "GBK";

@ -15,12 +15,11 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
public class XS7ReadCrawler2 extends BaseLocalCrawler implements BookInfoCrawler {
public class XS7ReadCrawler2 extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "https://www.xs7.co";
public static final String NOVEL_SEARCH = "https://www.xs7.co/modules/article/search.php?searchkey={key}&submit=%CB%D1%CB%F7";
public static final String CHARSET = "GBK";

@ -15,11 +15,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class XiaGuReadCrawler extends BaseLocalCrawler {
public class XiaGuReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.xiagu.org";
public static final String NOVEL_SEARCH = "https://www.xiagu.org/search/?keyword={key}&t=0";
public static final String CHARSET = "UTF-8";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class XingXingReadCrawler extends BaseLocalCrawler {
public class XingXingReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.hs918.com";
public static final String NOVEL_SEARCH = "https://www.hs918.com/search.php?key={key}";
public static final String CHARSET = "UTF-8";

@ -21,11 +21,10 @@ import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
@Deprecated
public class YanQingLouReadCrawler extends BaseLocalCrawler {
public class YanQingLouReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "http://www.yanqinglou.com";
public static final String NOVEL_SEARCH = "http://www.yanqinglou.com/Home/Search,action=search&q={key}";
public static final String AJAX_CONTENT = "http://www.yanqinglou.com/home/index/ajaxchapter";

@ -11,14 +11,13 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class YunZhongReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class YunZhongReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "http://www.yunxs.com";
public static final String NOVEL_SEARCH = "http://www.yunxs.com/plus/search.php?q={key}";
public static final String CHARSET = "UTF-8";

@ -15,12 +15,11 @@ import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
public class ZW37ReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class ZW37ReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
private static final String NAME_SPACE = "https://www.37zww.net";
private static final String NOVEL_SEARCH = "https://www.37zww.net/modules/article/search.php?searchtype=articlename&searchkey={key}";
private static final String CHARSET = "GBK";

@ -14,11 +14,10 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
public class ZaiShuYuanReadCrawler extends BaseLocalCrawler{
public class ZaiShuYuanReadCrawler extends BaseReadCrawler {
public static final String NAME_SPACE = "https://www.zhaishuyuan.com";
public static final String NOVEL_SEARCH = "https://www.zhaishuyuan.com/search/,key={key}";
public static final String CHARSET = "gbk";

@ -12,14 +12,13 @@ import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseLocalCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class ZuoPinReadCrawler extends BaseLocalCrawler implements BookInfoCrawler {
public class ZuoPinReadCrawler extends BaseReadCrawler implements BookInfoCrawler {
public static final String NAME_SPACE = "http://zuopinj.com";
public static final String NOVEL_SEARCH = "http://so.zuopinj.com/search/index.php,tbname=bookname&show=title&tempid=3&keyboard={key}";
public static final String CHARSET = "UTF-8";

@ -0,0 +1,72 @@
package xyz.fycz.myreader.webapi.crawler.source;
import java.util.List;
import java.util.Map;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BaseReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
/**
* @author fengyue
* @date 2021/5/14 10:55
*/
public class ThirdCrawler extends BaseReadCrawler implements BookInfoCrawler {
private BookSource source;
public ThirdCrawler(BookSource source) {
this.source = source;
}
public BookSource getSource() {
return source;
}
@Override
public String getSearchLink() {
return source.getSearchRule().getSearchUrl();
}
@Override
public String getCharset() {
return null;
}
@Override
public Book getBookInfo(String html, Book book) {
return null;
}
@Override
public String getSearchCharset() {
return null;
}
@Override
public String getNameSpace() {
return source.getSourceUrl();
}
@Override
public Boolean isPost() {
return null;
}
@Override
public Map<String, String> getHeaders() {
return null;
}
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooks(List<Book> books) {
ConcurrentMultiValueMap<SearchBookBean, Book> newBooks = new ConcurrentMultiValueMap<>();
for (Book book : books){
book.setSource(source.getSourceUrl());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
newBooks.add(sbb, book);
}
return newBooks;
}
}

@ -226,19 +226,15 @@ public class NetPageLoader extends PageLoader {
* @param chapter
*/
public void getChapterContent(Chapter chapter) {
CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler).flatMap(s -> Observable.create(emitter -> {
CommonApi.getChapterContent(chapter, mCollBook, mReadCrawler).flatMap(s -> Observable.create(emitter -> {
loadingChapters.remove(chapter);
String content = StringHelper.isEmpty(s) ? "章节内容为空" : s;
mChapterService.saveOrUpdateChapter(chapter, content);
emitter.onNext(content);
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
public void onNext(@NotNull Object o) {
}
@Override
public void onComplete() {
if (isClose()) return;
if (getPageStatus() == PageLoader.STATUS_LOADING && mCurChapterPos == chapter.getNumber()) {
if (isPrev) {

Loading…
Cancel
Save