404 lines
17 KiB
Python
404 lines
17 KiB
Python
#!/usr/bin/env python
|
||
import time
|
||
|
||
from jinja2 import Environment, PackageLoader, select_autoescape
|
||
from operator import itemgetter
|
||
from sanic import Blueprint
|
||
from sanic.response import redirect, html, text, json
|
||
|
||
from soulbook.config import RULES, LOGGER, REPLACE_RULES, ENGINE_PRIORITY, CONFIG
|
||
from soulbook.database.mongodb import MotorBase
|
||
from soulbook.fetcher.cache import cache_owllook_novels_content, cache_owllook_novels_chapter, \
|
||
cache_owllook_search_ranking
|
||
from soulbook.fetcher.function import get_time, get_netloc, get_scheme, get_timestamp
|
||
from soulbook.fetcher.novels_tools import get_novels_info
|
||
from soulbook.utils import ver_question
|
||
|
||
novels_bp = Blueprint('novels_blueprint')
|
||
novels_bp.static('/static/novels', CONFIG.BASE_DIR + '/static/novels')
|
||
|
||
|
||
@novels_bp.listener('before_server_start')
|
||
def setup_db(novels_bp, loop):
|
||
global motor_base
|
||
motor_base = MotorBase()
|
||
|
||
|
||
@novels_bp.listener('after_server_stop')
|
||
def close_connection(novels_bp, loop):
|
||
motor_base = None
|
||
|
||
|
||
# jinjia2 config
|
||
env = Environment(
|
||
loader=PackageLoader('soulbook.views.novels_blueprint', '../templates/novels'),
|
||
autoescape=select_autoescape(['html', 'xml', 'tpl']))
|
||
|
||
|
||
def template(tpl, **kwargs):
|
||
template = env.get_template(tpl)
|
||
return html(template.render(kwargs))
|
||
|
||
|
||
@novels_bp.route("/chapter")
|
||
async def chapter(request):
|
||
"""
|
||
返回小说章节目录页
|
||
: content_url 这决定当前U页面url的生成方式
|
||
: url 章节目录页源url
|
||
: novels_name 小说名称
|
||
:return: 小说章节内容页
|
||
"""
|
||
url = request.args.get('url', None)
|
||
novels_name = request.args.get('novels_name', None)
|
||
netloc = get_netloc(url)
|
||
# if netloc not in RULES.keys():
|
||
# return redirect(url)
|
||
if netloc in REPLACE_RULES.keys():
|
||
url = url.replace(REPLACE_RULES[netloc]['old'], REPLACE_RULES[netloc]['new'])
|
||
if RULES.get(netloc):
|
||
content_url = RULES[netloc].content_url
|
||
else:
|
||
content_url = "0"
|
||
content = await cache_owllook_novels_chapter(url=url, netloc=netloc)
|
||
if content:
|
||
content = str(content).strip('[],, Jjs').replace(', ', '').replace('onerror', '').replace('js', '').replace(
|
||
'加入书架', '').replace('target="_blank"', '').replace('"_blank"', '')
|
||
return template(
|
||
'chapter.html', novels_name=novels_name, url=url, content_url=content_url, soup=content)
|
||
else:
|
||
return text('解析失败,请将失败页面反馈给本站,请重新刷新一次,或者访问源网页:{url}'.format(url=url))
|
||
|
||
|
||
@novels_bp.route("/owllook_donate")
|
||
async def donate(request):
|
||
return template('donate.html')
|
||
|
||
|
||
@novels_bp.route("/owllook_feedback")
|
||
async def feedback(request):
|
||
return template('feedback.html')
|
||
|
||
|
||
@novels_bp.route("/")
|
||
async def index(request):
|
||
user = request['session'].get('user', None)
|
||
search_ranking = await cache_owllook_search_ranking()
|
||
if user:
|
||
return template('index.html', title='SoulBook - 网络小说搜索引擎', is_login=1, user=user,
|
||
search_ranking=search_ranking[:25])
|
||
else:
|
||
return template('index.html', title='soulBook - 网络小说搜索引擎', is_login=0, search_ranking=search_ranking[:25])
|
||
|
||
|
||
@novels_bp.route("/owllook_content")
|
||
async def owllook_content(request):
|
||
"""
|
||
返回小说章节内容页
|
||
: content_url 这决定当前U页面url的生成方式
|
||
: url 章节内容页源url
|
||
: chapter_url 小说目录源url
|
||
: novels_name 小说名称
|
||
:return: 小说章节内容页
|
||
"""
|
||
url = request.args.get('url', None)
|
||
chapter_url = request.args.get('chapter_url', None)
|
||
novels_name = request.args.get('novels_name', None)
|
||
name = request.args.get('name', '')
|
||
is_ajax = request.args.get('is_ajax', '')
|
||
# 拼接链接
|
||
if not url.startswith("http"):
|
||
# # 协议
|
||
# get_scheme(chapter_url)
|
||
# # 域名
|
||
# get_netloc(chapter_url)
|
||
if url.startswith("//"):
|
||
url = get_scheme(chapter_url) + ":" + url
|
||
elif url.startswith("/"):
|
||
url = get_scheme(chapter_url) + "://" + get_netloc(chapter_url) + url
|
||
else:
|
||
url = chapter_url + "/" + url
|
||
print("url: %s" % url)
|
||
# 当小说内容url不在解析规则内 跳转到原本url
|
||
netloc = get_netloc(url)
|
||
# if netloc not in RULES.keys():
|
||
# return redirect(url)
|
||
user = request['session'].get('user', None)
|
||
# 拼接小说目录url
|
||
book_url = "/chapter?url={chapter_url}&novels_name={novels_name}".format(
|
||
chapter_url=chapter_url,
|
||
novels_name=novels_name)
|
||
motor_db = motor_base.get_db()
|
||
if url == chapter_url:
|
||
# 阅读到最后章节时候 在数据库中保存最新阅读章节
|
||
if user and is_ajax == "owl_cache":
|
||
owl_referer = request.headers.get('Referer', '').split('owllook_content')[1]
|
||
if owl_referer:
|
||
latest_read = "/owllook_content" + owl_referer
|
||
await motor_db.user_message.update_one(
|
||
{'user': user, 'books_url.book_url': book_url},
|
||
{'$set': {'books_url.$.last_read_url': latest_read,
|
||
'books_url.$.set_time': time.time(),
|
||
"books_url.$.set_date": get_time()
|
||
}})
|
||
return redirect(book_url)
|
||
if RULES.get(netloc):
|
||
content_url = RULES[netloc].content_url
|
||
else:
|
||
content_url = "0"
|
||
content_data = await cache_owllook_novels_content(url=url, chapter_url=chapter_url,netloc=netloc)
|
||
if content_data:
|
||
try:
|
||
content = content_data.get('content', '获取失败')
|
||
next_chapter = content_data.get('next_chapter', [])
|
||
title = content_data.get('title', '').replace(novels_name, '')
|
||
name = title if title else name
|
||
# 拼接小说书签url
|
||
bookmark_url = "{path}?url={url}&name={name}&chapter_url={chapter_url}&novels_name={novels_name}".format(
|
||
path=request.path,
|
||
url=url,
|
||
name=name,
|
||
chapter_url=chapter_url,
|
||
novels_name=novels_name
|
||
)
|
||
# 破坏广告链接
|
||
content = str(content).strip('[]Jjs,').replace('http', 'hs').replace('.js', '').replace('();', '')
|
||
# content += """欢迎关注公众号【粮草小说】,享受精品书籍推荐以及实体书赠送福利!"""
|
||
# content += """欢迎关注公众号【Soul】,享受精品书籍推荐以及实体书赠送福利!"""
|
||
if "网页版章节内容慢,请下载爱阅小说app阅读最新内容" in content:
|
||
list_split = content.rsplit("网页版章节内容慢,请下载爱阅小说app阅读最新内容", 1)
|
||
content = list_split[0] + "</div>" + list_split[-1].split("</div>", 1)[-1]
|
||
if user:
|
||
bookmark = await motor_db.user_message.find_one({'user': user, 'bookmarks.bookmark': bookmark_url})
|
||
book = await motor_db.user_message.find_one({'user': user, 'books_url.book_url': book_url})
|
||
bookmark = 1 if bookmark else 0
|
||
if book:
|
||
# 当书架中存在该书源
|
||
book = 1
|
||
# 保存最后一次阅读记录
|
||
if is_ajax == "owl_cache":
|
||
owl_referer = request.headers.get('Referer', bookmark_url).split(
|
||
'owllook_content', 1)[-1]
|
||
latest_read = "/owllook_content" + owl_referer
|
||
await motor_db.user_message.update_one(
|
||
{'user': user, 'books_url.book_url': book_url},
|
||
{'$set': {'books_url.$.last_read_url': latest_read,
|
||
"books_url.$.set_time": get_timestamp(),
|
||
"books_url.$.set_date": get_time(),
|
||
# "books_url.$.name": title
|
||
}})
|
||
elif chapter_url and "owllook_content" in request.url:
|
||
owl_referer = request.url.split('owllook_content', 1)[-1]
|
||
latest_read = "/owllook_content" + owl_referer
|
||
await motor_db.user_message.update_one(
|
||
{'user': user, 'books_url.book_url': book_url},
|
||
{'$set': {'books_url.$.last_read_url': latest_read,
|
||
"books_url.$.set_time": get_timestamp(),
|
||
"books_url.$.set_date": get_time(),
|
||
"books_url.$.name_now": title
|
||
}})
|
||
else:
|
||
book = 0
|
||
if is_ajax == "owl_cache":
|
||
owl_cache_dict = dict(
|
||
is_login=1,
|
||
user=user,
|
||
name=name,
|
||
url=url,
|
||
bookmark=bookmark,
|
||
book=book,
|
||
content_url=content_url,
|
||
chapter_url=chapter_url,
|
||
novels_name=novels_name,
|
||
next_chapter=next_chapter,
|
||
soup=content
|
||
)
|
||
return json(owl_cache_dict)
|
||
return template(
|
||
'content.html',
|
||
is_login=1,
|
||
user=user,
|
||
name=name,
|
||
url=url,
|
||
bookmark=bookmark,
|
||
book=book,
|
||
content_url=content_url,
|
||
chapter_url=chapter_url,
|
||
novels_name=novels_name,
|
||
next_chapter=next_chapter,
|
||
soup=content)
|
||
else:
|
||
if is_ajax == "owl_cache":
|
||
owl_cache_dict = dict(
|
||
is_login=0,
|
||
name=name,
|
||
url=url,
|
||
bookmark=0,
|
||
book=0,
|
||
content_url=content_url,
|
||
chapter_url=chapter_url,
|
||
novels_name=novels_name,
|
||
next_chapter=next_chapter,
|
||
soup=content
|
||
)
|
||
return json(owl_cache_dict)
|
||
return template(
|
||
'content.html',
|
||
is_login=0,
|
||
name=name,
|
||
url=url,
|
||
bookmark=0,
|
||
book=0,
|
||
content_url=content_url,
|
||
chapter_url=chapter_url,
|
||
novels_name=novels_name,
|
||
next_chapter=next_chapter,
|
||
soup=content)
|
||
except Exception as e:
|
||
LOGGER.exception(e)
|
||
return redirect(book_url)
|
||
else:
|
||
if user:
|
||
is_login = 1
|
||
user = user
|
||
return template('parse_error.html', url=url, is_login=is_login, user=user)
|
||
else:
|
||
is_login = 0
|
||
return template('parse_error.html', url=url, is_login=is_login)
|
||
|
||
|
||
@novels_bp.route("/register")
|
||
async def owllook_register(request):
|
||
"""
|
||
用户登录
|
||
:param request:
|
||
:return:
|
||
: -1 用户名或密码不能为空
|
||
: 0 用户名或密码错误
|
||
: 1 登陆成功
|
||
"""
|
||
user = request['session'].get('user', None)
|
||
if user:
|
||
return redirect('/')
|
||
else:
|
||
ver_que_ans = ver_question()
|
||
if ver_que_ans:
|
||
request['session']['index'] = ver_que_ans
|
||
return template(
|
||
'register.html',
|
||
title='soulbook - 注册 - 网络小说搜索引擎',
|
||
question=ver_que_ans[1]
|
||
)
|
||
else:
|
||
return redirect('/')
|
||
|
||
|
||
@novels_bp.route("/search", methods=['GET'])
|
||
async def owllook_search(request):
|
||
start = time.time()
|
||
name = str(request.args.get('wd', '')).strip()
|
||
novels_keyword = name.split(' ')[0]
|
||
motor_db = motor_base.get_db()
|
||
if not name:
|
||
return redirect('/')
|
||
else:
|
||
# 记录搜索小说名
|
||
try:
|
||
await motor_db.search_records.update_one({'keyword': name}, {'$inc': {'count': 1}}, upsert=True)
|
||
except Exception as e:
|
||
LOGGER.exception(e)
|
||
# 通过搜索引擎获取检索结果
|
||
parse_result = None
|
||
if name.startswith('!baidu'):
|
||
novels_keyword = name.split('baidu')[1].strip()
|
||
novels_name = 'intitle:{name} 小说 阅读'.format(name=novels_keyword)
|
||
parse_result = await get_novels_info(class_name='baidu', novels_name=novels_name)
|
||
elif name.startswith('!360'):
|
||
novels_keyword = name.split('360')[1].strip()
|
||
novels_name = "{name} 小说 最新章节".format(name=novels_keyword)
|
||
parse_result = await get_novels_info(class_name='so', novels_name=novels_name)
|
||
elif name.startswith('!bing'):
|
||
novels_keyword = name.split('bing')[1].strip()
|
||
novels_name = "{name} 小说 阅读 最新章节".format(name=novels_keyword)
|
||
parse_result = await get_novels_info(class_name='bing', novels_name=novels_name)
|
||
elif name.startswith('!duck_go'):
|
||
novels_keyword = name.split('duck_go')[1].strip()
|
||
novels_name = '{name} 小说 阅读 最新章节'.format(name=novels_keyword)
|
||
parse_result = await get_novels_info(class_name='duck_go', novels_name=novels_name)
|
||
else:
|
||
for each_engine in ENGINE_PRIORITY:
|
||
# for bing
|
||
if each_engine == "bing":
|
||
novels_name = "{name} 小说 阅读 最新章节".format(name=name)
|
||
parse_result = await get_novels_info(class_name='bing', novels_name=novels_name)
|
||
if parse_result:
|
||
break
|
||
# for 360 so
|
||
if each_engine == "360":
|
||
novels_name = "{name} 小说 最新章节".format(name=name)
|
||
parse_result = await get_novels_info(class_name='so', novels_name=novels_name)
|
||
if parse_result:
|
||
break
|
||
# for baidu
|
||
if each_engine == "baidu":
|
||
novels_name = 'intitle:{name} 小说 阅读'.format(name=name)
|
||
parse_result = await get_novels_info(class_name='baidu', novels_name=novels_name)
|
||
if parse_result:
|
||
break
|
||
# for duckduckgo
|
||
if each_engine == "duck_go":
|
||
novels_name = '{name} 小说 阅读 最新章节'.format(name=name)
|
||
parse_result = await get_novels_info(class_name='duck_go', novels_name=novels_name)
|
||
if parse_result:
|
||
break
|
||
if parse_result:
|
||
# result_sorted = sorted(
|
||
# parse_result, reverse=True, key=lambda res: res['timestamp']) if ':baidu' not in name else parse_result
|
||
# 优先依靠是否解析进行排序 其次以更新时间进行排序
|
||
result_sorted = sorted(
|
||
parse_result,
|
||
reverse=True,
|
||
key=itemgetter('is_recommend', 'is_parse', 'timestamp'))
|
||
user = request['session'].get('user', None)
|
||
if user:
|
||
try:
|
||
time_current = get_time()
|
||
res = await motor_db.user_message.update_one({'user': user},
|
||
{'$set': {'last_update_time': time_current}},
|
||
upsert=True)
|
||
# 此处语法操作过多 下次看一遍mongo再改
|
||
if res:
|
||
is_ok = await motor_db.user_message.update_one(
|
||
{'user': user, 'search_records.keyword': {'$ne': novels_keyword}},
|
||
{'$push': {'search_records': {'keyword': novels_keyword, 'counts': 1}}},
|
||
)
|
||
|
||
if is_ok:
|
||
await motor_db.user_message.update_one(
|
||
{'user': user, 'search_records.keyword': novels_keyword},
|
||
{'$inc': {'search_records.$.counts': 1}}
|
||
)
|
||
|
||
except Exception as e:
|
||
LOGGER.exception(e)
|
||
return template(
|
||
'result.html',
|
||
is_login=1,
|
||
user=user,
|
||
name=novels_keyword,
|
||
time='%.2f' % (time.time() - start),
|
||
result=result_sorted,
|
||
count=len(parse_result))
|
||
|
||
else:
|
||
return template(
|
||
'result.html',
|
||
is_login=0,
|
||
name=novels_keyword,
|
||
time='%.2f' % (time.time() - start),
|
||
result=result_sorted,
|
||
count=len(parse_result))
|
||
|
||
else:
|
||
return html("No Result!请将小说名反馈给本站,谢谢!")
|