├── .gitattributes ├── .idea ├── .gitignore ├── deployment.xml └── webServers.xml ├── main.go ├── yournovel ├── static │ ├── img │ │ ├── lcxs.jpg │ │ ├── login.png │ │ ├── bookMark.png │ │ ├── favicon.png │ │ ├── github.png │ │ ├── read_bg.png │ │ ├── lcxs_show.jpg │ │ ├── logo_home.png │ │ ├── basic_bg.0.4.png │ │ ├── left_bar_flag.png │ │ ├── logo_search.png │ │ ├── read_content.png │ │ ├── theme_1_bg.0.4.png │ │ ├── theme_2_bg.0.4.png │ │ ├── theme_3_bg.0.4.png │ │ ├── theme_5_bg.0.4.png │ │ ├── body_base_bg.0.4.png │ │ ├── body_theme1_bg.0.4.png │ │ ├── body_theme2_bg.0.4.png │ │ ├── body_theme3_bg.0.4.png │ │ └── body_theme5_bg.0.4.png │ ├── js │ │ ├── books.js │ │ ├── bookmarks.js │ │ ├── list.js │ │ ├── register.js │ │ ├── main.js │ │ ├── theme.js │ │ └── content.js │ └── css │ │ └── web │ │ ├── result.css │ │ ├── register.css │ │ ├── index.css │ │ ├── main.css │ │ ├── content.css │ │ └── chapter.css ├── service │ ├── searchengine │ │ ├── baiduengine_test.go │ │ └── baiduengine.go │ └── novel │ │ ├── searchchapter_test.go │ │ ├── searchchapter.go │ │ └── searchcontent.go ├── dao │ ├── user │ │ └── user.go │ └── novel │ │ └── novel.go ├── conf │ ├── server.go │ └── rule.go ├── http │ ├── Index.go │ ├── chapter.go │ ├── content.go │ ├── http.go │ ├── search.go │ └── content_test.go ├── fetcher │ └── fetcher.go ├── view │ └── web │ │ ├── search │ │ ├── search_bar.html │ │ └── search_index.html │ │ ├── index │ │ └── index.html │ │ ├── category │ │ └── chapter_index.html │ │ ├── content │ │ └── content_index.html │ │ └── common │ │ └── main.html ├── middleware │ └── middleware.go ├── model │ └── search.go ├── tool │ └── utils.go └── db │ └── redis │ ├── redisclient_test.go │ └── redisclient.go ├── .gitignore ├── README.md ├── go.mod └── LICENSE /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Go -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Default ignored files 3 | /workspace.xml -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "yournovel/yournovel/http" 4 | 5 | func main() { 6 | http.Init() 7 | } 8 | -------------------------------------------------------------------------------- /yournovel/static/img/lcxs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/lcxs.jpg -------------------------------------------------------------------------------- /yournovel/static/img/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/login.png -------------------------------------------------------------------------------- /yournovel/static/img/bookMark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/bookMark.png -------------------------------------------------------------------------------- /yournovel/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/favicon.png -------------------------------------------------------------------------------- /yournovel/static/img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/github.png -------------------------------------------------------------------------------- /yournovel/static/img/read_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/read_bg.png -------------------------------------------------------------------------------- /yournovel/static/img/lcxs_show.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/lcxs_show.jpg -------------------------------------------------------------------------------- /yournovel/static/img/logo_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/logo_home.png -------------------------------------------------------------------------------- /yournovel/static/img/basic_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/basic_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/left_bar_flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/left_bar_flag.png -------------------------------------------------------------------------------- /yournovel/static/img/logo_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/logo_search.png -------------------------------------------------------------------------------- /yournovel/static/img/read_content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/read_content.png -------------------------------------------------------------------------------- /yournovel/static/img/theme_1_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/theme_1_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/theme_2_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/theme_2_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/theme_3_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/theme_3_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/theme_5_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/theme_5_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/body_base_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/body_base_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/body_theme1_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/body_theme1_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/body_theme2_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/body_theme2_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/body_theme3_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/body_theme3_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/static/img/body_theme5_bg.0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonFengYuXiang/YourNovel/HEAD/yournovel/static/img/body_theme5_bg.0.4.png -------------------------------------------------------------------------------- /yournovel/service/searchengine/baiduengine_test.go: -------------------------------------------------------------------------------- 1 | package searchengine 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestEngineRune(t *testing.T) { 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /yournovel/dao/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "fmt" 5 | "yournovel/yournovel/conf" 6 | ) 7 | 8 | func TestUser() { 9 | 10 | fmt.Println(conf.Config["host"]) 11 | } 12 | -------------------------------------------------------------------------------- /yournovel/conf/server.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | const ( 4 | Host = "0.0.0.0" 5 | Port = 8079 6 | Scheme = "http://" 7 | 8 | RedisAddr = "ip:port" 9 | RedisPassword = "password" 10 | RedisDb = 0 11 | ) 12 | -------------------------------------------------------------------------------- /yournovel/dao/novel/novel.go: -------------------------------------------------------------------------------- 1 | package novel 2 | 3 | import ( 4 | "fmt" 5 | "yournovel/yournovel/conf" 6 | ) 7 | 8 | func TestNovel() { 9 | fmt.Println(conf.Config["host"]) 10 | conf.Config["host"] = "10.1.1.1" 11 | } 12 | -------------------------------------------------------------------------------- /yournovel/http/Index.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | // 首页 9 | func home(c *gin.Context) { 10 | c.HTML(http.StatusOK, "index.html", gin.H{ 11 | "head": "index_head", 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | go.sum 14 | .idea/misc.xml 15 | .idea/modules.xml 16 | .idea/vcs.xml 17 | yournovel.iml 18 | -------------------------------------------------------------------------------- /yournovel/fetcher/fetcher.go: -------------------------------------------------------------------------------- 1 | package fetcher 2 | 3 | import ( 4 | "github.com/gocolly/colly" 5 | "github.com/gocolly/colly/extensions" 6 | ) 7 | 8 | func NewNovelConnector() *colly.Collector { 9 | c := colly.NewCollector( 10 | colly.DetectCharset(), 11 | ) 12 | extensions.RandomUserAgent(c) 13 | extensions.Referer(c) 14 | 15 | return c 16 | } 17 | -------------------------------------------------------------------------------- /.idea/deployment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /yournovel/service/novel/searchchapter_test.go: -------------------------------------------------------------------------------- 1 | package novel 2 | 3 | import ( 4 | "github.com/gocolly/colly" 5 | "github.com/gocolly/colly/extensions" 6 | "testing" 7 | ) 8 | 9 | func TestSearchChapter(t *testing.T) { 10 | 11 | c := colly.NewCollector( 12 | colly.DetectCharset(), 13 | ) 14 | extensions.RandomUserAgent(c) 15 | extensions.Referer(c) 16 | 17 | c.OnHTML("#at", func(element *colly.HTMLElement) { 18 | }) 19 | 20 | c.Visit("https://www.x23us.com/html/64/64889/") 21 | } 22 | -------------------------------------------------------------------------------- /.idea/webServers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /yournovel/view/web/search/search_bar.html: -------------------------------------------------------------------------------- 1 | {{ define "search_bar" }} 2 | 16 | {{ end }} -------------------------------------------------------------------------------- /yournovel/middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "errors" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | ) 8 | 9 | type MyMiddlewareOption struct { 10 | IsAuth bool 11 | } 12 | 13 | func RequestMiddlewareWrapper(f func(ctx *gin.Context), option MyMiddlewareOption) func(ctx *gin.Context) { 14 | return func(ctx *gin.Context) { 15 | if option.IsAuth { 16 | err := auth(ctx) 17 | if err != nil { 18 | ctx.JSON(http.StatusInternalServerError, gin.H{ 19 | "code": 0, 20 | "msg": "鉴权失败", 21 | }) 22 | return 23 | } 24 | } 25 | f(ctx) 26 | } 27 | } 28 | 29 | // 鉴权 30 | func auth(ctx *gin.Context) error { 31 | return errors.New("鉴权失败") 32 | } 33 | -------------------------------------------------------------------------------- /yournovel/http/chapter.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "yournovel/yournovel/service/novel" 7 | "yournovel/yournovel/tool" 8 | ) 9 | 10 | func novelChapter(c *gin.Context) { 11 | 12 | webSiteUrl, exist := c.GetQuery("url") 13 | if !exist { 14 | tool.ErrorResponse(c, "源网址不存在", webSiteUrl) 15 | return 16 | } 17 | novelName, exist := c.GetQuery("novel_name") 18 | if !exist { 19 | c.Redirect(http.StatusMovedPermanently, webSiteUrl) 20 | return 21 | } 22 | 23 | novelChapter, err := novel.SearchChapterOfNovel(webSiteUrl, novelName) 24 | if err != nil { 25 | //c.Redirect(http.StatusMovedPermanently, webSiteUrl) 26 | return 27 | } 28 | 29 | c.HTML(http.StatusOK, "chapter_index.html", gin.H{ 30 | "chapter": novelChapter, 31 | "head": "chapter_head", 32 | } ) 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YourNovel - 开源小说搜索引擎 2 | 3 | ## 起源 4 | 5 | 项目起源于Github上另外一个类似的小说搜索引擎项目 https://github.com/howie6879/owllook, 该项目给小说爱好者带来极大的福利(无广告、清爽的界面)。但是, 该项目作者貌似马上要关闭网站了, 想想自己手上有多余的服务器并且刚学完Golang, 马上行动起来使用Golang花了几天的时间开发出了这个项目。 6 | 7 | **注意: 本项目前端代码以及服务端的一些思想借鉴了owllook项目,后期可能会根据自己喜好进行修改,因此,若你看见类似的东西请不要惊讶** 8 | 9 | ## 要求 10 | 11 | - Go 1.12+ 12 | - Go Module 开启 13 | - Redis 14 | 15 | ## 安装以及使用 16 | 17 | - git clone https://github.com/DemonFengYuXiang/YourNovel.git 18 | 19 | - `mv YourNovel yournovel` 20 | 21 | - `vim yournovel/yournovel/conf/server.go` 填写Reids地址以及密码等信息 22 | 23 | - `cd yournovel` 24 | 25 | - ` go run main.go` 26 | 27 | ## Reference 28 | 29 | YourNovel使用了以下第三方包: 30 | 31 | - gin 32 | - go-redis/redis 33 | - colly 34 | - goquery 35 | .... 36 | 37 | YourNovel借鉴项目: 38 | 39 | [owllook](https://github.com/howie6879/owllook) 40 | 41 | -------------------------------------------------------------------------------- /yournovel/model/search.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "html/template" 4 | 5 | type SearchResult struct { 6 | Href string `json:"href"` 7 | Title string `json:"title"` 8 | IsParse int `json:"is_parse"` 9 | Host string `json:"host"` 10 | } 11 | 12 | type NovelChapter struct { 13 | Name string `json:"name"` 14 | OriginUrl string `json:"origin_url"` 15 | Content template.HTML `json:"content"` 16 | LinkPrefix string `json:"link_prefix"` 17 | Domain string `json:"domain"` 18 | } 19 | 20 | type NovelContent struct { 21 | NovelName string `json:"novel_name"` 22 | Title string `json:"title"` 23 | ContentUrl string `json:"content_url"` 24 | Content template.HTML `json:"content"` 25 | PreChapter string `json:"pre_chapter"` 26 | NextChapter string `json:"next_chapter"` 27 | } 28 | -------------------------------------------------------------------------------- /yournovel/tool/utils.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "net/url" 7 | "strconv" 8 | "yournovel/yournovel/conf" 9 | ) 10 | 11 | func GetHost() string { 12 | host := conf.Host+":"+ strconv.Itoa(conf.Port) 13 | return host 14 | } 15 | 16 | func SuccessResponse(ctx *gin.Context, msg string, data interface{}) { 17 | ctx.JSON(http.StatusOK, gin.H{ 18 | "code": 1, 19 | "msg": msg, 20 | "data": data, 21 | }) 22 | } 23 | 24 | func ErrorResponse(ctx *gin.Context, msg string, data interface{}) { 25 | ctx.JSON(http.StatusOK, gin.H{ 26 | "code": 0, 27 | "msg": msg, 28 | "data": data, 29 | }) 30 | } 31 | 32 | func UrlJoin(href, base string) string { 33 | uri, err := url.Parse(href) 34 | if err != nil { 35 | return " " 36 | } 37 | baseUrl, err := url.Parse(base) 38 | if err != nil { 39 | return " " 40 | } 41 | 42 | return baseUrl.ResolveReference(uri).String() 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /yournovel/static/js/books.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by howie on 24/04/2017. 3 | */ 4 | 5 | $(document).ready(function () { 6 | $(".del-book").click(function () { 7 | var msg = "您真的确定要删除吗?请确认!"; 8 | if (confirm(msg)) { 9 | var book_url = $(this).find('a.book_url').attr("data-value"); 10 | var del_pd = {"book_url": book_url}; 11 | var del_object = $(this).parent(); 12 | $.ajax({ 13 | type: "post", 14 | contentType: "application/json", 15 | url: "/operate/delete_book", 16 | data: del_pd, 17 | dataType: 'json', 18 | success: function (data) { 19 | if (data.status == 1) { 20 | del_object.remove(); 21 | } 22 | if (data.status == -1) { 23 | alert('您还没有登录'); 24 | } 25 | } 26 | }); 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /yournovel/static/js/bookmarks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by howie on 24/04/2017. 3 | */ 4 | 5 | $(document).ready(function () { 6 | $(".del-bookmark").click(function () { 7 | var msg = "您真的确定要删除吗?请确认!"; 8 | if (confirm(msg)) { 9 | var bookmarkurl = $(this).find('a.bookmark_url').attr("data-value"); 10 | var del_bm_pd = {"bookmarkurl": bookmarkurl}; 11 | var del_bm_object = $(this).parent(); 12 | $.ajax({ 13 | type: "post", 14 | contentType: "application/json", 15 | url: "/operate/delete_bookmark", 16 | data: del_bm_pd, 17 | dataType: 'json', 18 | success: function (data) { 19 | if (data.status == 1) { 20 | del_bm_object.remove(); 21 | } 22 | if (data.status == -1) { 23 | alert('您还没有登录'); 24 | } 25 | } 26 | }); 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /yournovel/db/redis/redisclient_test.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "yournovel/yournovel/model" 7 | ) 8 | 9 | func TestRedisClinet(t *testing.T) { 10 | 11 | InitRedisClient() 12 | 13 | searchResult := &model.SearchResult{} 14 | searchResult.Title = "帝霸" 15 | searchResult.Host = "hao123.com" 16 | searchResult.IsParse = 1 17 | searchResult.Href = "http://www.baidu.com" 18 | 19 | searchResult2 := &model.SearchResult{} 20 | searchResult2.Title = "帝霸" 21 | searchResult2.Host = "hao123.com" 22 | searchResult2.IsParse = 1 23 | searchResult2.Href = "http://www.baidu.com" 24 | 25 | searchResults := []*model.SearchResult{ 26 | searchResult, 27 | searchResult2, 28 | } 29 | 30 | err := RedisConnector.SaveSearchResultByNovelName("帝霸", searchResults) 31 | if err != nil { 32 | t.Errorf("%s", err) 33 | } 34 | 35 | results, err := RedisConnector.SearchNovelByNovelName("帝霸") 36 | if err != nil { 37 | t.Errorf("%s", err) 38 | } 39 | 40 | for _,result := range results { 41 | fmt.Println(result.Href) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /yournovel/view/web/index/index.html: -------------------------------------------------------------------------------- 1 | {{ template "header" .}} 2 | 3 | {{ define "index_head" }} 4 | YourNovel-基于Go的开源简洁免费小说网站无广告无弹窗 5 | {{ end }} 6 | 7 | 8 |
9 | Fork me on GitHub 14 | 15 |
16 |
17 | 20 | 26 |
27 | 28 | {{ template "footer" .}} 29 | -------------------------------------------------------------------------------- /yournovel/static/js/list.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by howie on 21/02/2017. 3 | */ 4 | $(document).ready(function () { 5 | var link_prefix = $("#link_prefix").val(); 6 | var chapter_url = $("#url").val(); 7 | var novels_name = $("#novels_name").val(); 8 | var domain = $("#domain").val(); 9 | $(".container a").each(function () { 10 | var url = $(this).attr('href'); 11 | url = url.trimLeft("/"); 12 | if (typeof(url) != "undefined") { 13 | if (url.indexOf("yournovel") < 0) { 14 | var name = $(this).text(); 15 | var prefix = ''; 16 | if (link_prefix == '-1') { 17 | prefix = chapter_url; 18 | }else if (link_prefix == '1') { 19 | prefix = ''; 20 | }else if (link_prefix == '0') { 21 | prefix = domain; 22 | } 23 | show_url = "content?content_url=" + prefix + '/' + url + "&content_title=" + name + "&chapter_url=" + chapter_url + "&novel_name=" + novels_name; 24 | $(this).attr('href', show_url); 25 | $(this).attr('target', '_blank'); 26 | } 27 | } 28 | }); 29 | }); -------------------------------------------------------------------------------- /yournovel/http/content.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | "yournovel/yournovel/service/novel" 8 | ) 9 | 10 | func novelContent(c *gin.Context) { 11 | 12 | chapterUrl, exist := c.GetQuery("chapter_url") 13 | if !exist { 14 | c.Redirect(http.StatusMovedPermanently, "/") 15 | return 16 | } 17 | 18 | contentUrl, exist := c.GetQuery("content_url") 19 | if !exist { 20 | c.Redirect(http.StatusMovedPermanently, chapterUrl) 21 | return 22 | } 23 | novelName, exist := c.GetQuery("novel_name") 24 | if !exist { 25 | c.Redirect(http.StatusMovedPermanently, chapterUrl) 26 | return 27 | } 28 | 29 | contentTitle, exist := c.GetQuery("content_title") 30 | if !exist { 31 | c.Redirect(http.StatusMovedPermanently, chapterUrl) 32 | return 33 | } 34 | 35 | content, err := novel.SearchContentOfNovel(contentUrl) 36 | if err != nil { 37 | fmt.Println(content) 38 | //c.Redirect(http.StatusMovedPermanently, chapterUrl) 39 | return 40 | } 41 | content.NovelName = novelName 42 | content.ContentUrl = contentUrl 43 | 44 | c.HTML(http.StatusOK, "content_index.html", gin.H{ 45 | "content": content, 46 | "chapter_url": chapterUrl, 47 | "novel_name": novelName, 48 | "content_title": contentTitle, 49 | "head": "content_head", 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /yournovel/service/novel/searchchapter.go: -------------------------------------------------------------------------------- 1 | package novel 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gocolly/colly" 6 | "html/template" 7 | "net/url" 8 | "yournovel/yournovel/conf" 9 | "yournovel/yournovel/fetcher" 10 | "yournovel/yournovel/model" 11 | ) 12 | 13 | func SearchChapterOfNovel(novelUrl string, novelName string) (*model.NovelChapter, error) { 14 | c := fetcher.NewNovelConnector() 15 | 16 | var novelChapter model.NovelChapter 17 | requestURI, err := url.ParseRequestURI(novelUrl) 18 | if err != nil { 19 | return &novelChapter, err 20 | } 21 | 22 | host := requestURI.Host 23 | chapterSelector,ok := conf.RuleConfig.Rule[host]["chapter_selector"].(string) 24 | if !ok { 25 | return &novelChapter, err 26 | } 27 | 28 | c.OnHTML(chapterSelector, func(element *colly.HTMLElement) { 29 | html,_ := element.DOM.Parent().Html() 30 | if err != nil { 31 | return 32 | } 33 | 34 | novelChapter.Content = template.HTML(html) 35 | novelChapter.Name = novelName 36 | novelChapter.OriginUrl = novelUrl 37 | novelChapter.LinkPrefix = conf.RuleConfig.Rule[host]["link_prefix"].(string) 38 | novelChapter.Domain = fmt.Sprintf("%s://%s", requestURI.Scheme, requestURI.Host) 39 | }) 40 | 41 | err = c.Visit(novelUrl) 42 | if err != nil { 43 | return &novelChapter, err 44 | } 45 | 46 | return &novelChapter, nil 47 | } 48 | -------------------------------------------------------------------------------- /yournovel/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "yournovel/yournovel/conf" 6 | "yournovel/yournovel/db/redis" 7 | "yournovel/yournovel/middleware" 8 | "yournovel/yournovel/tool" 9 | ) 10 | 11 | func Init() { 12 | g := gin.Default() 13 | initRouter(g) 14 | err := g.Run(tool.GetHost()) 15 | if err != nil { 16 | panic(err) 17 | } 18 | } 19 | 20 | // 初始化路由 21 | func initRouter(engine *gin.Engine) { 22 | conf.InitConfig() 23 | redis.InitRedisClient() 24 | initConfig(engine) 25 | initGetRouter(engine) 26 | initPostRouter(engine) 27 | } 28 | 29 | func initConfig(engine *gin.Engine) { 30 | engine.LoadHTMLGlob("./yournovel/view/**/**/*") 31 | engine.Static("/assets", "./yournovel/static") 32 | } 33 | 34 | // 初始化Get路由 35 | func initGetRouter(engine *gin.Engine) { 36 | engine.GET("/", middleware.RequestMiddlewareWrapper(home, middleware.MyMiddlewareOption{ 37 | IsAuth: false, 38 | })) 39 | 40 | engine.GET("/chapter", middleware.RequestMiddlewareWrapper(novelChapter, middleware.MyMiddlewareOption{ 41 | IsAuth: false, 42 | })) 43 | 44 | engine.GET("/search", middleware.RequestMiddlewareWrapper(novelSearch, middleware.MyMiddlewareOption{ 45 | IsAuth: false, 46 | })) 47 | 48 | engine.GET("/content", middleware.RequestMiddlewareWrapper(novelContent, middleware.MyMiddlewareOption{ 49 | IsAuth: false, 50 | })) 51 | } 52 | 53 | // 初始化Post路由 54 | func initPostRouter(engine *gin.Engine) { 55 | 56 | } 57 | -------------------------------------------------------------------------------- /yournovel/view/web/category/chapter_index.html: -------------------------------------------------------------------------------- 1 | {{ template "header" .}} 2 | 3 | {{ define "chapter_head" }} 4 | {{ .chapter.Name }}-目录 5 | 6 | {{ end }} 7 | 8 |
9 |

10 | × 11 |

12 |

13 | 注意!页面内容来自{{ .chapter.OriginUrl }},本站不储存任何内容,为了更好的阅读体验进行在线解析,若有广告出现,请及时反馈。若您觉得侵犯了您的利益,请通知我们进行删除,然后访问 14 | 原网页 15 |

16 |
17 | 18 |
19 | 20 | 21 | 22 | 23 | {{ .chapter.Content }} 24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 | {{ template "footer" . }} 35 | 36 | -------------------------------------------------------------------------------- /yournovel/view/web/content/content_index.html: -------------------------------------------------------------------------------- 1 | {{ template "header" .}} 2 | 3 | {{ define "content_head" }} 4 | {{ .content.Title }}-小说内容 5 | 6 | 12 | {{ end }} 13 | 14 | 22 | 23 |
24 |
25 |

{{ .content.Title }}

26 |
27 |
28 | {{ .content.Content }} 29 |
30 |
31 | 32 |
33 | 35 | 上一章 36 | 37 | 38 | 40 | 下一章 41 | 42 |
43 | 44 | {{ template "footer" . }} 45 | 46 | 47 | -------------------------------------------------------------------------------- /yournovel/static/css/web/result.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | border-radius: 0 !important; 3 | min-height: 65px !important; 4 | } 5 | 6 | .result { 7 | margin-right: 0 !important; 8 | padding-right: 15px; 9 | padding-left: 7%; 10 | margin-bottom: 60px; 11 | } 12 | 13 | .result > p { 14 | padding-left: 4%; 15 | } 16 | 17 | .form-control { 18 | width: 90% !important; 19 | height: 35px !important; 20 | line-height: 35px !important; 21 | margin-top: 20px; 22 | border-radius: 0 !important; 23 | } 24 | 25 | div.item > li { 26 | margin-top: 15px; 27 | } 28 | 29 | div.item > li > a { 30 | color: #38556A; 31 | font-size: 16px; 32 | } 33 | 34 | .netloc { 35 | display: block; 36 | color: #006621; 37 | } 38 | 39 | .result_item { 40 | width: 35em; 41 | height: 95px; 42 | overflow: hidden; 43 | text-overflow: ellipsis; 44 | white-space: nowrap; 45 | padding-left: 5%; 46 | font-size: 15px; 47 | } 48 | 49 | .tags { 50 | position: relative; 51 | margin-top: 5px; 52 | } 53 | 54 | .parse-label { 55 | position: absolute; 56 | right: 0; 57 | } 58 | 59 | .login { 60 | min-height: 65px; 61 | text-align: center; 62 | } 63 | 64 | .login > a { 65 | margin-top: 15px; 66 | } 67 | 68 | .modal-dialog .form-control { 69 | margin-top: 0 !important; 70 | } 71 | 72 | @media (max-width: 767px) { 73 | .result { 74 | padding-right: 0 !important; 75 | } 76 | 77 | .show-result { 78 | margin-bottom: 40px; 79 | } 80 | 81 | .col-xs-12 { 82 | padding-left: 0 !important; 83 | } 84 | 85 | .move { 86 | display: none; 87 | } 88 | 89 | .show-result .tags .label { 90 | right: -10px !important; 91 | } 92 | 93 | .netloc i { 94 | font-size: 13px; 95 | } 96 | } -------------------------------------------------------------------------------- /yournovel/db/redis/redisclient.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/go-redis/redis" 7 | jsoniter "github.com/json-iterator/go" 8 | "time" 9 | "yournovel/yournovel/conf" 10 | "yournovel/yournovel/model" 11 | ) 12 | 13 | type RedisClient struct { 14 | client *redis.Client 15 | } 16 | 17 | var RedisConnector *RedisClient 18 | 19 | func InitRedisClient() { 20 | client := redis.NewClient(&redis.Options{ 21 | Addr: conf.RedisAddr, 22 | Password: conf.RedisPassword, 23 | DB: conf.RedisDb, 24 | }) 25 | 26 | RedisConnector = &RedisClient{ 27 | client: client, 28 | } 29 | 30 | pong, err := client.Ping().Result() 31 | if err != nil { 32 | fmt.Println(err) 33 | } 34 | fmt.Println(pong) 35 | return 36 | } 37 | 38 | // 根据小说名称将搜索结果保存至Redis中 39 | func (connector *RedisClient) SaveSearchResultByNovelName(novelName string, searchResult []*model.SearchResult) error { 40 | if searchResult == nil { 41 | return errors.New(fmt.Sprintf("novel:%s searchResult failure", novelName)) 42 | } 43 | 44 | searchJson, err := jsoniter.ConfigFastest.MarshalToString(searchResult) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | err = connector.client.Set(novelName, searchJson, 24 * time.Hour).Err() 50 | if err != nil { 51 | return err 52 | } 53 | 54 | return nil 55 | } 56 | 57 | // 从Redis中获取搜索小说 58 | func (connector *RedisClient) SearchNovelByNovelName(novelName string) ([]*model.SearchResult, error) { 59 | var searchResult []*model.SearchResult 60 | searchJson, err := connector.client.Get(novelName).Result() 61 | if err == redis.Nil { 62 | return searchResult, errors.New(fmt.Sprintf("Redis Error: %s not exists??", novelName)) 63 | } 64 | 65 | err = jsoniter.ConfigFastest.UnmarshalFromString(searchJson, &searchResult) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | return searchResult, nil 71 | } 72 | -------------------------------------------------------------------------------- /yournovel/static/css/web/register.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f1f1f1 !important; 3 | } 4 | 5 | .logo { 6 | margin: 5% auto; 7 | } 8 | 9 | .register { 10 | width: 40%; 11 | height: 100%; 12 | background-color: white; 13 | margin-top: 4%; 14 | padding: 2rem 4rem; 15 | box-shadow: 0 0 8px rgba(0, 0, 0, .1); 16 | } 17 | 18 | .title { 19 | text-align: center; 20 | font-family: -apple-system, SF UI Display, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif; 21 | font-weight: 700; 22 | font-size: 3rem; 23 | margin: 2rem 0; 24 | } 25 | 26 | .input-group { 27 | position: relative; 28 | width: 100%; 29 | } 30 | 31 | .sign { 32 | padding: 4px 12px 4px 25px !important; 33 | font-family: -apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif; 34 | height: 50px !important; 35 | font-size: 14px; 36 | border: 1px solid #c8c8c8; 37 | border-bottom: none; 38 | width: 100%; 39 | background-color: #f1f1f1; 40 | outline: none 41 | } 42 | 43 | .sign-last { 44 | border-bottom: 1px solid #c8c8c8; 45 | margin-bottom: 2rem; 46 | } 47 | 48 | .icon { 49 | position: absolute !important; 50 | top: 17px !important; 51 | left: 8px; 52 | color: #969696; 53 | } 54 | 55 | .submitBtn { 56 | width: 100%; 57 | margin-bottom: 3rem !important; 58 | padding: 1rem 0 !important; 59 | } 60 | 61 | .errorInfo { 62 | color: red; 63 | text-align: center; 64 | } 65 | 66 | .errorHidden0, .errorHidden1, .errorHidden2, .errorHidden3 { 67 | display: none; 68 | } 69 | 70 | @media (max-width: 960px) { 71 | .register { 72 | width: 100%; 73 | margin-top: -6%; 74 | } 75 | } 76 | 77 | @media (min-width: 1680px) { 78 | .register { 79 | margin-top: 20%; 80 | } 81 | } -------------------------------------------------------------------------------- /yournovel/http/search.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | "sync" 8 | "time" 9 | "yournovel/yournovel/conf" 10 | "yournovel/yournovel/db/redis" 11 | "yournovel/yournovel/model" 12 | "yournovel/yournovel/service/searchengine" 13 | "yournovel/yournovel/tool" 14 | ) 15 | 16 | type EngineSearch interface { 17 | EngineRun(string, *sync.WaitGroup) 18 | } 19 | 20 | // 小说搜索 21 | func novelSearch(c *gin.Context) { 22 | wd, exist := c.GetQuery("wd") 23 | if !exist || len(wd) == 0 { 24 | c.Redirect(http.StatusMovedPermanently, conf.Scheme + tool.GetHost() + "/") 25 | return 26 | } 27 | 28 | start := time.Now().UnixNano() / 1e6 29 | results := startEngine(wd) 30 | end := time.Now().UnixNano() / 1e6 31 | 32 | elapsedTime := (end - start) / 1e3 33 | searchCount := len(results) 34 | c.HTML(http.StatusOK, "search_index.html", gin.H{ 35 | "list": results, 36 | "novelName": wd, 37 | "elapsedTime": elapsedTime, 38 | "count": searchCount, 39 | "head": "search_head", 40 | }) 41 | } 42 | 43 | // 开始引擎搜索 44 | func startEngine(novelName string) []*model.SearchResult { 45 | 46 | results, err := redis.RedisConnector.SearchNovelByNovelName(novelName) 47 | if err == nil { 48 | return results 49 | } 50 | 51 | group := sync.WaitGroup{} 52 | results = make([]*model.SearchResult, 0) 53 | for _,engine := range conf.RuleConfig.Engines { 54 | var searchEngine EngineSearch 55 | group.Add(1) 56 | switch engine { 57 | case "baidu": 58 | searchEngine = searchengine.NewBaiDuSearchEngine(func(result *model.SearchResult) { 59 | results = append(results, result) 60 | }) 61 | } 62 | 63 | if searchEngine != nil { 64 | go searchEngine.EngineRun(novelName, &group) 65 | } 66 | } 67 | group.Wait() 68 | if len(results) > 0 { 69 | err = redis.RedisConnector.SaveSearchResultByNovelName(novelName, results) 70 | if err != nil { 71 | fmt.Printf("Redis Save Failure %s", err) 72 | } 73 | } 74 | 75 | return results 76 | } 77 | -------------------------------------------------------------------------------- /yournovel/view/web/search/search_index.html: -------------------------------------------------------------------------------- 1 | {{ template "header" .}} 2 | 3 | {{ define "search_head" }} 4 | {{ .novelName }}-搜索结果 5 | 6 | 13 | {{ end }} 14 | {{ $novelName := .novelName }} 15 | {{ template "search_bar" .}} 16 |
17 |
18 |
19 |

找到 {{ .count }} 条结果(用时{{ .elapsedTime }}s)

20 |
21 | {{ range .list }} 22 |
23 |
  • 24 | {{ .Title }} 25 |
    26 | {{ .Host }} 27 | 查看源网址 28 | {{ if .IsParse }} 29 | 已解析 30 | {{ else }} 31 | 未解析 32 | {{ end }} 33 |
    34 |
    35 | 36 |
    37 |
  • 38 | 39 |
    40 | {{ end }} 41 |
    42 |
    43 |
    44 | 45 |
    46 |
    47 | 48 |
    49 |
    50 |
    51 |
    52 |
    53 | {{ template "footer" .}} 54 | -------------------------------------------------------------------------------- /yournovel/static/css/web/index.css: -------------------------------------------------------------------------------- 1 | div.search { 2 | width: 100%; 3 | text-align: center; 4 | height: 30px; 5 | line-height: 30px; 6 | 7 | } 8 | 9 | div.logo { 10 | padding-top: 10%; 11 | } 12 | 13 | form.home_search { 14 | height: 120px; 15 | margin-top: 55px; 16 | } 17 | 18 | div.search > .form-control { 19 | width: 55%; 20 | height: 40px; 21 | line-height: 40px; 22 | } 23 | 24 | div.search > .btn-default { 25 | width: 134px; 26 | height: 35px; 27 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#f1f1f1)); 28 | background-image: -webkit-linear-gradient(top, #f5f5f5, #f1f1f1); 29 | -webkit-border-radius: 2px; 30 | -webkit-user-select: none; 31 | background-color: #f2f2f2; 32 | border: 1px solid #f2f2f2; 33 | border-radius: 2px; 34 | color: #757575; 35 | cursor: default; 36 | font-size: 13px; 37 | font-weight: bold; 38 | margin: 30px 4px; 39 | min-width: 54px; 40 | padding: 0 12px; 41 | text-align: center; 42 | } 43 | 44 | div.search > .btn-default:hover { 45 | border: 1px solid #c8c8c8; 46 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f3f3f3)); 47 | background-image: -webkit-linear-gradient(top, #ffffff, #f4f4f4); 48 | background-color: #ffffff; 49 | color: #474747; 50 | } 51 | 52 | .login > .pull-right { 53 | position: absolute; 54 | right: 0; 55 | top: 5px; 56 | border: 1px solid #4285f4; 57 | font-weight: bold; 58 | outline: none; 59 | background: #4285f4; 60 | background: -webkit-linear-gradient(top, #4387fd, #4683ea); 61 | } 62 | 63 | div.owl_search_ranking { 64 | margin-top: 5%; 65 | width: 55%; 66 | } 67 | 68 | div.owl_search_ranking span { 69 | display: inline-block; 70 | font-size: 12px; 71 | padding: .3em .6em .3em; 72 | margin-bottom: 2px; 73 | } 74 | 75 | @media (max-width: 767px) { 76 | .logo { 77 | padding-top: 28% !important; 78 | } 79 | 80 | .logo img { 81 | width: 100px; 82 | height: 45px; 83 | } 84 | 85 | div.owl_search_ranking { 86 | display: none; 87 | } 88 | } 89 | 90 | @media (min-width: 992px) { 91 | 92 | } 93 | 94 | @media (min-width: 1680px) { 95 | .logo { 96 | padding-top: 20% !important; 97 | } 98 | } -------------------------------------------------------------------------------- /yournovel/view/web/common/main.html: -------------------------------------------------------------------------------- 1 | {{ define "header" }} 2 | 3 | 4 | 5 | 6 | 7 | {{ if eq .head "index_head" }} 8 | {{ template "index_head" .}} 9 | {{ else if eq .head "search_head" }} 10 | {{ template "search_head" .}} 11 | {{ else if eq .head "chapter_head" }} 12 | {{ template "chapter_head" .}} 13 | {{ else if eq .head "content_head" }} 14 | {{ template "content_head" .}} 15 | {{ end }} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | {{ end }}} 38 | {{ define "footer" }} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {{ end }}} 47 | -------------------------------------------------------------------------------- /yournovel/static/js/register.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by howie on 14/03/2017. 3 | */ 4 | $(function () { 5 | $(".submitBtn").click(function () { 6 | var nickname = $("[name='nickname']").val(); 7 | var email = $("[name='email']").val(); 8 | var answer = $("[name='answer']").val(); 9 | var password = $("[name='password']").val(); 10 | var email_reg = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/; 11 | var str_email = email_reg.test(email); 12 | $("#errorInfo0").addClass("errorHidden0"); 13 | $("#errorInfo1").addClass("errorHidden1"); 14 | $("#errorInfo2").addClass("errorHidden2"); 15 | $("#errorInfo3").addClass("errorHidden3"); 16 | if (str_email) { 17 | if (password == "" || password != $("[name='password2']").val()) { 18 | $(".errorInfo").removeClass("errorHidden1"); 19 | } 20 | else if (password.length < 6 || nickname.length <= 2) { 21 | $(".errorInfo").removeClass("errorHidden0"); 22 | } 23 | else if (nickname == "" || email == "" || answer == "") { 24 | $(".errorInfo").removeClass("errorHidden2"); 25 | } 26 | else { 27 | register_pd = { 28 | 'user': nickname, 29 | 'pwd': password, 30 | 'email': email, 31 | 'answer': answer 32 | }; 33 | $.ajax({ 34 | type: "post", 35 | contentType: "application/json", 36 | url: "/operate/register", 37 | data: register_pd, 38 | dataType: 'json', 39 | success: function (data) { 40 | if (data.status == 1) { 41 | window.location.href = '/'; 42 | } 43 | if (data.status == -1) { 44 | alert('用户名已存在 换个更好的名字吧'); 45 | } 46 | if (data.status == -2) { 47 | alert('回答错了^_^'); 48 | } 49 | if (data.status == 0) { 50 | alert('注册失败'); 51 | } 52 | } 53 | }); 54 | } 55 | } else { 56 | $(".errorInfo").removeClass("errorHidden3"); 57 | } 58 | }); 59 | }); -------------------------------------------------------------------------------- /yournovel/service/searchengine/baiduengine.go: -------------------------------------------------------------------------------- 1 | package searchengine 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gocolly/colly" 6 | "github.com/gocolly/colly/extensions" 7 | "net/url" 8 | "strings" 9 | "sync" 10 | "yournovel/yournovel/conf" 11 | "yournovel/yournovel/model" 12 | ) 13 | 14 | type BaiDuSearchEngine struct { 15 | parseRule string 16 | searchRule string 17 | domain string 18 | parseResultFunc func(searchResult *model.SearchResult) 19 | } 20 | 21 | func NewBaiDuSearchEngine(parseResultFunc func(result *model.SearchResult)) *BaiDuSearchEngine { 22 | return &BaiDuSearchEngine{ 23 | parseRule: "#content_left h3.t a", 24 | searchRule: "intitle:%s 小说 阅读", 25 | domain: "http://www.baidu.com/s?wd=%s&ie=utf-8&rn=15&vf_bl=1", 26 | parseResultFunc: parseResultFunc, 27 | } 28 | } 29 | 30 | func (engine *BaiDuSearchEngine) EngineRun(novelName string, group *sync.WaitGroup) { 31 | defer group.Done() 32 | searchKey := url.QueryEscape(fmt.Sprintf(engine.searchRule, novelName)) 33 | requestUrl := fmt.Sprintf(engine.domain, searchKey) 34 | 35 | c := colly.NewCollector() 36 | extensions.RandomUserAgent(c) 37 | extensions.Referer(c) 38 | 39 | c.OnHTML(engine.parseRule, func(element *colly.HTMLElement) { 40 | group.Add(1) 41 | go engine.extractData(element, group) 42 | }) 43 | 44 | err := c.Visit(requestUrl) 45 | if err != nil { 46 | fmt.Println(err) 47 | } 48 | } 49 | 50 | func (engine *BaiDuSearchEngine) extractData(element *colly.HTMLElement, group *sync.WaitGroup) { 51 | defer group.Done() 52 | href := element.Attr("href") 53 | title := element.Text 54 | 55 | c := colly.NewCollector() 56 | extensions.RandomUserAgent(c) 57 | extensions.Referer(c) 58 | 59 | c.OnResponse(func(response *colly.Response) { 60 | realUrl := response.Request.URL.String() 61 | isContain := strings.Contains(realUrl, "baidu") 62 | if isContain { 63 | return 64 | } 65 | 66 | host := response.Request.URL.Host 67 | _,ok := conf.RuleConfig.IgnoreDomain[host] 68 | if ok { 69 | return 70 | } 71 | 72 | isParse := engine.CheckIsParse(host) 73 | result := &model.SearchResult{Href:realUrl, Title:title, IsParse:isParse, Host:host} 74 | engine.parseResultFunc(result) 75 | }) 76 | 77 | err := c.Visit(href) 78 | if err != nil { 79 | fmt.Println(err) 80 | } 81 | } 82 | 83 | func (engine *BaiDuSearchEngine) CheckIsParse(host string) int { 84 | isParse := 0 85 | for key := range conf.RuleConfig.Rule { 86 | if host == key { 87 | isParse = 1 88 | break 89 | } 90 | } 91 | return isParse 92 | } 93 | -------------------------------------------------------------------------------- /yournovel/http/content_test.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/PuerkitoBio/goquery" 7 | "github.com/gocolly/colly" 8 | "regexp" 9 | "strings" 10 | "testing" 11 | "yournovel/yournovel/tool" 12 | ) 13 | 14 | func TestNextChapter(t *testing.T) { 15 | 16 | c := colly.NewCollector( 17 | colly.DetectCharset(), 18 | ) 19 | 20 | html := "" 21 | c.OnResponse(func(response *colly.Response) { 22 | html = string(response.Body) 23 | }) 24 | 25 | c.Visit("https://www.qb5200.tw/xiaoshuo/40/40994/13951060.html") 26 | 27 | html = strings.ReplaceAll(html, "<<", "") 28 | html = strings.ReplaceAll(html, ">>", "") 29 | 30 | // 参考https://greasyfork.org/zh-CN/scripts/292-my-novel-reader 31 | next_reg := `(.*[第上前下后][一]?[0-9]{0,6}?[页张个篇章节步].*?)` 32 | judge_reg := `[第上前下后][一]?[0-9]{0,6}?[页张个篇章节步]` 33 | 34 | re := regexp.MustCompile(next_reg) 35 | allFind := re.FindAll([]byte(html), -1) 36 | allByte := bytes.Join(allFind, []byte("\n")) 37 | reader := bytes.NewReader(allByte) 38 | doc,_ := goquery.NewDocumentFromReader(reader) 39 | 40 | judgeRe := regexp.MustCompile(judge_reg) 41 | 42 | doc.Find("a").Each(func(i int, selection *goquery.Selection) { 43 | text := selection.Text() 44 | text = strings.ReplaceAll(text, " ", "") 45 | isNext := judgeRe.MatchString(text) 46 | if isNext { 47 | href,exist := selection.Attr("href") 48 | if !exist { 49 | return 50 | } 51 | 52 | url := tool.UrlJoin(href, "http://www.hao123.com") 53 | fmt.Println(url) 54 | } 55 | }) 56 | } 57 | 58 | func TestTtile(t *testing.T) { 59 | 60 | c := colly.NewCollector( 61 | colly.DetectCharset(), 62 | ) 63 | 64 | html := "" 65 | c.OnResponse(func(response *colly.Response) { 66 | html = string(response.Body) 67 | }) 68 | 69 | c.Visit("https://www.qb5200.tw/xiaoshuo/40/40994/13951060.html") 70 | 71 | titleReg := `(第?\s*[一二两三四五六七八九十○零百千万亿0-91234567890]{1,6}\s*[章回卷节折篇幕集]\s*.*?)[_,-]` 72 | re := regexp.MustCompile(titleReg) 73 | titleRes := re.FindAllString(html, -1) 74 | title := "" 75 | reader := strings.NewReader(html) 76 | doc, _ := goquery.NewDocumentFromReader(reader) 77 | if titleRes != nil { 78 | title = titleRes[1] 79 | }else { 80 | doc.Find("h1").Each(func(i int, selection *goquery.Selection) { 81 | if i == 0 { 82 | title = selection.Text() 83 | } 84 | return 85 | }) 86 | } 87 | 88 | if len(title) <= 0 { 89 | doc.Find("title").Each(func(i int, selection *goquery.Selection) { 90 | if i == 0 { 91 | title = selection.Text() 92 | } 93 | return 94 | }) 95 | } 96 | 97 | fmt.Println(title) 98 | } 99 | 100 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module yournovel 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.5.0 7 | github.com/antchfx/htmlquery v1.0.0 // indirect 8 | github.com/antchfx/xmlquery v1.0.0 // indirect 9 | github.com/antchfx/xpath v1.0.0 // indirect 10 | github.com/gin-gonic/gin v1.4.0 11 | github.com/go-redis/redis v6.15.2+incompatible 12 | github.com/gobwas/glob v0.2.3 // indirect 13 | github.com/gocolly/colly v1.2.0 14 | github.com/golang/protobuf v1.3.2 // indirect 15 | github.com/json-iterator/go v1.1.7 16 | github.com/kennygrant/sanitize v1.2.4 // indirect 17 | github.com/kr/pretty v0.1.0 // indirect 18 | github.com/onsi/ginkgo v1.10.1 // indirect 19 | github.com/onsi/gomega v1.7.0 // indirect 20 | github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect 21 | github.com/temoto/robotstxt v1.1.1 // indirect 22 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 23 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 // indirect 24 | golang.org/x/text v0.3.2 25 | google.golang.org/appengine v1.6.1 // indirect 26 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 27 | gopkg.in/olivere/elastic.v5 v5.0.82 28 | ) 29 | 30 | replace ( 31 | cloud.google.com/go => github.com/googleapis/google-cloud-go v0.44.3 32 | cloud.google.com/go/datastore => github.com/googleapis/google-cloud-go/datastore v1.0.0 33 | github.com/coreos/etcd => github.com/etcd-io/etcd v3.3.15+incompatible 34 | go.etcd.io/bbolt => github.com/etcd-io/bbolt v1.3.3 35 | golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190829043050-9756ffdc2472 36 | golang.org/x/exp => github.com/golang/exp v0.0.0-20190829153037-c13cbed26979 37 | golang.org/x/image => github.com/golang/image v0.0.0-20190829233526-b3c06291d021 38 | golang.org/x/lint => github.com/golang/lint v0.0.0-20190409202823-959b441ac422 39 | golang.org/x/mobile => github.com/golang/mobile v0.0.0-20190826170111-cafc553e1ac5 40 | 41 | golang.org/x/mod => github.com/golang/mod v0.1.0 42 | golang.org/x/net => github.com/golang/net v0.0.0-20190827160401-ba9fcec4b297 43 | golang.org/x/oauth2 => github.com/golang/oauth2 v0.0.0-20190604053449-0f29369cfe45 44 | golang.org/x/sync => github.com/golang/sync v0.0.0-20190423024810-112230192c58 45 | golang.org/x/sys => github.com/golang/sys v0.0.0-20190829204830-5fe476d8906b 46 | golang.org/x/time => github.com/golang/time v0.0.0-20190308202827-9d24e82272b4 47 | golang.org/x/tools => github.com/golang/tools v0.0.0-20190829210313-340205e581e5 48 | golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20190717185122-a985d3407aa7 49 | google.golang.org/api => github.com/googleapis/google-api-go-client v0.9.0 50 | google.golang.org/appengine => github.com/golang/appengine v1.6.2 51 | google.golang.org/genproto => github.com/googleapis/go-genproto v0.0.0-20190819201941-24fa4b261c55 52 | google.golang.org/grpc => github.com/grpc/grpc-go v1.23.0 53 | sigs.k8s.io/yaml => github.com/kubernetes-sigs/yaml v1.1.0 54 | ) 55 | -------------------------------------------------------------------------------- /yournovel/service/novel/searchcontent.go: -------------------------------------------------------------------------------- 1 | package novel 2 | 3 | import ( 4 | "bytes" 5 | "github.com/PuerkitoBio/goquery" 6 | "github.com/gocolly/colly" 7 | "html/template" 8 | "net/url" 9 | "regexp" 10 | "strings" 11 | "yournovel/yournovel/conf" 12 | "yournovel/yournovel/fetcher" 13 | "yournovel/yournovel/model" 14 | "yournovel/yournovel/tool" 15 | ) 16 | 17 | func SearchContentOfNovel(contentUrl string) (*model.NovelContent, error) { 18 | c := fetcher.NewNovelConnector() 19 | var novelContent model.NovelContent 20 | 21 | requestURI, err := url.ParseRequestURI(contentUrl) 22 | if err != nil { 23 | return &novelContent, err 24 | } 25 | 26 | host := requestURI.Host 27 | contentSelector,ok := conf.RuleConfig.Rule[host]["content_selector"].(string) 28 | if !ok { 29 | return &novelContent, err 30 | } 31 | 32 | c.OnResponse(func(response *colly.Response) { 33 | webHtml := string(response.Body) 34 | chapterUrls := getPreChapterAndNextChapterFromHtml(webHtml, contentUrl) 35 | novelContent.PreChapter = chapterUrls[0] 36 | novelContent.NextChapter = chapterUrls[1] 37 | 38 | title := getTitleFromHtml(webHtml) 39 | novelContent.Title = title 40 | }) 41 | 42 | c.OnHTML(contentSelector, func(element *colly.HTMLElement) { 43 | html,err := element.DOM.Html() 44 | if err != nil { 45 | return 46 | } 47 | novelContent.Content = template.HTML(html) 48 | }) 49 | 50 | err = c.Visit(contentUrl) 51 | if err != nil { 52 | return &novelContent, err 53 | } 54 | 55 | return &novelContent, nil 56 | } 57 | 58 | // 从HTML文件中获得上一页以及下一页 59 | func getPreChapterAndNextChapterFromHtml(html string, url string) []string { 60 | html = strings.ReplaceAll(html, "<<", "") 61 | html = strings.ReplaceAll(html, ">>", "") 62 | 63 | // 参考https://greasyfork.org/zh-CN/scripts/292-my-novel-reader 64 | next_reg := `(.*[第上前下后][一]?[0-9]{0,6}?[页张个篇章节步].*?)` 65 | judge_reg := `[第上前下后][一]?[0-9]{0,6}?[页张个篇章节步]` 66 | 67 | re := regexp.MustCompile(next_reg) 68 | allFind := re.FindAll([]byte(html), -1) 69 | allByte := bytes.Join(allFind, []byte("\n")) 70 | reader := bytes.NewReader(allByte) 71 | doc,_ := goquery.NewDocumentFromReader(reader) 72 | 73 | judgeRe := regexp.MustCompile(judge_reg) 74 | 75 | chapterUrls := make([]string, 0, 2) 76 | doc.Find("a").Each(func(i int, selection *goquery.Selection) { 77 | text := selection.Text() 78 | text = strings.ReplaceAll(text, " ", "") 79 | isNext := judgeRe.MatchString(text) 80 | if isNext { 81 | href,exist := selection.Attr("href") 82 | if !exist { 83 | return 84 | } 85 | 86 | chapterUrl := tool.UrlJoin(href, url) 87 | chapterUrls = append(chapterUrls, chapterUrl) 88 | } 89 | }) 90 | return chapterUrls 91 | } 92 | 93 | func getTitleFromHtml(html string) string { 94 | titleReg := `(第?\s*[一二两三四五六七八九十○零百千万亿0-91234567890]{1,6}\s*[章回卷节折篇幕集]\s*.*?)[_,-]` 95 | re := regexp.MustCompile(titleReg) 96 | titleRes := re.FindAllString(html, 0) 97 | title := "" 98 | reader := strings.NewReader(html) 99 | doc, _ := goquery.NewDocumentFromReader(reader) 100 | if titleRes != nil { 101 | title = titleRes[0] 102 | }else { 103 | doc.Find("h1").Each(func(i int, selection *goquery.Selection) { 104 | if i == 0 { 105 | title = selection.Text() 106 | } 107 | return 108 | }) 109 | } 110 | 111 | if len(title) <= 0 { 112 | doc.Find("title").Each(func(i int, selection *goquery.Selection) { 113 | if i == 0 { 114 | title = selection.Text() 115 | } 116 | return 117 | }) 118 | } 119 | 120 | return title 121 | } 122 | 123 | -------------------------------------------------------------------------------- /yournovel/static/css/web/main.css: -------------------------------------------------------------------------------- 1 | /*style for login*/ 2 | .header { 3 | width: 100%; 4 | height: 70px; 5 | } 6 | 7 | .header > li { 8 | list-style: none; 9 | } 10 | 11 | .header .pull-right { 12 | margin-top: 15px; 13 | margin-right: 25px; 14 | } 15 | 16 | .header .dropdown-menu > li > a { 17 | padding: 2px 10px !important; 18 | } 19 | 20 | .header .dropdown-menu .glyphicon { 21 | top: 2px !important; 22 | } 23 | 24 | .header .dropdown-menu { 25 | min-width: 120px !important; 26 | } 27 | 28 | .header span.personal-basic { 29 | margin-left: 10px; 30 | } 31 | 32 | /*style for footer*/ 33 | div.footer > .navbar { 34 | min-height: 40px !important; 35 | margin-top: 40px; 36 | margin-bottom: 0 !important; 37 | } 38 | 39 | .message > .right { 40 | float: left; 41 | position: absolute; 42 | right: 5%; 43 | } 44 | 45 | .message > .left { 46 | float: left; 47 | position: absolute; 48 | left: 2%; 49 | } 50 | 51 | .message > .right > span, .message > .left > span { 52 | text-align: center !important; 53 | line-height: 40px !important; 54 | height: 40px; 55 | } 56 | 57 | .right > span > a, .message > .left > span > a { 58 | color: #666; 59 | margin-left: 25px; 60 | } 61 | 62 | .donate { 63 | width: 60%; 64 | margin-top: 5%; 65 | margin-left: 20%; 66 | } 67 | 68 | .donate_lists { 69 | width: 60%; 70 | margin-top: 5%; 71 | margin-left: 20%; 72 | margin-bottom: 80px; 73 | } 74 | 75 | .alert { 76 | margin-bottom: 0 !important; 77 | } 78 | 79 | .move { 80 | position: fixed; 81 | bottom: 60px; 82 | right: 5px; 83 | border: 1px solid #dcdcdc; 84 | border-radius: 5px; 85 | } 86 | 87 | .move > .move_up, .move > .move_down { 88 | width: 48px; 89 | height: 48px; 90 | line-height: 40px; 91 | border: 1px solid #dcdcdc; 92 | background-color: #fff; 93 | transition: .1s ease-in; 94 | -webkit-transition: .1s ease-in; 95 | -moz-transition: .1s ease-in; 96 | -o-transition: .1s ease-in; 97 | -ms-transition: .1s ease-in; 98 | cursor: pointer; 99 | } 100 | 101 | .move_up > span, .move_down > span { 102 | width: 45px; 103 | height: 43px; 104 | line-height: 43px; 105 | text-align: center; 106 | font-size: 20px; 107 | color: rgba(72, 70, 70, 0.71); 108 | } 109 | 110 | .move > .move_up:hover, .move > .move_down:hover { 111 | background-color: #f9f9f9; 112 | } 113 | 114 | .move > .move_up > span:hover, .move > .move_down > span:hover { 115 | color: #434343; 116 | } 117 | 118 | .modal-dialog { 119 | margin-top: 15% !important; 120 | } 121 | 122 | .modal-dialog .modal-content { 123 | width: 420px; 124 | height: 250px; 125 | margin-left: auto; 126 | margin-right: auto; 127 | } 128 | 129 | @media (max-width: 1024px) { 130 | .right > span > a { 131 | margin-left: 5px !important; 132 | font-size: 12px; 133 | } 134 | 135 | .modal-dialog .modal-content { 136 | width: 80%; 137 | min-width: 265px; 138 | height: 260px; 139 | margin-left: auto; 140 | margin-right: auto; 141 | } 142 | 143 | label { 144 | padding-top: 8px !important; 145 | } 146 | 147 | .navbar-header { 148 | display: none !important; 149 | } 150 | 151 | .login > a { 152 | margin-top: 20px !important; 153 | } 154 | 155 | .header .pull-right { 156 | margin-top: 17px !important; 157 | } 158 | 159 | /*登陆后排版*/ 160 | .header li.pull-right { 161 | margin-right: 0 !important; 162 | } 163 | } -------------------------------------------------------------------------------- /yournovel/static/css/web/content.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: url(../img/read_bg.png) #ede7da !important; 3 | color: #333 !important; 4 | font-family: "Microsoft YaHei" !important; 5 | } 6 | 7 | .navbar { 8 | background: rgb(242, 238, 230) !important; 9 | } 10 | 11 | .search { 12 | width: 200px; 13 | height: 50px; 14 | } 15 | 16 | .nav-search { 17 | background: rgba(242, 238, 230, 0.58) !important; 18 | } 19 | 20 | .search input { 21 | font-family: PingFangSC-Regular, HelveticaNeue-Light, 'Helvetica Neue Light', 'Microsoft YaHei', sans-serif; 22 | font-size: 14px; 23 | position: absolute; 24 | z-index: 1; 25 | top: 20px; 26 | right: 40px; 27 | width: 175px; 28 | height: 26px; 29 | transition: border .5s; 30 | border: none; 31 | outline: none; 32 | border-bottom: 1px solid #ccc; 33 | background: 0 0; 34 | -moz-transition: border .5s; /* Firefox 4 */ 35 | -webkit-transition: border .5s; /* Safari and Chrome */ 36 | -o-transition: border .5s; 37 | 38 | } 39 | 40 | .login > a { 41 | margin-top: 8px; 42 | } 43 | 44 | .btn-login { 45 | border: 1px solid #d8d8d8 !important; 46 | color: #b3acac; 47 | background-color: #5c6165; 48 | } 49 | 50 | .btn-login:hover, .btn-login:focus { 51 | color: #fff !important; 52 | background-color: #337ab7; 53 | border-color: #2e6da4; 54 | } 55 | 56 | .search input:focus { 57 | top: 15px; 58 | border-bottom: 3px solid #E67676; 59 | } 60 | 61 | .all-content { 62 | width: 65% !important; 63 | box-shadow: 0 0 15px 0 #CCC; 64 | background: #F6F4EC; 65 | margin: 0 auto 30px; 66 | border-radius: 3px; 67 | padding-bottom: 10px; 68 | min-height: 600px; 69 | margin-bottom: 70px; 70 | border: 1px solid #d8d8d8; 71 | background: url(../img/read_content.png) !important; 72 | } 73 | 74 | .title { 75 | position: relative; 76 | } 77 | 78 | .title > a.bookMark { 79 | position: absolute; 80 | background: url(../img/bookMark.png) no-repeat; 81 | width: 20px; 82 | height: 36px; 83 | right: 30px; 84 | top: -30px; 85 | cursor: pointer; 86 | } 87 | 88 | .title > a.bookMarkAct { 89 | position: absolute; 90 | background: url(../img/bookMark.png) no-repeat -20px 0; 91 | width: 22px; 92 | height: 39px; 93 | right: 30px; 94 | top: -30px; 95 | cursor: pointer; 96 | } 97 | 98 | .title > h2 { 99 | width: 100%; 100 | height: 40px; 101 | line-height: 40px; 102 | text-align: center; 103 | margin-top: 30px; 104 | margin-bottom: 30px; 105 | } 106 | 107 | .show-content, pre { 108 | width: 90%; 109 | line-height: 1.8; 110 | font-size: 16px !important; 111 | padding-top: 16px; 112 | min-height: 360px; 113 | margin-left: 5%; 114 | word-break: break-all; 115 | font-family: "Microsoft YaHei"; 116 | text-align: left; 117 | position: relative; 118 | } 119 | 120 | pre { 121 | border: none !important; 122 | background: none !important; 123 | } 124 | 125 | .left-bar-list { 126 | width: 60px; 127 | position: fixed; 128 | left: 12%; 129 | } 130 | 131 | .left-bar-list .left-bar-flag { 132 | position: relative; 133 | z-index: 100; 134 | cursor: pointer; 135 | border: 1px solid rgba(0, 0, 0, .1); 136 | border-top: none; 137 | background: url("../img/left_bar_flag.png") 138 | } 139 | 140 | .left-bar-list .left-bar-flag .glyphicon { 141 | font-size: 18px; 142 | } 143 | 144 | .left-bar-list .left-bar-flag a { 145 | font: 12px/16px PingFangSC-Regular, '-apple-system', Simsun; 146 | display: block; 147 | width: 58px; 148 | height: 58px; 149 | transition: none; 150 | text-align: center; 151 | padding-top: 12px; 152 | color: #999; 153 | text-decoration: none; 154 | color: rgba(0, 0, 0, .4) 155 | } 156 | 157 | .left-bar-list .left-bar-flag a:hover, .add-color { 158 | color: #E67676 !important; 159 | } 160 | 161 | .show-content > p { 162 | font-size: 16px !important; 163 | } 164 | 165 | .show-content > #content { 166 | text-align: left !important; 167 | } 168 | 169 | .pre_next { 170 | width: 100%; 171 | height: 40px; 172 | text-align: center; 173 | margin-top: 20px; 174 | margin-bottom: 10px; 175 | } 176 | 177 | .pre_next > a:first-child { 178 | margin-right: 40%; 179 | } 180 | 181 | .pre_next > a { 182 | cursor: pointer; 183 | border: 1px solid rgba(90, 76, 76, 0.1); 184 | color: #555; 185 | background: rgba(0, 0, 0, .0001); 186 | } 187 | 188 | .pre_next > a:hover { 189 | border: 1px solid rgba(90, 76, 76, 0.1); 190 | color: #E67676; 191 | background: rgba(0, 0, 0, .0001); 192 | } 193 | 194 | .move { 195 | right: 5% !important; 196 | } 197 | 198 | /*原网页上一章下一章等隐藏*/ 199 | 200 | .mingzhuPage, #htmlContent img, .chapter_Turnpage, #pageselect, .biao-qian-love-bd, #toplink, #tuijian, .chapter_Turnpage a, .bottem, .chapter h1, .content > center, .content p > strong, .content p > a { 201 | display: none !important; 202 | } 203 | 204 | @media (max-width: 1024px) { 205 | .left-bar-list, #cloud-tie-wrapper { 206 | display: none !important; 207 | } 208 | 209 | .all-content { 210 | width: 96% !important; 211 | } 212 | 213 | .move { 214 | display: none; 215 | } 216 | 217 | .search input { 218 | right: 0 !important; 219 | left: 0 !important; 220 | } 221 | 222 | .login > a { 223 | margin-top: 10px !important; 224 | } 225 | 226 | .title > h2 { 227 | font-size: 20px; 228 | } 229 | } -------------------------------------------------------------------------------- /yournovel/static/js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by howie on 17/02/2017. 3 | */ 4 | 5 | $(function () { 6 | $('[data-toggle="popover"]').popover() 7 | }); 8 | 9 | 10 | $(document).ready(function () { 11 | $('.move_up').click(function () { 12 | $('html, body').animate({scrollTop: 0}, 'slow'); 13 | return false; 14 | }); 15 | $('.move_down').click(function () { 16 | $('html, body, .content').animate({scrollTop: $(document).height()}, 300); 17 | return false; 18 | }); 19 | // book 20 | $('#owllook_book').click(function () { 21 | var chapter_url = $("#chapter_url").val(); 22 | var novels_name = $("#novels_name").val(); 23 | if ($(this).hasClass('add-color')) { 24 | // delete book 25 | var del_pd = {"novels_name": novels_name, "chapter_url": chapter_url}; 26 | $.ajax({ 27 | type: "post", 28 | contentType: "application/json", 29 | url: "/operate/delete_book", 30 | data: del_pd, 31 | dataType: 'json', 32 | success: function (data) { 33 | if (data.status == 1) { 34 | $('#owllook_book').removeClass('add-color'); 35 | } 36 | if (data.status == -1) { 37 | alert('您还没有登录'); 38 | } 39 | } 40 | }); 41 | } else { 42 | // add book 43 | last_read_url = window.location.pathname + window.location.search; 44 | var add_pd = {"novels_name": novels_name, "chapter_url": chapter_url, 'last_read_url': last_read_url}; 45 | $.ajax({ 46 | type: "post", 47 | contentType: "application/json", 48 | url: "/operate/add_book", 49 | data: add_pd, 50 | dataType: 'json', 51 | success: function (data) { 52 | if (data.status == 1) { 53 | $('#owllook_book').addClass('add-color'); 54 | alert('已加入书架^_^'); 55 | window.location.reload(); 56 | 57 | } 58 | if (data.status == -1) { 59 | alert('您还没有登录'); 60 | } 61 | } 62 | }); 63 | } 64 | }); 65 | 66 | // bookmark 67 | $('#bookMark').click(function () { 68 | var chapter_url = $("#chapter_url").val(); 69 | var novels_name = $("#novels_name").val(); 70 | var url = $("#url").val(); 71 | var content_name = $("#content_name").text(); 72 | bookmarkurl = "/owllook_content?url=" + url + "&name=" + content_name + "&chapter_url=" + chapter_url + "&novels_name=" + novels_name; 73 | if ($(this).hasClass('bookMark')) { 74 | // add bookmark 75 | var add_bm_pd = {'bookmark_url': bookmarkurl}; 76 | $.ajax({ 77 | type: "post", 78 | contentType: "application/json", 79 | url: "/operate/add_bookmark", 80 | data: add_bm_pd, 81 | dataType: 'json', 82 | success: function (data) { 83 | if (data.status == 1) { 84 | $('#bookMark').removeClass('bookMark'); 85 | $('#bookMark').addClass('bookMarkAct'); 86 | } 87 | if (data.status == -1) { 88 | alert('您还没有登录'); 89 | } 90 | } 91 | }); 92 | } else { 93 | // delete bookmark 94 | var del_bm_pd = {'bookmarkurl': bookmarkurl}; 95 | $.ajax({ 96 | type: "post", 97 | contentType: "application/json", 98 | url: "/operate/delete_bookmark", 99 | data: del_bm_pd, 100 | dataType: 'json', 101 | success: function (data) { 102 | if (data.status == 1) { 103 | $('#bookMark').removeClass('bookMarkAct'); 104 | $('#bookMark').addClass('bookMark'); 105 | } 106 | if (data.status == -1) { 107 | alert('您还没有登录'); 108 | } 109 | } 110 | }); 111 | } 112 | }); 113 | // login 114 | $("#owllook_login").click(function () { 115 | var owllook_user = $("#owllook_user").val(); 116 | var owllook_pass = $("#owllook_pass").val(); 117 | if (owllook_user == "" || owllook_pass == "") { 118 | alert('不能有内容为空'); 119 | } else { 120 | var login_pd = {'user': owllook_user, 'pwd': owllook_pass}; 121 | $.ajax({ 122 | type: "post", 123 | contentType: "application/json", 124 | url: "/operate/login", 125 | data: login_pd, 126 | dataType: 'json', 127 | success: function (data) { 128 | if (data.status == 1) { 129 | location.reload(); 130 | } 131 | if (data.status == -1) { 132 | alert('用户名错误'); 133 | } 134 | if (data.status == -2) { 135 | alert('密码错误'); 136 | } 137 | } 138 | }); 139 | } 140 | }); 141 | // logout 142 | $("#logout").click(function () { 143 | $.ajax({ 144 | type: "get", 145 | contentType: "application/json", 146 | url: "/operate/logout", 147 | dataType: 'json', 148 | success: function (data) { 149 | if (data.status == 1) { 150 | location.reload(); 151 | } 152 | } 153 | }); 154 | }) 155 | }); 156 | 157 | $('.we-button').popover({ 158 | trigger: 'hover', 159 | html: true, 160 | content: "

    关注后回复进群

    " 161 | }); 162 | 163 | $('.lcxs-button').popover({ 164 | trigger: 'hover', 165 | html: true, 166 | content: "

    微信关注粮草小说

    " 167 | }); 168 | 169 | -------------------------------------------------------------------------------- /yournovel/static/css/web/chapter.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #E5E4DB !important 3 | } 4 | 5 | .all-chapter { 6 | margin-bottom: 50px; 7 | } 8 | 9 | #chapterlist .lm, .ablum_read, .TabCss .p10, .TabCss .h10, div.mb_21b, .booktext li center a, #ClassTitle, .bookurl, .booklist .vt, #bookname, #ad01, .shucansu, .samewriter, .bd > span, .bdsub > dl > dt, .all_ad, li.volume, #list > .notice, .cover, #new_chapter, .con_top, #sidebar, #fmimg, .tjlist, #listtj, #intro > p:last-child, #intro, .star_title, .starlist { 10 | display: none !important; 11 | } 12 | 13 | .mulu h1, .readout h1, #maininfo, .bdsub dd h1 { 14 | background: #F6F4EC; 15 | border-radius: 15px; 16 | margin-top: 35px; 17 | overflow: hidden; 18 | text-overflow: ellipsis; 19 | white-space: nowrap; 20 | padding-bottom: 25px; 21 | } 22 | 23 | .mulu h1, .readout h1 { 24 | text-align: center; 25 | line-height: 60px; 26 | height: 60px; 27 | } 28 | 29 | .bdsub dd h1 { 30 | text-align: center; 31 | padding-top: 20px; 32 | } 33 | 34 | #info h1 { 35 | text-align: center; 36 | padding-top: 20px; 37 | color: rgba(50, 68, 84, 0.61); 38 | } 39 | 40 | #info p { 41 | float: left; 42 | margin-top: 25px; 43 | width: 25%; 44 | margin-left: 6%; 45 | overflow: hidden; 46 | text-overflow: ellipsis; 47 | white-space: nowrap; 48 | } 49 | 50 | /* 区域1 */ 51 | .chapters, .listmain, .story_list_m62topxs, .directory_con, .book-list, #yuedu table, .leftList, table.css, .dirconone, .book_list, #readerlist, .liebiao_bottom dl, .insert_list dl, .uclist, .list_box, .chapter, .mulu, #chapterslist, .booktext, #chapterslist dl, .box_chap, #book dl, .centent, #chapterlist, .booklist, #at, table.border, .zjlist, .mulu_list, .acss, .list, #xslist ul, .dirlist, .list-chapter, .list_box ul, #defaulthtml4 table, .article-list > dl, .update, #list .box, .bookcontent > dl, .listmain > dl, .ml_main > dl, #list dl, #chapter_list, .chapterlist, .mt10, .catalog, #readerlists, .readout table { 52 | overflow: hidden; 53 | padding-bottom: 1px; 54 | margin: auto; 55 | background: #F6F4EC; 56 | margin-top: 5px; 57 | border-radius: 15px; 58 | margin-bottom: 20px; 59 | } 60 | 61 | table.border, .mulu_list, #list, .article-list { 62 | margin-top: 35px; 63 | } 64 | 65 | /* 区域2 */ 66 | .ml_list h3, .story-catalog h3.catalog-title, .listmain dl dt, .list .topfill h1, .story_list_m62topxs h2, .directory_con .tit, #yuedu table tr th, table.css td.vcss, .book_list h5, #main, .jm h3, #chapterlist .ui-title h3, #readerlist .topa, .insert_list dt, .uclist dt, .list_box td.list_line, .chapter .name, .mulu .mulu-title, #list dl dt h2, #chapterslist dt, .box_chap .t, .centent .list, .booklist .v, table.border .M, .zjlist dt, td.tcss, .acss .vcss, .list .tit, #xslist ul span, .article-list > dl > dt, .update .hd > #list .box-head, .bookcontent dt, .listmain dt, .bdsub tr th, #readerlists > ul > h3, #cl_content > dl > dt, #list dt, .panel > h2, .TabCss dt, .chapterlist > dt, .vcss, .readout td.vcss .chapter_list_chapter_title, .mt10 > .title > h2 { 67 | background: none repeat scroll 0 0 rgba(214, 212, 200, 0.89) !important; 68 | display: inline !important; 69 | float: left !important; 70 | font-size: 14px !important; 71 | line-height: 28px !important; 72 | overflow: hidden !important; 73 | text-align: center !important; 74 | vertical-align: middle !important; 75 | width: 100% !important; 76 | margin: auto auto 5px !important; 77 | padding: 5px 10px !important; 78 | } 79 | 80 | table.css { 81 | width: 100%; 82 | } 83 | 84 | /* 区域3 */ 85 | .ml_list ul li, ul.chaw_c li, .mod-article-list li, .chapter, .story-catalog .catalog-list ul li, #chapter_list .chapter_list_chapterx, .listmain dl dd, .list .block_ul li, .story_list_m62topxs li, .directory_con .con li, .book-list li, #yuedu table tr li, .leftList li, table.css td.ccss, .dirconone ul li, .boxm ul li, .zjlist4 li, ul.pox li, .dccss, #readerlist ul li, .liebiao_bottom dd, .insert_list li, .uclist dd, .list_box td.list_lineb, .chapter li, #category .content .combox h2, .mulu li, .booktext li, #chapterslist dd, .book li, .art_listmain_main span, .box_chap li, #dirsort01 li, #book dd, .centent li, #chapterlist li, .booklist span, .box .zjlist4 li, .chapterlist li, #at td, .readout td, .zjlist dd, .mulu_list > li, .acss .ccss, .list .zl, #xslist ul li, .dirlist li, .list-chapter > li, .list_box ul li, #defaulthtml4 table .dccss, #defaulthtml4 table .dccss a, .article-list > dl > dd, .update .bd, #list td.swbt, .bookcontent dd, .listmain dd, .L, .ml_main > dl > dd, #readerlists > ul > li, .catalog > ul > li, .article_texttitleb > ul > li, #cl_content > dl > dd, #list dd, .panel li, .chapterlist > dd, td.ccss, .chapter_list_chapter, .book_list > ul > li { 86 | border-bottom: 1px dashed #CCC; 87 | display: inline; 88 | float: left; 89 | height: 25px; 90 | line-height: 200%; 91 | margin-bottom: 5px; 92 | text-align: left; 93 | text-indent: 10px; 94 | vertical-align: middle; 95 | width: 26%; 96 | font-size: 15px !important; 97 | margin-left: 5% !important; 98 | overflow: hidden; 99 | text-overflow: ellipsis; 100 | white-space: nowrap; 101 | } 102 | 103 | .TabCss dd.col4 { 104 | float: right; 105 | margin-right: 4px; 106 | } 107 | 108 | .TabCss dd { 109 | width: 228px !important; 110 | height: 28px; 111 | line-height: 28px; 112 | vertical-align: middle; 113 | text-indent: 10px; 114 | font-size: 12px; 115 | background: #E7F4F4; 116 | text-align: left; 117 | margin-left: 5px; 118 | margin-bottom: 5px; 119 | float: left; 120 | display: inline; 121 | border: 1px solid #9FBFE2; 122 | overflow: hidden; 123 | } 124 | 125 | dd > a { 126 | cursor: pointer; 127 | } 128 | 129 | .panel { 130 | margin-bottom: 20px; 131 | background-color: #E5E4DB !important; 132 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); 133 | box-shadow: 0 1px 1px rgba(0, 0, 0, .05); 134 | } 135 | 136 | tr > td.ccss, .L, ul#chapterlist li, .list_box td.list_lineb { 137 | width: 19% !important; 138 | } 139 | 140 | #at td.L { 141 | width: 14% !important; 142 | } 143 | 144 | .download-area, .list .part01, .list .part02, .list .topfill .right_juan, .pinglun, #main .kl, div#comment, #dirsort01 li strong, .notice, .zuixin10, .update h2, .bdsub dd h3, .bdsub .tags, .bdsub .tips { 145 | display: none !important; 146 | } 147 | 148 | .book_list > ul > li { 149 | width: 20% !important; 150 | float: right !important; 151 | } 152 | 153 | .boxm ul li { 154 | float: left !important; 155 | width: 25% !important; 156 | } 157 | 158 | #defaulthtml4 table .dccss { 159 | width: 100% !important; 160 | } 161 | 162 | @media (max-width: 960px) { 163 | li.chapter, .list_box td.list_lineb, .chapter li, #category .content .combox h2, .mulu li, .booktext li, #chapterslist dd, .book li, .art_listmain_main span, .box_chap li, #dirsort01 li, #book dd, .centent li, #chapterlist li, .booklist span, .box .zjlist4 li, .chapterlist li, #at td, .readout td, .zjlist dd, .mulu_list > li, .acss .ccss, .list .zl, #xslist ul li, .dirlist li, .list-chapter > li, .list_box ul li, #defaulthtml4 table .dccss, .article-list > dl > dd, .update .bd, #list td.swbt, .bookcontent dd, .listmain dd, .L, .ml_main > dl > dd, #readerlists > ul > li, .catalog > ul > li, .article_texttitleb > ul > li, #cl_content > dl > dd, #list dd, .panel li, .chapterlist > dd, td.ccss, .chapter_list_chapter, .book_list > ul > li { 164 | width: 100% !important; 165 | } 166 | 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /yournovel/static/js/theme.js: -------------------------------------------------------------------------------- 1 | /* 2 | Created By MSCSTSTS 3 | 2017年10月11日 14:13:13 UTC+8 4 | */ 5 | 6 | 7 | $(document).ready(function () { 8 | function theme(bg_color, bg_img, color , img){ 9 | this.bg_color = bg_color; 10 | this.bg_img = bg_img; 11 | this.color = color; 12 | this.img = img; 13 | } 14 | 15 | 16 | /*-------------------初始化-------------------*/ 17 | var base = []; 18 | 19 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/body_base_bg.0.4.png"] = "../img/body_base_bg.0.4.png"; 20 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/basic_bg.0.4.png"] = "../img/basic_bg.0.4.png"; 21 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme1_bg.0.4.png"] = "../img/body_theme1_bg.0.4.png"; 22 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_1_bg.0.4.png"] = "../img//theme_1_bg.0.4.png"; 23 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme2_bg.0.4.png"] = "../img/body_theme2_bg.0.4.png"; 24 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_2_bg.0.4.png"] = "../img/theme_2_bg.0.4.png"; 25 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme3_bg.0.4.png"] = "../img/body_theme3_bg.0.4.png"; 26 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_3_bg.0.4.png"] = "../img/theme_3_bg.0.4.png"; 27 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme5_bg.0.4.png"] = "../img/body_theme5_bg.0.4.png"; 28 | base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_5_bg.0.4.png"] = "../img/theme_5_bg.0.4.png"; 29 | 30 | var mise = new theme("rgb(237, 231, 218)", base["http://qidian.gtimg.com/qd/images/read.qidian.com/body_base_bg.0.4.png"],"rgba(0, 0, 0, 0)" ,base["http://qidian.gtimg.com/qd/images/read.qidian.com/basic_bg.0.4.png"] ); //米色 31 | var maihuang=new theme("rgb(224, 206, 158)" ,base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme1_bg.0.4.png"] ,"rgb(243, 233, 198)" ,base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_1_bg.0.4.png"] ); //麦黄 32 | var yaqing = new theme("rgb(205, 223, 205)" , base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme2_bg.0.4.png"],"rgb(226, 238, 226)" ,base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_2_bg.0.4.png"] );//雅青 33 | var hulan = new theme("rgb(207, 221, 225)" ,base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme3_bg.0.4.png"] ,"rgb(226, 239, 243)" ,base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_3_bg.0.4.png"] );//湖蓝 34 | var danfen = new theme("rgb(235, 206, 206)","none" ,"rgb(245, 228, 228)" ,"none" );//淡粉 35 | var wuhui = new theme( "rgb(208, 208, 208)",base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/body_theme5_bg.0.4.png"] , "rgb(220, 220, 220)",base["http://qidian.gtimg.com/qd/images/read.qidian.com/theme/theme_5_bg.0.4.png"] );//雾灰 36 | 37 | 38 | var bg_target = $("body"); 39 | var target = $("body > div.container.all-content"); 40 | var setting = $("body > div.container.all-content > div.left-bar-list > div:nth-child(2)"); 41 | var content = $($(".show-content").children("*").get(0)); 42 | $("body").append('
    16px
    '); 43 | $("body").append(''); 44 | /*-------------------初始化-------------------*/ 45 | 46 | var arr = []; 47 | arr["mise"]=mise; 48 | arr["maihuang"]=maihuang; 49 | arr["yaqing"]=yaqing; 50 | arr["hulan"]=hulan; 51 | arr["danfen"]=danfen; 52 | arr["wuhui"]=wuhui; 53 | var ff = []; 54 | ff['"Microsoft YaHei"']="yahei"; 55 | ff["Microsoft YaHei"]="yahei"; 56 | ff["Simsun"]="songti"; 57 | ff['"Simsun"']="songti"; 58 | ff["Kaiti"]="kaiti"; 59 | ff['"Kaiti"']="kaiti"; 60 | ff["yahei"]="Microsoft YaHei"; 61 | ff["songti"]="Simsun"; 62 | ff["kaiti"]="Kaiti"; 63 | ff[""]="yahei"; 64 | 65 | function Forcr_css(tar,att1,data1,data2){ 66 | tar.css("cssText", att1+": url("+data2+") "+data1+"!important;"); 67 | } 68 | function set_font(num){ 69 | //console.log(num); 70 | //console.log("font-size:"+(fz-(-num))+"px !important;"); 71 | content.css("cssText", "font-size: "+num+"px !important;"); 72 | refresh_font_size(); 73 | window.localStorage["theme-font-size"]=num; 74 | } 75 | function set_font_family(ff){ 76 | content.removeClass("songti"); 77 | content.removeClass("kaiti"); 78 | content.removeClass("yahei"); 79 | content.addClass(ff); 80 | //content.css("cssText", "font-family: "+ff+" !important;"); 81 | refresh_font_family(); 82 | window.localStorage["theme-font-family"]=ff; 83 | } 84 | 85 | 86 | function refresh_font_size(){ 87 | $("#helper-font-size").text(content.css("font-size")); 88 | return parseInt(content.css("font-size")); 89 | } 90 | function refresh_font_family(){ 91 | bright_font_family(content.attr("class")); 92 | //console.log(content.css("font-family")); 93 | return ff[content.css("font-family")]; 94 | } 95 | function bright_font_family(ff){ 96 | $("#helper div.family button").removeClass("act"); 97 | $("#"+ff).addClass("act"); 98 | } 99 | function change_Theme(theme){ 100 | Forcr_css(bg_target,"background",theme.bg_color,theme.bg_img); 101 | var href =""+ window.location.href; 102 | if(href.indexOf("/chapter?")>0){ 103 | Forcr_css($("#maininfo"),"background",theme.color,theme.img); 104 | Forcr_css($("#list > dl"),"background",theme.color,theme.img); 105 | Forcr_css($("div.container.all-chapter > div.inner > dl.chapterlist"),"background",theme.color,theme.img); 106 | Forcr_css($(" #at"),"background",theme.color,theme.img); 107 | Forcr_css($("div>dl"),"background",theme.color,theme.img); 108 | Forcr_css($("table"),"background",theme.color,theme.img); 109 | Forcr_css($("dl"),"background",theme.color,theme.img); 110 | Forcr_css($("ul"),"background",theme.color,theme.img); 111 | } 112 | else{ 113 | Forcr_css(target,"background",theme.color,theme.img); 114 | } 115 | target.css("box-shadow","0 0 8px 1px rgba(100,100,100,0.4)"); 116 | } 117 | $(document).ready(function(){ 118 | if("undefined" == typeof localStorage["theme-color"]){ 119 | } 120 | else{ 121 | change_Theme(arr[localStorage["theme-color"]]); 122 | } 123 | if("undefined" == typeof localStorage["theme-font-size"]){ 124 | } 125 | else{ 126 | set_font(localStorage["theme-font-size"]); 127 | } 128 | if("undefined" == typeof localStorage["theme-font-family"]){ 129 | } 130 | else{ 131 | set_font_family(localStorage["theme-font-family"]); 132 | } 133 | 134 | 135 | 136 | setting.click(function(){ 137 | event.preventDefault(); 138 | /** Show Div**/ 139 | $("#helper").toggle(); 140 | $(".layer").toggle(); 141 | refresh_font_size(); 142 | refresh_font_family(); 143 | }); 144 | $("div.color").click(function(e){ 145 | change_Theme(arr[$(this).attr("theme-data")]); 146 | window.localStorage["theme-color"] = $(this).attr("theme-data"); 147 | }); 148 | $("#helper-font-minus").click(function(){ 149 | set_font((parseInt(refresh_font_size())-(1))); 150 | }); 151 | $("#helper-font-plus").click(function(){ 152 | set_font((parseInt(refresh_font_size())-(-1))); 153 | //console.log("+1"); 154 | }); 155 | $("#songti").click(function(){ 156 | set_font_family("songti"); 157 | }); 158 | $("#yahei").click(function(){ 159 | set_font_family("yahei"); 160 | }); 161 | $("#kaiti").click(function(){ 162 | set_font_family("kaiti"); 163 | }); 164 | $(".layer").click(function(){ 165 | $("#helper").hide(); 166 | $(".layer").hide(); 167 | }); 168 | }); 169 | }); -------------------------------------------------------------------------------- /yournovel/static/js/content.js: -------------------------------------------------------------------------------- 1 | /* 2 | Created By MSCSTSTS 3 | 2017-09-27 18:13:50 UTC+8 4 | 5 | 6 | Edit By MSCSTSTS 7 | 2017-12-02 19:36:17 UTC+8 8 | +触屏滑动切换上一章/下一章。 9 | +键盘左右方向键切换上一章/下一章。 10 | 11 | */ 12 | 13 | 14 | //-------------------------------------------------------------------------------------------- 15 | 16 | /* 17 | 登录、书签、加入书库等功能,都是基于刷新或者ajax 18 | 所以仅需修改部分页面 19 | 最后,将页面滑动到顶部即可。 20 | 处理逻辑: 21 | 1:页面加载,检查浏览器是否支持 ajax 和 sessionStorage 22 | 2:查询下一章 和 上一章是否已被缓存,若没有,则进行 ajax 请求,并将对应页面存入sessionStorage 23 | 3:点击上一章或下一章按钮以后,将缓存中的数据取出,重新渲染页面,回到顶部,修改浏览器url,并重新执行2 24 | 需要缓存的条目: 25 | 1:本页链接 data.url 26 | 2:下一章链接 data.next_chapter."下一章" 27 | 3:上一章链接 data.next_chapter."上一章" 28 | 4:章节名 data.name 29 | 5:小说名 data.novels_name 30 | 6:正文内容 data.soup 31 | 7:是否书签 data.bookmark 32 | 需要更新的页面内容: 33 | 0:【索引项】 页面链接(相对地址) 由 缓存数据的本页链接 拼接产生 34 | 1:书签状态 #bookMark 的 class ,根据 缓存数据 的 是否书签来定义 class 35 | 2:隐藏表单 page_url 的 value , 根据 缓存数据 的 本页链接 36 | 3:页面标题 page_title , 根据 缓存数据 的 章节名产生 37 | 4:正文文本 page_chapter_content ,根据 缓存数据 的 正文内容产生 38 | 5:上一章和下一章的地址 ,根据缓存数据的上一章链接和下一章链接产生 39 | 6:地址栏URL ,使用replaceState来更新,根据缓存数据的 本页链接 来拼接 40 | */ 41 | 42 | 43 | $(document).ready(function () { 44 | 45 | var page_btn_pre = $("div.pre_next > a:nth-child(1)"); 46 | var page_btn_next = $("div.pre_next > a:nth-child(2)"); 47 | var page_title = $("title"); 48 | var page_chapter_name = $("#content_name"); 49 | // var page_chapter_content = $(".show-content>div");//正文 50 | var page_chapter_content = $($(".show-content").children("*").get(0)); //正文 51 | var page_bookmark = $("#bookMark");//书签,需要修改样式 52 | var page_url = $("#url");//页面隐藏表单,本页地址 53 | 54 | function get_chapter(n_url) { 55 | $.ajax({ 56 | //提交数据的类型 POST GET 57 | type: "GET", 58 | //提交的网址 59 | url: n_url, 60 | //提交的数据 61 | data: {is_ajax: "owl_cache"}, 62 | //返回数据的格式 63 | datatype: "json",//"xml", "html", "script", "json", "jsonp", "text". 64 | //在请求之前调用的函数 65 | //beforeSend:function(){$("#msg").html("logining");}, 66 | //成功返回之后调用的函数 67 | success: function (data) { 68 | if (typeof data.name == "undefined") { 69 | 70 | } else { // 正确获取到数据 71 | var obj = { 72 | url: data.url, 73 | pre_chapter_url: transform(data.next_chapter)[0], 74 | next_chapter_url: transform(data.next_chapter)[1], 75 | name: data.name, 76 | novels_name: data.novels_name, 77 | content: data.soup, 78 | bookmark: data.bookmark, 79 | chapter_url: data.chapter_url 80 | }; 81 | store(n_url, obj); 82 | } 83 | }, 84 | //调用执行后调用的函数 85 | complete: function () { 86 | 87 | }, 88 | //调用出错执行的函数 89 | error: function () { 90 | //请求出错处理 91 | } 92 | }); 93 | } 94 | 95 | function transform(obj) { 96 | var arr = []; 97 | for (var item in obj) { 98 | arr.push(obj[item]); 99 | } 100 | return arr; 101 | } 102 | 103 | function store(n_url, obj) { 104 | window.sessionStorage.setItem(n_url, JSON.stringify(obj)); 105 | } 106 | 107 | function ajax_content_init() { 108 | if (isSupport()) { 109 | log("支持sessionStorage,将为你开启页面缓存"); 110 | search_url = window.location; 111 | // 来自书签页面的跳转不进行缓存 112 | if (search_url.search.indexOf("from_bookmarks") > 0) { 113 | log('来自书签页面的跳转不进行缓存') 114 | } else { 115 | ajax_task(); 116 | page_bookmark.bind("click", function () { 117 | cache_reset(); 118 | }); 119 | } 120 | } else { 121 | //不支持 122 | return; 123 | } 124 | } 125 | 126 | function ajax_task() { 127 | store_query();//检查是否已缓存 128 | page_btn_pre.unbind("click"); 129 | page_btn_pre.click(function () { 130 | event.preventDefault(); 131 | if (window.sessionStorage.getItem(page_btn_pre.attr("href")) === null) { 132 | //若未缓存 133 | window.location.href = page_btn_pre.attr("href"); 134 | } else { 135 | try { 136 | load(page_btn_pre.attr("href")); 137 | } catch (err) { 138 | window.location.href = page_btn_pre.attr("href"); 139 | } 140 | 141 | } 142 | }); 143 | page_btn_next.unbind("click"); 144 | page_btn_next.click(function () { 145 | event.preventDefault(); 146 | if (window.sessionStorage.getItem(page_btn_next.attr("href")) === null) { 147 | //若未缓存 148 | window.location.href = page_btn_next.attr("href"); 149 | } else { 150 | try { 151 | load(page_btn_next.attr("href")); 152 | } catch (err) { 153 | window.location.href = page_btn_next.attr("href"); 154 | } 155 | 156 | } 157 | }); 158 | } 159 | 160 | 161 | function load_bookmark(data) { 162 | page_bookmark.removeClass("bookMark"); 163 | page_bookmark.removeClass("bookMarkAct"); 164 | //log(data.bookmark); 165 | if (data.bookmark == 0) { 166 | page_bookmark.addClass("bookMark"); 167 | } else { 168 | page_bookmark.addClass("bookMarkAct"); 169 | } 170 | } 171 | 172 | function load_hiddenForm(data) { 173 | page_url.val(data.url); 174 | } 175 | 176 | function load_title(data) { 177 | page_title.html(data.name + " - owllook"); 178 | } 179 | 180 | function stripscript(s) {//用于过滤script标签 181 | return s.replace(/