parent
db31cf2500
commit
49acebd2f9
@ -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; |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
Loading…
Reference in new issue