新增书源

pull/5/head
fengyuecanzhu 4 years ago
parent 142829313c
commit 42cc3f6d3f
  1. 6
      README.md
  2. 3
      app/src/main/assets/updatelog.fy
  3. 18
      app/src/main/java/xyz/fycz/myreader/application/SysManager.java
  4. 12
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  5. 15
      app/src/main/java/xyz/fycz/myreader/entity/Setting.java
  6. 2
      app/src/main/java/xyz/fycz/myreader/enums/BookSource.java
  7. 3
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  8. 72
      app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java
  9. 8
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseAdapter.java
  10. 14
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  11. 204
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChuanQiReadCrawler.java
  12. 202
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ReXueReadCrawler.java
  13. 10
      app/src/main/res/menu/menu_search.xml
  14. 9
      app/src/main/res/values/strings.xml
  15. 2
      app/src/main/resources/crawler.properties
  16. 4
      app/version_code.properties

@ -2,18 +2,18 @@
风月读书,一款开源、无广告的小说阅读软件。
成品下载:[https://fycz.lanzoui.com/ibgVdj42lbc](https://fycz.lanzoui.com/ibgVdj42lbc)
成品下载:[https://fycz.lanzoui.com/iwIrojatw9e](https://fycz.lanzoui.com/iwIrojatw9e)
#### 一、关于书源
* 软件内置了25个书源如下:
* 21个网络小说书源:天籁小说、笔趣阁44、品书网、笔趣阁、
* 23个网络小说书源:天籁小说、笔趣阁44、品书网、笔趣阁、
全本小说、米趣小说、九桃小说、云中书库、
搜小说网、全小说网、奇奇小说、妙笔阁、
丹书铁券、小说旗、读一读网、飘天文学、
老幺小说、星星小说、时光小说、峡谷文学、
红尘小说
红尘小说、热血小说、传奇小说
* 4个实体书书源:超星图书·实体、作品集·实体、99藏书·实体、100本·实体
* 如何自行制作并添加书源.

@ -1,6 +1,9 @@
2020.12.12
风月读书v1.20.121218
1、极大地优化本地书籍章节拆分速度
2、优化本地书籍文件编码识别
3、修复目录未加载时搜索闪退的bug
4、新增书源:热血小说、传奇小说
2020.12.06
风月读书v1.20.120612

@ -3,24 +3,13 @@ package xyz.fycz.myreader.application;
import android.util.Log;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.enums.BookcaseStyle;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.enums.Language;
import xyz.fycz.myreader.enums.ReadStyle;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.model.storage.Backup;
import xyz.fycz.myreader.util.CacheHelper;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.widget.page.PageMode;
import static xyz.fycz.myreader.application.MyApplication.getVersionCode;
import static xyz.fycz.myreader.common.APPCONST.READ_STYLE_LEATHER;
import static xyz.fycz.myreader.common.APPCONST.s;
import static xyz.fycz.myreader.widget.page.PageLoader.DEFAULT_MARGIN_WIDTH;
public class SysManager {
@ -100,7 +89,7 @@ public class SysManager {
public static void resetSetting() {
Setting setting = getSetting();
switch (setting.getSettingVersion()) {
case 10:
case 10: default:
setting.initReadStyle();
setting.setCurReadStyleIndex(1);
setting.setSharedLayout(true);
@ -117,7 +106,7 @@ public class SysManager {
public static void resetSource() {
Setting setting = getSetting();
switch (setting.getSourceVersion()) {
case 0:
case 0: default:
ReadCrawlerUtil.addReadCrawler(BookSource.miaobi, BookSource.dstq, BookSource.xs7, BookSource.du1du, BookSource.paiotian);
ReadCrawlerUtil.removeReadCrawler("cangshu99");
Log.d("SourceVersion", "" + 0);
@ -125,6 +114,7 @@ public class SysManager {
ReadCrawlerUtil.addReadCrawler(BookSource.laoyao, BookSource.xingxing, BookSource.shiguang, BookSource.xiagu, BookSource.hongchen);
Log.d("SourceVersion", "" + 1);
case 2:
ReadCrawlerUtil.addReadCrawler(BookSource.rexue, BookSource.chuanqi);
Log.d("SourceVersion", "" + 2);
}
setting.setSourceVersion(APPCONST.SOURCE_VERSION);

@ -12,11 +12,8 @@ public class APPCONST {
public static String publicKey = "";//服务端公钥
public static String privateKey;//app私钥
public final static String s = "11940364935628058505";
public static final String KEY = "";
public static final String ALARM_SCHEDULE_MSG = "alarm_schedule_msg";
public static final String FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/";
public static final String SHARE_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/share/";
public static final String LOG_DIR = Environment.getExternalStorageDirectory() + "/FYReader/log/";
@ -32,7 +29,6 @@ public class APPCONST {
+ "book_cache"+ File.separator ;
public static String HTML_CACHE_PATH = FileUtils.getCachePath() + File.separator
+ "html_cache"+ File.separator ;
public static final String LOADING_ERROR = "\t\t \t\t\t\t\n\n  章节内容转码失败!\n\n\t\t \t\t\t\n\n";
public static long exitTime;
public static final int exitConfirmTime = 2000;
@ -61,7 +57,6 @@ public class APPCONST {
public static final String FILE_NAME_SETTING = "setting";
public static final String FILE_NAME_UPDATE_INFO = "updateInfo";
public static final String FILE_NAME_SPLASH_IMAGE = "splashImage";
public static final int SELECT_TEXT_COLOR = 201;
@ -78,11 +73,6 @@ public class APPCONST {
public static final int REQUEST_READ = 1;
public static final CharSequence[] DIALOG_DOWNLOAD = {
"下载后面五十章", "下载前后五十章",
"下载后面全部章", "下载本书所有章"
};
public static final int APP_INSTALL_CODE = 10086;
public static final int SELECT_FILE_CODE = 10000;
@ -92,7 +82,7 @@ public class APPCONST {
//设置版本号
public static final int SETTING_VERSION = 11;
public static final int SOURCE_VERSION = 2;
public static final int SOURCE_VERSION = 3;
public static final String FORMAT_FILE_DATE = "yyyy-MM-dd";

@ -6,15 +6,17 @@ import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import xyz.fycz.myreader.application.MyApplication;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.enums.BookcaseStyle;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.enums.Language;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.ZipUtils;
import xyz.fycz.myreader.util.utils.BitmapUtil;
import xyz.fycz.myreader.util.utils.FileUtils;
@ -22,19 +24,12 @@ import xyz.fycz.myreader.util.utils.GsonExtensionsKt;
import xyz.fycz.myreader.util.utils.MeUtils;
import xyz.fycz.myreader.widget.page.PageMode;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static xyz.fycz.myreader.common.APPCONST.READ_STYLE_BLUE_DEEP;
import static xyz.fycz.myreader.common.APPCONST.READ_STYLE_BREEN;
import static xyz.fycz.myreader.common.APPCONST.READ_STYLE_COMMON;
import static xyz.fycz.myreader.common.APPCONST.READ_STYLE_LEATHER;
import static xyz.fycz.myreader.common.APPCONST.READ_STYLE_NIGHT;
import static xyz.fycz.myreader.common.APPCONST.READ_STYLE_PROTECTED_EYE;
import static xyz.fycz.myreader.common.APPCONST.s;
import static xyz.fycz.myreader.widget.page.PageLoader.DEFAULT_MARGIN_WIDTH;
/**

@ -30,6 +30,8 @@ public enum BookSource {
laoyao(MyApplication.getApplication().getString(R.string.read_laoyao)),
xingxing(MyApplication.getApplication().getString(R.string.read_xingxing)),
shiguang(MyApplication.getApplication().getString(R.string.read_shiguang)),
rexue(MyApplication.getApplication().getString(R.string.read_rexue)),
chuanqi(MyApplication.getApplication().getString(R.string.read_chuanqi)),
xiagu(MyApplication.getApplication().getString(R.string.read_xiagu)),
hongchen(MyApplication.getApplication().getString(R.string.read_hongchen)),
chaoxing(MyApplication.getApplication().getString(R.string.read_chaoxing)),

@ -1595,7 +1595,8 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
MyApplication.runOnUiThread(() -> {
MyAlertDialog.build(this)
.setTitle("缓存书籍")
.setSingleChoiceItems(APPCONST.DIALOG_DOWNLOAD, selectedIndex, (dialog, which) -> selectedIndex = which).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定",
.setSingleChoiceItems(getResources().getStringArray(R.array.download), selectedIndex,
(dialog, which) -> selectedIndex = which).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定",
(dialog, which) -> {
switch (selectedIndex) {
case 0:

@ -2,6 +2,7 @@ package xyz.fycz.myreader.ui.activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@ -9,11 +10,14 @@ import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatRadioButton;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
@ -30,6 +34,10 @@ import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
@ -53,6 +61,7 @@ import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
/**
@ -122,6 +131,9 @@ public class SearchBookActivity extends BaseActivity {
private Setting mSetting;
//选择禁用更新书源对话框
private AlertDialog mDisableSourceDia;
private static String[] suggestion = {"第一序列", "大道朝天", "伏天氏", "终极斗罗", "我师兄实在太稳健了", "烂柯棋缘", "诡秘之主"};
private static String[] suggestion2 = {"不朽凡人", "圣墟", "我是至尊", "龙王传说", "太古神王", "一念永恒", "雪鹰领主", "大主宰"};
@ -321,6 +333,66 @@ public class SearchBookActivity extends BaseActivity {
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_search, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_disable_source){
showDisableSourceDia();
}
return super.onOptionsItemSelected(item);
}
private void showDisableSourceDia(){
if (mDisableSourceDia != null) {
mDisableSourceDia.show();
return;
}
HashMap<CharSequence, Boolean> mSources = ReadCrawlerUtil.getDisableSources();
CharSequence[] mSourcesName = new CharSequence[mSources.keySet().size()];
boolean[] isDisables = new boolean[mSources.keySet().size()];
int dSourceCount = 0;
int i = 0;
for (CharSequence sourceName : mSources.keySet()) {
mSourcesName[i] = sourceName;
Boolean isDisable = mSources.get(sourceName);
if (isDisable == null) isDisable = false;
if (isDisable) dSourceCount++;
isDisables[i++] = isDisable;
}
mDisableSourceDia = new MultiChoiceDialog().create(this, "选择禁用的书源",
mSourcesName, isDisables, dSourceCount, (dialog, which) -> {
SharedPreUtils spu = SharedPreUtils.getInstance();
StringBuilder sb = new StringBuilder();
for (CharSequence sourceName : mSources.keySet()) {
if (!mSources.get(sourceName)) {
sb.append(BookSource.getFromName(String.valueOf(sourceName)));
sb.append(",");
}
}
if (sb.lastIndexOf(",") >= 0) sb.deleteCharAt(sb.lastIndexOf(","));
spu.putString(getString(R.string.searchSource), sb.toString());
}, null, new DialogCreator.OnMultiDialogListener() {
@Override
public void onItemClick(DialogInterface dialog, int which, boolean isChecked) {
mSources.put(mSourcesName[which], isChecked);
}
@Override
public void onSelectAll(boolean isSelectAll) {
for (CharSequence sourceName : mSources.keySet()) {
mSources.put(sourceName, isSelectAll);
}
}
});
}
/**
* 初始化建议书目
*/

@ -277,12 +277,8 @@ public abstract class BookcaseAdapter extends DragAdapter {
final int[] end = new int[1];
MyAlertDialog.build(mContext)
.setTitle("缓存书籍")
.setSingleChoiceItems(APPCONST.DIALOG_DOWNLOAD, selectedIndex, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
selectedIndex = which;
}
}).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定",
.setSingleChoiceItems(mContext.getResources().getStringArray(R.array.download), selectedIndex,
(dialog, which) -> selectedIndex = which).setNegativeButton("取消", ((dialog, which) -> dialog.dismiss())).setPositiveButton("确定",
(dialog, which) -> {
switch (selectedIndex) {
case 0:

@ -141,7 +141,7 @@ public class BookcasePresenter implements BasePresenter {
initBook();
isFirstRefresh = false;
}
downloadAll(false);
downloadAll(false, false);
}
break;
case 5:
@ -501,11 +501,11 @@ public class BookcasePresenter implements BasePresenter {
DialogCreator.createCommonDialog(mMainActivity, "一键缓存",
mMainActivity.getString(R.string.all_cathe_tip), true,
(dialog, which) -> {
downloadAll(true);
downloadAll(true, true);
SharedPreUtils.getInstance().putBoolean(mMainActivity.getString(R.string.isReadDownloadAllTip), true);
}, null);
} else {
downloadAll(true);
downloadAll(true, true);
}
return true;
@ -729,13 +729,14 @@ public class BookcasePresenter implements BasePresenter {
/**
* 缓存所有书籍
*/
private void downloadAll(boolean isDownloadAllChapters) {
private void downloadAll(boolean isDownloadAllChapters, boolean isFromUser) {
if (!NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showWarring("无网络连接!");
return;
}
if (mBooks.size() == 0) {
ToastUtils.showWarring("当前书架没有任何书籍,无法一键缓存!");
if (isFromUser)
ToastUtils.showWarring("当前书架没有任何书籍,无法一键缓存!");
return;
}
MyApplication.getApplication().newThread(() -> {
@ -747,7 +748,8 @@ public class BookcasePresenter implements BasePresenter {
}
}
if (needDownloadBooks.size() == 0) {
ToastUtils.showWarring("当前书架书籍不支持/已关闭(可在设置开启)一键缓存!");
if (isFromUser)
ToastUtils.showWarring("当前书架书籍不支持/已关闭(可在设置开启)一键缓存!");
return;
}
if (isDownloadAllChapters) {

@ -0,0 +1,204 @@
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Collections;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
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.ReadCrawler;
public class ChuanQiReadCrawler implements ReadCrawler {
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";
public static final String SEARCH_CHARSET = "UTF-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("txt");
if (divContent != null) {
Elements aDiv = divContent.getElementsByTag("dd");
StringBuilder sb = new StringBuilder();
Collections.sort(aDiv, (o1, o2) -> Integer.parseInt(o1.attr("data-id")) -
Integer.parseInt(o2.attr("data-id")));
for (int i = 0; i < aDiv.size(); i++) {
Element dd = aDiv.get(i);
if (i == aDiv.size() - 1) break;
sb.append(Html.fromHtml(dd.html()).toString());
sb.append("\n");
}
String content = sb.toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
Element divList = doc.getElementById("listsss");
Elements elementsByTag = divList.getElementsByTag("a");
int i = 0;
for (int j = 0; j < elementsByTag.size(); j++) {
Element a = elementsByTag.get(j);
String title = a.text();
String url = a.attr("href");
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
chapter.setUrl(NAME_SPACE + url);
chapters.add(chapter);
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
/*
<li><a href="/wuxian/4/4473/" target="_blank" class="book_cov" title="大主宰"><img src="/public/images/default.jpg"
data-original="https://www.youxs.org/files/article/image/0/29/29s.jpg" class="lazyload_book_cover" alt="大主宰" /></a>
<div class="book_inf">
<h3><a href="/wuxian/4/4473/" title="大主宰" target="_blank" mod="data_book_name">
<font style="font-weight:bold;color:#f00">大主宰</font>
</a></h3>
<p class="tags"><span>作者<a title="天蚕土豆">天蚕土豆</a></span><span>分类<a href="/liebiao/8_0_0_0_0_1.html" target="_blank">网络文学</a></span><span>状态连载中</span><span>总字数2497万字+</span></p>
<p><b>最近更新</b><a href="/wuxian/4/4473/15283.html" title="第1598章 邪神陨落(大结局)" target="_blank">第1598章 邪神陨落大结局</a></p>
<p class="int"> 大千世界位面交汇万族林立群雄荟萃一位位来自下位面的天之至尊在这无尽世界演绎着令人向往的传奇追求着那主宰之路
无尽火域炎帝执掌万火焚苍穹
武境之内武祖之威</p>
</div>
<div class="right">
<span>更新时间09-06 05:25</span>
<a href="/wuxian/4/4473/" target="_blank" class="read_btn btn">立即阅读</a>
<a href="javascript:BookCaseAdd('4473');" class="store_btn btn" btn="book_fav">加入书架</a>
</div>
</li>
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
// try {
String urlType = doc.select("meta[property=og:type]").attr("content");
if ("novel".equals(urlType)) {
String readUrl = doc.select("meta[property=og:novel:read_url]").attr("content");
Book book = new Book();
book.setChapterUrl(readUrl);
getBookInfo(doc, book);
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
} else {
Element div = doc.getElementsByClass("result").first();
Elements lis = div.getElementsByTag("li");
for (Element li : lis) {
Elements as = li.getElementsByTag("a");
Book book = new Book();
book.setName(as.get(1).text());
book.setAuthor(as.get(2).text());
book.setType(as.get(3).text());
book.setNewestChapterTitle(as.get(4).text().replace("最新章节:", ""));
book.setDesc(li.getElementsByClass("int").first().text());
book.setUpdateDate(li.getElementsByClass("right").first().getElementsByTag("span").text());
String imgUrl = li.getElementsByTag("img").attr("data-original");
book.setImgUrl(imgUrl);
book.setChapterUrl(NAME_SPACE + as.get(1).attr("href"));
book.setSource(BookSource.chuanqi.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
}
// } catch (Exception e) {
// e.printStackTrace();
// }
return books;
}
public Book getBookInfo(Document doc, Book book) {
//小说源
book.setSource(BookSource.chuanqi.toString());
//图片url
String imgUrl = doc.select("meta[property=og:image]").attr("content");
book.setImgUrl(imgUrl);
//书名
String title = doc.select("meta[property=og:novel:book_name]").attr("content");
book.setName(title);
//作者
String author = doc.select("meta[property=og:novel:author]").attr("content");
book.setAuthor(author);
//更新时间
String updateDate = doc.select("meta[property=og:novel:update_time]").attr("content");
book.setUpdateDate(updateDate);
//最新章节
String newestChapterTitle = doc.select("meta[property=og:novel:latest_chapter_name]").attr("content");
book.setNewestChapterTitle(newestChapterTitle);
//类型
String type = doc.select("meta[property=og:novel:category]").attr("content");
book.setType(type);
//简介
String desc = doc.select("meta[property=og:description]").attr("content");
book.setDesc(desc);
return book;
}
}

@ -0,0 +1,202 @@
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Collections;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
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.ReadCrawler;
public class ReXueReadCrawler implements ReadCrawler {
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";
public static final String SEARCH_CHARSET = "UTF-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("txt");
if (divContent != null) {
Elements aDiv = divContent.getElementsByTag("dd");
StringBuilder sb = new StringBuilder();
Collections.sort(aDiv, (o1, o2) -> Integer.parseInt(o1.attr("data-id")) -
Integer.parseInt(o2.attr("data-id")));
for (int i = 0; i < aDiv.size(); i++) {
Element dd = aDiv.get(i);
if (i == aDiv.size() - 1) break;
sb.append(Html.fromHtml(dd.html()).toString());
sb.append("\n");
}
String content = sb.toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
Element divList = doc.getElementById("listsss");
Elements elementsByTag = divList.getElementsByTag("a");
int i = 0;
for (int j = 0; j < elementsByTag.size(); j++) {
Element a = elementsByTag.get(j);
String title = a.text();
String url = a.attr("href");
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
chapter.setUrl(NAME_SPACE + url);
chapters.add(chapter);
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
/*
<li><a href="/content/3357/" target="_blank" class="book_cov" title="大主宰"><img src="/public/images/default.jpg" data-original="//www.rexue.org/files/article/image/0/24/24s.jpg" class="lazyload_book_cover" alt="大主宰"/></a>
<div class="book_inf">
<h3><a href="/content/3357/" title="大主宰" target="_blank" mod="data_book_name"><font style="font-weight:bold;color:#f00">大主宰</font></a></h3>
<p class="tags"><span>作者<a title="天蚕土豆">天蚕土豆</a></span><span>分类<a href="/sort/8_0_0_0_0_1.html" target="_blank">短篇文学</a></span><span>状态连载中</span><span>总字数2497万字+</span></p>
<p><b>最近更新</b><a href="/content/3357/11940.html" title="第1598章 邪神陨落(大结局)" target="_blank">第1598章 邪神陨落大结局</a></p>
<p class="int"> 大千世界位面交汇万族林立群雄荟萃一位位来自下位面的天之至尊在这无尽世界演绎着令人向往的传奇追求着那主宰之路
无尽火域炎帝执掌万火焚苍穹
武境之内武祖之威</p>
</div>
<div class="right">
<span>更新时间09-06 06:47</span>
<a href="/content/3357/" target="_blank" class="read_btn btn">立即阅读</a>
<a href="javascript:BookCaseAdd('3357');" class="store_btn btn" btn="book_fav">加入书架</a>
</div>
</li>
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
// try {
String urlType = doc.select("meta[property=og:type]").attr("content");
if ("novel".equals(urlType)) {
String readUrl = doc.select("meta[property=og:novel:read_url]").attr("content");
Book book = new Book();
book.setChapterUrl(readUrl);
getBookInfo(doc, book);
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
} else {
Element div = doc.getElementsByClass("result").first();
Elements lis = div.getElementsByTag("li");
for (Element li : lis) {
Elements as = li.getElementsByTag("a");
Book book = new Book();
book.setName(as.get(1).text());
book.setAuthor(as.get(2).text());
book.setType(as.get(3).text());
book.setNewestChapterTitle(as.get(4).text().replace("最新章节:", ""));
book.setDesc(li.getElementsByClass("int").first().text());
book.setUpdateDate(li.getElementsByClass("right").first().getElementsByTag("span").text());
String imgUrl = li.getElementsByTag("img").attr("data-original");
book.setImgUrl(!imgUrl.contains("http") ? "https:" + imgUrl : imgUrl);
book.setChapterUrl(NAME_SPACE + as.get(1).attr("href"));
book.setSource(BookSource.rexue.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
}
// } catch (Exception e) {
// e.printStackTrace();
// }
return books;
}
public Book getBookInfo(Document doc, Book book) {
//小说源
book.setSource(BookSource.rexue.toString());
//图片url
String imgUrl = doc.select("meta[property=og:image]").attr("content");
book.setImgUrl(imgUrl);
//书名
String title = doc.select("meta[property=og:novel:book_name]").attr("content");
book.setName(title);
//作者
String author = doc.select("meta[property=og:novel:author]").attr("content");
book.setAuthor(author);
//更新时间
String updateDate = doc.select("meta[property=og:novel:update_time]").attr("content");
book.setUpdateDate(updateDate);
//最新章节
String newestChapterTitle = doc.select("meta[property=og:novel:latest_chapter_name]").attr("content");
book.setNewestChapterTitle(newestChapterTitle);
//类型
String type = doc.select("meta[property=og:novel:category]").attr("content");
book.setType(type);
//简介
String desc = doc.select("meta[property=og:description]").attr("content");
book.setDesc(desc);
return book;
}
}

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_disable_source"
android:title="@string/disable_source"
app:showAsAction="always" />
</menu>

@ -205,6 +205,8 @@
<string name="read_laoyao">老幺小说</string>
<string name="read_xingxing">星星小说</string>
<string name="read_shiguang">时光小说</string>
<string name="read_rexue">热血小说</string>
<string name="read_chuanqi">传奇小说</string>
<string name="read_xiagu">峡谷文学</string>
<string name="read_hongchen">红尘小说</string>
<string name="read_chaoxing">超星图书·实体</string>
@ -335,4 +337,11 @@
<item>@string/time_sort</item>
<item>@string/book_name_sort</item>
</string-array>
<string-array name="download">
<item>下载后面五十章</item>
<item>下载前后五十章</item>
<item>下载后面全部章</item>
<item>下载本书所有章</item>
</string-array>
</resources>

@ -23,5 +23,7 @@ paiotian=xyz.fycz.myreader.webapi.crawler.read.PiaoTianReadCrawler
laoyao=xyz.fycz.myreader.webapi.crawler.read.LaoYaoReadCrawler
xingxing=xyz.fycz.myreader.webapi.crawler.read.XingXingReadCrawler
shiguang=xyz.fycz.myreader.webapi.crawler.read.ShiGuangReadCrawler
rexue=xyz.fycz.myreader.webapi.crawler.read.ReXueReadCrawler
chuanqi=xyz.fycz.myreader.webapi.crawler.read.ChuanQiReadCrawler
xiagu=xyz.fycz.myreader.webapi.crawler.read.XiaGuReadCrawler
hongchen=xyz.fycz.myreader.webapi.crawler.read.HongChenReadCrawler

@ -1,2 +1,2 @@
#Sun Dec 06 20:42:20 CST 2020
VERSION_CODE=176
#Sat Dec 12 18:48:03 CST 2020
VERSION_CODE=177

Loading…
Cancel
Save