Sync all projects
This commit is contained in:
@@ -0,0 +1,425 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# File : base_spider.py
|
||||
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
|
||||
# Author's Blog: https://blog.csdn.net/qq_32394351
|
||||
# Date : 2024/1/7
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
import json
|
||||
import time
|
||||
import base64
|
||||
import re
|
||||
from pathlib import Path
|
||||
import io
|
||||
import tokenize
|
||||
from urllib.parse import quote
|
||||
|
||||
"""
|
||||
配置示例:
|
||||
t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾
|
||||
api里会自动含有ext参数是base64编码后的选中的筛选条件
|
||||
{
|
||||
"key":"hipy_t4_base_spider",
|
||||
"name":"base_spider(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/base_spider",
|
||||
"searchable":1,
|
||||
"quickSearch":0,
|
||||
"filterable":1,
|
||||
"ext":"base_spider"
|
||||
},
|
||||
{
|
||||
"key": "hipy_t3_base_spider",
|
||||
"name": "base_spider(hipy_t3)",
|
||||
"type": 3,
|
||||
"api": "{{host}}/txt/hipy/base_spider.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 0,
|
||||
"filterable": 1,
|
||||
"ext": "{{host}}/txt/hipy/base_spider.json"
|
||||
},
|
||||
"""
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
def getName(self):
|
||||
return "规则名称如:基础示例"
|
||||
|
||||
def init_api_ext_file(self):
|
||||
"""
|
||||
这个函数用于初始化py文件对应的json文件,用于存筛选规则。
|
||||
执行此函数会自动生成筛选文件
|
||||
@return:
|
||||
"""
|
||||
ext_file = __file__.replace('.py', '.json')
|
||||
print(f'ext_file:{ext_file}')
|
||||
ext_file_dict = {
|
||||
"分类1": [{"key": "letter", "name": "首字母", "value": [{"n": "A", "v": "A"}, {"n": "B", "v": "B"}]}],
|
||||
"分类2": [{"key": "letter", "name": "首字母", "value": [{"n": "A", "v": "A"}, {"n": "B", "v": "B"}]},
|
||||
{"key": "year", "name": "年份",
|
||||
"value": [{"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}]}],
|
||||
}
|
||||
with open(ext_file, mode='w+', encoding='utf-8') as f:
|
||||
f.write(json.dumps(ext_file_dict, ensure_ascii=False))
|
||||
|
||||
def init(self, extend=""):
|
||||
"""
|
||||
初始化加载extend,一般与py文件名同名的json文件作为扩展筛选
|
||||
@param extend:
|
||||
@return:
|
||||
"""
|
||||
|
||||
def init_file(ext_file):
|
||||
"""
|
||||
根据与py对应的json文件去扩展规则的筛选条件
|
||||
"""
|
||||
ext_file = Path(ext_file).as_posix()
|
||||
if os.path.exists(ext_file):
|
||||
with open(ext_file, mode='r', encoding='utf-8') as f:
|
||||
try:
|
||||
ext_dict = json.loads(f.read())
|
||||
self.config['filter'].update(ext_dict)
|
||||
except Exception as e:
|
||||
print(f'更新扩展筛选条件发生错误:{e}')
|
||||
|
||||
ext = self.extend
|
||||
print(f"============ext:{ext},extend:{extend}============")
|
||||
if isinstance(ext, str) and ext:
|
||||
if ext.startswith('./'):
|
||||
ext_file = os.path.join(os.path.dirname(__file__), ext)
|
||||
init_file(ext_file)
|
||||
elif ext.startswith('http'):
|
||||
try:
|
||||
r = self.fetch(ext)
|
||||
self.config['filter'].update(r.json())
|
||||
except Exception as e:
|
||||
print(f'更新扩展筛选条件发生错误:{e}')
|
||||
elif not ext.startswith('./') and not ext.startswith('http'):
|
||||
ext_file = os.path.join(os.path.dirname(__file__), './' + ext + '.json')
|
||||
init_file(ext_file)
|
||||
|
||||
# 装载模块,这里只要一个就够了
|
||||
if isinstance(extend, list):
|
||||
for lib in extend:
|
||||
if '.Spider' in str(type(lib)):
|
||||
self.module = lib
|
||||
break
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filterable=False):
|
||||
"""
|
||||
获取首页分类及筛选数据
|
||||
@param filterable: 能否筛选,跟t3/t4配置里的filterable参数一致
|
||||
@return:
|
||||
"""
|
||||
class_name = '电影&电视剧&综艺&动漫' # 静态分类名称拼接
|
||||
class_url = '1&2&3&4' # 静态分类标识拼接
|
||||
|
||||
result = {}
|
||||
classes = []
|
||||
|
||||
if all([class_name, class_url]):
|
||||
class_names = class_name.split('&')
|
||||
class_urls = class_url.split('&')
|
||||
cnt = min(len(class_urls), len(class_names))
|
||||
for i in range(cnt):
|
||||
classes.append({
|
||||
'type_name': class_names[i],
|
||||
'type_id': class_urls[i]
|
||||
})
|
||||
|
||||
result['class'] = classes
|
||||
if filterable:
|
||||
result['filters'] = self.config['filter']
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
"""
|
||||
首页推荐列表
|
||||
@return:
|
||||
"""
|
||||
d = []
|
||||
d.append({
|
||||
'vod_name': '测试',
|
||||
'vod_id': 'index.html',
|
||||
'vod_pic': 'https://gitee.com/CherishRx/imagewarehouse/raw/master/image/13096725fe56ce9cf643a0e4cd0c159c.gif',
|
||||
'vod_remarks': '原始hipy',
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filterable, extend):
|
||||
"""
|
||||
返回一级列表页数据
|
||||
@param tid: 分类id
|
||||
@param pg: 当前页数
|
||||
@param filterable: 能否筛选
|
||||
@param extend: 当前筛选数据
|
||||
@return:
|
||||
"""
|
||||
page_count = 24 # 默认赋值一页列表24条数据
|
||||
|
||||
d = []
|
||||
d.append({
|
||||
'vod_name': '测试',
|
||||
'vod_id': 'index.html',
|
||||
'vod_pic': 'https://gitee.com/CherishRx/imagewarehouse/raw/master/image/13096725fe56ce9cf643a0e4cd0c159c.gif',
|
||||
'vod_remarks': '类型:' + tid,
|
||||
})
|
||||
result = {
|
||||
'list': d,
|
||||
'page': pg,
|
||||
'pagecount': 9999 if len(d) >= page_count else pg,
|
||||
'limit': 90,
|
||||
'total': 999999,
|
||||
}
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
"""
|
||||
返回二级详情页数据
|
||||
@param ids: 一级传过来的vod_id列表
|
||||
@return:
|
||||
"""
|
||||
vod_id = ids[0]
|
||||
vod = {"vod_id": vod_id,
|
||||
"vod_name": '测试二级',
|
||||
"vod_pic": 'https://gitee.com/CherishRx/imagewarehouse/raw/master/image/13096725fe56ce9cf643a0e4cd0c159c.gif',
|
||||
"type_name": '详情页类型',
|
||||
"vod_year": '详情页年份',
|
||||
"vod_area": '详情页地区',
|
||||
"vod_remarks": '详情页标签',
|
||||
"vod_actor": '详情页演员名称',
|
||||
"vod_director": '详情页导演名称',
|
||||
"vod_content": '详情页剧情描述',
|
||||
"vod_play_from": '测试线路1$$$测试线路2',
|
||||
"vod_play_url": '选集播放1$1.mp4#选集播放2$2.mp4$$$选集播放3$3.mp4#选集播放4$4.mp4'}
|
||||
result = {
|
||||
'list': [vod]
|
||||
}
|
||||
return result
|
||||
|
||||
def searchContent(self, wd, quick=False, pg=1):
|
||||
"""
|
||||
返回搜索列表
|
||||
@param wd: 搜索关键词
|
||||
@param quick: 是否来自快速搜索。t3/t4配置里启用了快速搜索,在快速搜索在执行才会是True
|
||||
@return:
|
||||
"""
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
|
||||
"Host": "www.bttwo.net",
|
||||
"Referer": "https://www.bttwo.net/"
|
||||
}
|
||||
|
||||
url = f'https://www.bttwo.net/xssearch?q={quote(wd)}'
|
||||
r = self.fetch(url, headers=headers)
|
||||
cookies = ['myannoun=1']
|
||||
for key, value in r.headers.items():
|
||||
if str(key).lower() == 'set-cookie':
|
||||
cookies.append(value.split(';')[0])
|
||||
new_headers = {
|
||||
'Cookie': ';'.join(cookies),
|
||||
# 'Pragma': 'no-cache',
|
||||
# 'Origin': 'https://www.bttwo.net',
|
||||
# 'Referer': url,
|
||||
# 'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
|
||||
# 'Sec-Ch-Ua-Mobile': '?0',
|
||||
# 'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
# 'Sec-Fetch-Dest': 'document',
|
||||
# 'Sec-Fetch-Mode': 'navigate',
|
||||
# 'Sec-Fetch-Site': 'same-origin',
|
||||
# 'Sec-Fetch-User': '?1',
|
||||
# 'Upgrade-Insecure-Requests': '1',
|
||||
}
|
||||
headers.update(new_headers)
|
||||
print(headers)
|
||||
|
||||
html = self.html(r.text)
|
||||
captcha = ''.join(html.xpath('//*[@class="erphp-search-captcha"]/form/text()')).strip()
|
||||
print('验证码:', captcha)
|
||||
answer = self.eval_computer(captcha)
|
||||
print('回答:', captcha, answer)
|
||||
data = {'result': str(answer)}
|
||||
print('待post数据:', data)
|
||||
self.post(url, data=data, headers=headers, cookies=None)
|
||||
r = self.fetch(url, headers=headers)
|
||||
# print(r.text)
|
||||
html = self.html(r.text)
|
||||
lis = html.xpath('//*[contains(@class,"search_list")]/ul/li')
|
||||
print('搜索结果数:', len(lis))
|
||||
d = []
|
||||
if len(lis) < 1:
|
||||
d.append({
|
||||
'vod_name': wd,
|
||||
'vod_id': 'index.html',
|
||||
'vod_pic': 'https://gitee.com/CherishRx/imagewarehouse/raw/master/image/13096725fe56ce9cf643a0e4cd0c159c.gif',
|
||||
'vod_remarks': '测试搜索',
|
||||
})
|
||||
else:
|
||||
for li in lis:
|
||||
d.append({
|
||||
'vod_name': ''.join(li.xpath('h3//text()')),
|
||||
'vod_id': ''.join(li.xpath('a/@href')),
|
||||
'vod_pic': ''.join(li.xpath('a/img/@data-original')),
|
||||
'vod_remarks': ''.join(li.xpath('p//text()')),
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
print(result)
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
"""
|
||||
解析播放,返回json。壳子视情况播放直链或进行嗅探
|
||||
@param flag: vod_play_from 播放来源线路
|
||||
@param id: vod_play_url 播放的链接
|
||||
@param vipFlags: vip标识
|
||||
@return:
|
||||
"""
|
||||
# url = 'http://bizcommon.alicdn.com/l2nDqpMmn6DGHnWzZQA/Cg9qI5imMInpPvK5Mnm%40%40hd.m3u8'
|
||||
url = 'https://s1.bfzycdn.com/video/renmindemingyi/%E7%AC%AC07%E9%9B%86/index.m3u8'
|
||||
parse = 0
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
|
||||
}
|
||||
result = {
|
||||
'parse': parse, # 1=嗅探,0=播放
|
||||
'playUrl': '', # 解析链接
|
||||
'url': url, # 直链或待嗅探地址
|
||||
'header': headers, # 播放UA
|
||||
}
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def adRemove():
|
||||
return 'reg:/video/adjump.*?ts'
|
||||
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {}
|
||||
}
|
||||
header = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
|
||||
"Host": "www.baidu.com",
|
||||
"Referer": "https://www.baidu.com/"
|
||||
}
|
||||
|
||||
def localProxy(self, params):
|
||||
# http://192.168.31.49:5707/api/v1/vod/哔滴影视?proxy=1&do=py&type=1.m3u8
|
||||
print(params)
|
||||
content = """
|
||||
#EXTM3U
|
||||
#EXT-X-VERSION:3
|
||||
#EXT-X-ALLOW-CACHE:YES
|
||||
#EXT-X-MEDIA-SEQUENCE:170471784
|
||||
#EXT-X-TARGETDURATION:10
|
||||
#EXT-X-PROGRAM-DATE-TIME:2024-01-11T20:43:53+08:00
|
||||
#EXTINF:10.000, no desc
|
||||
http://gctxyc.liveplay.myqcloud.com/gc/gllj01_1_md-170471784.ts
|
||||
#EXT-X-PROGRAM-DATE-TIME:2024-01-11T20:44:03+08:00
|
||||
#EXTINF:10.000, no desc
|
||||
http://gctxyc.liveplay.myqcloud.com/gc/gllj01_1_md-170471785.ts
|
||||
#EXT-X-PROGRAM-DATE-TIME:2024-01-11T20:44:13+08:00
|
||||
#EXTINF:10.000, no desc
|
||||
http://gctxyc.liveplay.myqcloud.com/gc/gllj01_1_md-170471786.ts
|
||||
#EXT-X-PROGRAM-DATE-TIME:2024-01-11T20:44:23+08:00
|
||||
#EXTINF:10.000, no desc
|
||||
http://gctxyc.liveplay.myqcloud.com/gc/gllj01_1_md-170471787.ts
|
||||
""".strip()
|
||||
return [200, 'text/plain', content]
|
||||
# return [404, 'text/plain', 'Not Found']
|
||||
# return [200, "video/MP2T", content]
|
||||
# return [200, "video/MP2T", ""]
|
||||
|
||||
# -----------------------------------------------自定义函数-----------------------------------------------
|
||||
def eval_computer(self, text):
|
||||
"""
|
||||
自定义的字符串安全计算器
|
||||
@param text:字符串的加减乘除
|
||||
@return:计算后得到的值
|
||||
"""
|
||||
localdict = {}
|
||||
self.safe_eval(f'ret={text.replace("=", "")}', localdict)
|
||||
ret = localdict.get('ret') or None
|
||||
return ret
|
||||
|
||||
def safe_eval(self, code: str = '', localdict: dict = None):
|
||||
code = code.strip()
|
||||
if not code:
|
||||
return {}
|
||||
if localdict is None:
|
||||
localdict = {}
|
||||
builtins = __builtins__
|
||||
if not isinstance(builtins, dict):
|
||||
builtins = builtins.__dict__.copy()
|
||||
else:
|
||||
builtins = builtins.copy()
|
||||
for key in ['__import__', 'eval', 'exec', 'globals', 'dir', 'copyright', 'open', 'quit']:
|
||||
del builtins[key] # 删除不安全的关键字
|
||||
# print(builtins)
|
||||
global_dict = {'__builtins__': builtins,
|
||||
'json': json, 'print': print,
|
||||
're': re, 'time': time, 'base64': base64
|
||||
} # 禁用内置函数,不允许导入包
|
||||
try:
|
||||
self.check_unsafe_attributes(code)
|
||||
exec(code, global_dict, localdict)
|
||||
return localdict
|
||||
except Exception as e:
|
||||
return {'error': f'执行报错:{e}'}
|
||||
|
||||
# ==================== 静态函数 ======================
|
||||
@staticmethod
|
||||
def check_unsafe_attributes(string):
|
||||
"""
|
||||
安全检测需要exec执行的python代码
|
||||
:param string:
|
||||
:return:
|
||||
"""
|
||||
g = tokenize.tokenize(io.BytesIO(string.encode('utf-8')).readline)
|
||||
pre_op = ''
|
||||
for toktype, tokval, _, _, _ in g:
|
||||
if toktype == tokenize.NAME and pre_op == '.' and tokval.startswith('_'):
|
||||
attr = tokval
|
||||
msg = "access to attribute '{0}' is unsafe.".format(attr)
|
||||
raise AttributeError(msg)
|
||||
elif toktype == tokenize.OP:
|
||||
pre_op = tokval
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
spider = Spider()
|
||||
spider.init()
|
||||
# spider.init_api_ext_file() # 生成筛选对应的json文件
|
||||
spider.log({'key': 'value'})
|
||||
spider.log('====文本内容====')
|
||||
with open('test_1.txt', encoding='utf-8') as f:
|
||||
code = f.read()
|
||||
a = spider.superStr2dict(code)
|
||||
print(type(a), a)
|
||||
# spider.searchContent('斗罗大陆')
|
||||
print(spider.playerContent(None, 1, None))
|
||||
with open('ad.m3u8', encoding='utf-8') as f:
|
||||
adt = f.read()
|
||||
url = adt.split('\n')[0]
|
||||
adt = '\n'.join(adt.split('\n')[1:])
|
||||
ad_remove = 'reg:/video/adjump(.*?)ts'
|
||||
print(spider.fixAdM3u8(adt, url, ad_remove))
|
||||
@@ -0,0 +1,1056 @@
|
||||
# coding=utf-8
|
||||
# !/usr/bin/python
|
||||
import os.path
|
||||
import random
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
import json
|
||||
import time
|
||||
import base64
|
||||
import datetime
|
||||
import re
|
||||
from urllib import request, parse
|
||||
from pathlib import Path
|
||||
import urllib
|
||||
import urllib.request
|
||||
|
||||
"""
|
||||
配置示例:
|
||||
t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾
|
||||
api里会自动含有ext参数是base64编码后的选中的筛选条件
|
||||
|
||||
错误示例,ext含有json:
|
||||
{
|
||||
"key":"hipy_cntv央视",
|
||||
"name":"cntv央视(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/cntv央视?api_ext={{host}}/txt/hipy/cntv央视.json",
|
||||
"searchable":1,
|
||||
"quickSearch":1,
|
||||
"filterable":0,
|
||||
"ext":"cntv央视.json"
|
||||
}
|
||||
正确示例。同时存在ext和api_ext会优先取ext作为extend加载init
|
||||
{
|
||||
"key":"hipy_t4_cntv央视",
|
||||
"name":"cntv央视(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/cntv央视?api_ext={{host}}/txt/hipy/cntv央视.json",
|
||||
"searchable":1,
|
||||
"quickSearch":0,
|
||||
"filterable":1,
|
||||
"ext":"{{host}}/files/hipy/cntv央视.json"
|
||||
},
|
||||
{
|
||||
"key": "hipy_t3_cntv央视",
|
||||
"name": "cntv央视(hipy_t3)",
|
||||
"type": 3,
|
||||
"api": "{{host}}/txt/hipy/cntv央视.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 0,
|
||||
"filterable": 1,
|
||||
"ext": "{{host}}/files/hipy/cntv央视.json"
|
||||
},
|
||||
"""
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
module = None
|
||||
|
||||
def getDependence(self):
|
||||
return ['base_spider']
|
||||
|
||||
def getName(self):
|
||||
return "中央电视台" # 可搜索
|
||||
|
||||
def init_api_ext_file(self):
|
||||
ext_file = __file__.replace('.py', '.json')
|
||||
print(f'ext_file:{ext_file}')
|
||||
# 特别节目网页: https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65
|
||||
# 特别节目分类筛选获取页面: https://tv.cctv.com/yxg/tbjm/index.shtml
|
||||
# 纪录片网页: https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65
|
||||
# 纪录片分类筛选获取页面:https://tv.cctv.com/yxg/jlp/index.shtml
|
||||
# ==================== 获取特别节目的筛选条件 ======================
|
||||
r = self.fetch('https://tv.cctv.com/yxg/tbjm/index.shtml')
|
||||
html = r.text
|
||||
html = self.html(html)
|
||||
|
||||
filter_tbjm = []
|
||||
lis = html.xpath('//*[@id="pindao"]/li')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./span//text()')),
|
||||
'v': ''.join(li.xpath('@datacd')),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_tbjm.append({
|
||||
"key": "datapd-channel",
|
||||
"name": "频道",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
lis = html.xpath('//*[@id="fenlei"]/li')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./span//text()')),
|
||||
'v': ''.join(li.xpath('@datalx')),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_tbjm.append({
|
||||
"key": "datafl-sc",
|
||||
"name": "类型",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
lis = html.xpath('//*[@id="zimu"]/li')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./span//text()')),
|
||||
'v': ''.join(li.xpath('@datazm')),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_tbjm.append({
|
||||
"key": "dataszm-letter",
|
||||
"name": "首字母",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
print(filter_tbjm)
|
||||
|
||||
# ==================== 纪录片筛选获取 ======================
|
||||
r = self.fetch('https://tv.cctv.com/yxg/jlp/index.shtml')
|
||||
html = r.text
|
||||
html = self.html(html)
|
||||
|
||||
filter_jlp = []
|
||||
lis = html.xpath('//*[@id="pindao"]/li')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./span//text()')),
|
||||
'v': ''.join(li.xpath('@datacd')),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_jlp.append({
|
||||
"key": "datapd-channel",
|
||||
"name": "频道",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
lis = html.xpath('//*[@id="fenlei"]/li')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./span//text()')),
|
||||
'v': ''.join(li.xpath('@datalx')),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_jlp.append({
|
||||
"key": "datafl-sc",
|
||||
"name": "类型",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
lis = html.xpath('//*[@id="nianfen"]/li')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./span//text()')),
|
||||
'v': ''.join(li.xpath('@datanf')),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_jlp.append({
|
||||
"key": "datanf-year",
|
||||
"name": "年份",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
lis = html.xpath('//*[@id="zimu"]/li')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./span//text()')),
|
||||
'v': ''.join(li.xpath('@datazm')),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_jlp.append({
|
||||
"key": "dataszm-letter",
|
||||
"name": "首字母",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
print(filter_jlp)
|
||||
|
||||
ext_file_dict = {
|
||||
"特别节目": filter_tbjm,
|
||||
"纪录片": filter_jlp,
|
||||
}
|
||||
|
||||
# print(json.dumps(ext_file_dict,ensure_ascii=False,indent=4))
|
||||
with open(ext_file, mode='w+', encoding='utf-8') as f:
|
||||
# f.write(json.dumps(ext_file_dict,ensure_ascii=False,indent=4))
|
||||
f.write(json.dumps(ext_file_dict, ensure_ascii=False))
|
||||
|
||||
def init(self, extend=""):
|
||||
def init_file(ext_file):
|
||||
ext_file = Path(ext_file).as_posix()
|
||||
# print(f'ext_file:{ext_file}')
|
||||
if os.path.exists(ext_file):
|
||||
# print('存在扩展文件')
|
||||
with open(ext_file, mode='r', encoding='utf-8') as f:
|
||||
try:
|
||||
ext_dict = json.loads(f.read())
|
||||
# print(ext_dict)
|
||||
self.config['filter'].update(ext_dict)
|
||||
except Exception as e:
|
||||
print(f'更新扩展筛选条件发生错误:{e}')
|
||||
|
||||
print("============依赖列表:{0}============".format(extend))
|
||||
ext = self.extend
|
||||
print("============ext:{0}============".format(ext))
|
||||
if isinstance(ext, str) and ext:
|
||||
if ext.startswith('./'):
|
||||
ext_file = os.path.join(os.path.dirname(__file__), ext)
|
||||
init_file(ext_file)
|
||||
elif ext.startswith('http'):
|
||||
try:
|
||||
r = self.fetch(ext)
|
||||
self.config['filter'].update(r.json())
|
||||
except Exception as e:
|
||||
print(f'更新扩展筛选条件发生错误:{e}')
|
||||
elif not ext.startswith('./') and not ext.startswith('http'):
|
||||
ext_file = os.path.join(os.path.dirname(__file__), './' + ext + '.json')
|
||||
init_file(ext_file)
|
||||
|
||||
# ==================== 栏目大全加载年月筛选 ======================
|
||||
lanmu_list = self.config['filter']['栏目大全']
|
||||
lanmu_keys_list = [lanmu['key'] for lanmu in lanmu_list]
|
||||
if 'year' not in lanmu_keys_list:
|
||||
currentYear = datetime.date.today().year
|
||||
yearList = [{"n": "全部", "v": ""}]
|
||||
for year in range(currentYear, currentYear - 10, -1):
|
||||
yearList.append({"n": year, "v": year})
|
||||
yearDict = {"key": "year", "name": "年份", "value": yearList}
|
||||
lanmu_list.append(yearDict)
|
||||
if 'month' not in lanmu_keys_list:
|
||||
monthList = [{"n": "全部", "v": ""}]
|
||||
for month in range(1, 13):
|
||||
text = str(month).rjust(2, '0')
|
||||
monthList.append({"n": text, "v": text})
|
||||
monthDict = {"key": "month", "name": "月份", "value": monthList}
|
||||
lanmu_list.append(monthDict)
|
||||
|
||||
# 装载模块,这里只要一个就够了
|
||||
if isinstance(extend, list):
|
||||
for lib in extend:
|
||||
if '.Spider' in str(type(lib)):
|
||||
self.module = lib
|
||||
break
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
cateManual = {
|
||||
"4K专区": "4K专区",
|
||||
"栏目大全": "栏目大全",
|
||||
"特别节目": "特别节目",
|
||||
"纪录片": "纪录片",
|
||||
"电视剧": "电视剧",
|
||||
"动画片": "动画片",
|
||||
"频道直播": "频道直播",
|
||||
|
||||
}
|
||||
classes = []
|
||||
for k in cateManual:
|
||||
classes.append({
|
||||
'type_name': k,
|
||||
'type_id': cateManual[k]
|
||||
})
|
||||
result['class'] = classes
|
||||
if (filter):
|
||||
result['filters'] = self.config['filter']
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
result = {
|
||||
'list': []
|
||||
}
|
||||
if self.module:
|
||||
result = self.module.homeVideoContent()
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
result = {}
|
||||
month = "" # 月
|
||||
year = "" # 年
|
||||
area = '' # 地区
|
||||
channel = '' # 频道
|
||||
datafl = '' # 类型
|
||||
letter = '' # 字母
|
||||
year_prefix = '' # 栏目大全的年月筛选过滤
|
||||
pagecount = 24
|
||||
if tid == '动画片':
|
||||
id = urllib.parse.quote(tid)
|
||||
if 'datadq-area' in extend.keys():
|
||||
area = urllib.parse.quote(extend['datadq-area'])
|
||||
if 'dataszm-letter' in extend.keys():
|
||||
letter = extend['dataszm-letter']
|
||||
if 'datafl-sc' in extend.keys():
|
||||
datafl = urllib.parse.quote(extend['datafl-sc'])
|
||||
url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955899450127&area={0}&sc={4}&fc={1}&letter={2}&p={3}&n=24&serviceId=tvcctv&topv=1&t=json'.format(
|
||||
area, id, letter, pg, datafl)
|
||||
elif tid == '纪录片':
|
||||
id = urllib.parse.quote(tid)
|
||||
if 'datapd-channel' in extend.keys():
|
||||
channel = urllib.parse.quote(extend['datapd-channel'])
|
||||
if 'datafl-sc' in extend.keys():
|
||||
datafl = urllib.parse.quote(extend['datafl-sc'])
|
||||
if 'datanf-year' in extend.keys():
|
||||
year = extend['datanf-year']
|
||||
if 'dataszm-letter' in extend.keys():
|
||||
letter = extend['dataszm-letter']
|
||||
url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955924871139&fc={0}&channel={1}&sc={2}&year={3}&letter={4}&p={5}&n=24&serviceId=tvcctv&topv=1&t=json'.format(
|
||||
id, channel, datafl, year, letter, pg)
|
||||
elif tid == '电视剧':
|
||||
id = urllib.parse.quote(tid)
|
||||
if 'datafl-sc' in extend.keys():
|
||||
datafl = urllib.parse.quote(extend['datafl-sc'])
|
||||
if 'datanf-year' in extend.keys():
|
||||
year = extend['datanf-year']
|
||||
if 'dataszm-letter' in extend.keys():
|
||||
letter = extend['dataszm-letter']
|
||||
url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955853485115&area={0}&sc={1}&fc={2}&year={3}&letter={4}&p={5}&n=24&serviceId=tvcctv&topv=1&t=json'.format(
|
||||
area, datafl, id, year, letter, pg)
|
||||
elif tid == '特别节目':
|
||||
id = urllib.parse.quote(tid)
|
||||
if 'datapd-channel' in extend.keys():
|
||||
channel = urllib.parse.quote(extend['datapd-channel'])
|
||||
if 'datafl-sc' in extend.keys():
|
||||
datafl = urllib.parse.quote(extend['datafl-sc'])
|
||||
if 'dataszm-letter' in extend.keys():
|
||||
letter = extend['dataszm-letter']
|
||||
url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955953877151&channel={0}&sc={1}&fc={2}&bigday=&letter={3}&p={4}&n=24&serviceId=tvcctv&topv=1&t=json'.format(
|
||||
channel, datafl, id, letter, pg)
|
||||
elif tid == '栏目大全':
|
||||
cid = '' # 频道
|
||||
if 'cid' in extend.keys():
|
||||
cid = extend['cid']
|
||||
fc = '' # 分类
|
||||
if 'fc' in extend.keys():
|
||||
fc = extend['fc']
|
||||
fl = '' # 字母
|
||||
if 'fl' in extend.keys():
|
||||
fl = extend['fl']
|
||||
year = extend.get('year') or ''
|
||||
month = extend.get('month') or ''
|
||||
if year:
|
||||
year_prefix = year + month
|
||||
url = 'https://api.cntv.cn/lanmu/columnSearch?&fl={0}&fc={1}&cid={2}&p={3}&n=20&serviceId=tvcctv&t=json&cb=ko'.format(
|
||||
fl, fc, cid, pg)
|
||||
pagecount = 20
|
||||
elif tid == '4K专区':
|
||||
cid = 'CHAL1558416868484111'
|
||||
url = 'https://api.cntv.cn/NewVideo/getLastVideoList4K?serviceId=cctv4k&cid={0}&p={1}&n={2}&t=json&cb=ko'.format(
|
||||
cid, pg, pagecount
|
||||
)
|
||||
elif tid == '频道直播':
|
||||
url = 'https://tv.cctv.com/epg/index.shtml'
|
||||
else:
|
||||
url = 'https://tv.cctv.com/epg/index.shtml'
|
||||
|
||||
videos = []
|
||||
htmlText = self.fetch(url).text
|
||||
if tid == '栏目大全':
|
||||
index = htmlText.rfind(');')
|
||||
if index > -1:
|
||||
htmlText = htmlText[3:index]
|
||||
videos = self.get_list1(html=htmlText, tid=tid, year_prefix=year_prefix)
|
||||
elif tid == '4K专区':
|
||||
index = htmlText.rfind(');')
|
||||
if index > -1:
|
||||
htmlText = htmlText[3:index]
|
||||
videos = self.get_list_4k(html=htmlText, tid=tid)
|
||||
elif tid == '频道直播':
|
||||
html = self.html(htmlText)
|
||||
lis = html.xpath('//*[@id="jiemudan01"]//div[contains(@class,"channel_con")]//ul/li')
|
||||
for li in lis:
|
||||
vid = ''.join(li.xpath('./img/@title'))
|
||||
pic = ''.join(li.xpath('./img/@src'))
|
||||
pic = self.urljoin('https://tv.cctv.com/epg/index.shtml', pic)
|
||||
videos.append({
|
||||
'vod_id': '||'.join([tid, vid, f'https://tv.cctv.com/live/{vid}/', pic]),
|
||||
'vod_name': vid,
|
||||
'vod_pic': pic,
|
||||
'vod_mark': '',
|
||||
})
|
||||
|
||||
else:
|
||||
videos = self.get_list(html=htmlText, tid=tid)
|
||||
# print(videos)
|
||||
|
||||
result['list'] = videos
|
||||
result['page'] = pg
|
||||
result['pagecount'] = 9999 if len(videos) >= pagecount else pg
|
||||
result['limit'] = 90
|
||||
result['total'] = 999999
|
||||
return result
|
||||
|
||||
def detailContent(self, array):
|
||||
result = {}
|
||||
year_prefix = ''
|
||||
did = array[0]
|
||||
if '$$$' in did:
|
||||
year_prefix = did.split('$$$')[0]
|
||||
did = did.split('$$$')[1]
|
||||
aid = did.split('||')
|
||||
tid = aid[0]
|
||||
title = aid[1]
|
||||
lastVideo = aid[2]
|
||||
logo = aid[3]
|
||||
if tid == '频道直播':
|
||||
vod = {
|
||||
"vod_id": did,
|
||||
"vod_name": title.replace(' ', ''),
|
||||
"vod_pic": logo,
|
||||
"vod_content": f'频道{title}正在直播中',
|
||||
"vod_play_from": '道长在线直播',
|
||||
"vod_play_url": f'在线观看${title}||{lastVideo}',
|
||||
}
|
||||
result = {'list': [vod]}
|
||||
return result
|
||||
|
||||
id = aid[4]
|
||||
|
||||
vod_year = aid[5]
|
||||
actors = aid[6] if len(aid) > 6 else ''
|
||||
brief = aid[7] if len(aid) > 7 else '' # get请求最长255,这个描述会有可能直接被干没了。
|
||||
fromId = 'CCTV'
|
||||
if tid == "栏目大全":
|
||||
lastUrl = 'https://api.cntv.cn/video/videoinfoByGuid?guid={0}&serviceId=tvcctv'.format(id)
|
||||
# htmlTxt = self.webReadFile(urlStr=lastUrl, header=self.header)
|
||||
htmlTxt = self.fetch(lastUrl).text
|
||||
topicId = json.loads(htmlTxt)['ctid']
|
||||
url = 'https://api.cntv.cn/NewVideo/getVideoListByColumn'
|
||||
# params = {
|
||||
# 'p': '1',
|
||||
# 'n': '100',
|
||||
# 't': 'json',
|
||||
# 'mode': '0',
|
||||
# 'sort': 'desc',
|
||||
# 'serviceId': 'tvcctv',
|
||||
# 'd': year_prefix,
|
||||
# 'id': topicId
|
||||
# }
|
||||
# htmlTxt = self.fetch(url,data=params).text
|
||||
|
||||
Url = "{0}?id={1}&d=&p=1&n=100&sort=desc&mode=0&serviceId=tvcctv&t=json&d={2}".format(
|
||||
url, topicId, year_prefix)
|
||||
|
||||
|
||||
elif tid == "4K专区":
|
||||
Url = 'https://api.cntv.cn/NewVideo/getVideoListByAlbumIdNew?id={0}&serviceId=cctv4k&p=1&n=100&mode=0&pub=1'.format(
|
||||
id)
|
||||
print(Url)
|
||||
else:
|
||||
Url = 'https://api.cntv.cn/NewVideo/getVideoListByAlbumIdNew?id={0}&serviceId=tvcctv&p=1&n=100&mode=0&pub=1'.format(
|
||||
id)
|
||||
jRoot = ''
|
||||
videoList = []
|
||||
try:
|
||||
if tid == "搜索":
|
||||
fromId = '中央台'
|
||||
videoList = [title + "$" + lastVideo]
|
||||
else:
|
||||
# htmlTxt = self.webReadFile(urlStr=Url, header=self.header)
|
||||
htmlTxt = self.fetch(Url).text
|
||||
jRoot = json.loads(htmlTxt)
|
||||
data = jRoot['data']
|
||||
jsonList = data['list']
|
||||
videoList = self.get_EpisodesList(jsonList=jsonList)
|
||||
if len(videoList) < 1:
|
||||
# htmlTxt = self.webReadFile(urlStr=lastVideo, header=self.header)
|
||||
htmlTxt = self.fetch(lastVideo).text
|
||||
if tid == "电视剧" or tid == "纪录片" or tid == "4K专区":
|
||||
patternTxt = r"'title':\s*'(?P<title>.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'url':\s*'(?P<url>.+?)'"
|
||||
elif tid == "特别节目":
|
||||
patternTxt = r'class="tp1"><a\s*href="(?P<url>https://.+?)"\s*target="_blank"\s*title="(?P<title>.+?)"></a></div>'
|
||||
elif tid == "动画片":
|
||||
patternTxt = r"'title':\s*'(?P<title>.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'url':\s*'(?P<url>.+?)'"
|
||||
elif tid == "栏目大全":
|
||||
patternTxt = r'href="(?P<url>.+?)" target="_blank" alt="(?P<title>.+?)" title=".+?">'
|
||||
videoList = self.get_EpisodesList_re(htmlTxt=htmlTxt, patternTxt=patternTxt)
|
||||
fromId = '央视'
|
||||
except:
|
||||
pass
|
||||
if len(videoList) == 0:
|
||||
return {}
|
||||
vod = {
|
||||
"vod_id": did,
|
||||
"vod_name": title.replace(' ', ''),
|
||||
"vod_pic": logo,
|
||||
"type_name": tid,
|
||||
"vod_year": vod_year,
|
||||
"vod_area": "",
|
||||
"vod_remarks": '',
|
||||
"vod_actor": actors,
|
||||
"vod_director": '',
|
||||
"vod_content": brief
|
||||
}
|
||||
vod['vod_play_from'] = fromId
|
||||
vod['vod_play_url'] = "#".join(videoList)
|
||||
result = {
|
||||
'list': [
|
||||
vod
|
||||
]
|
||||
}
|
||||
return result
|
||||
|
||||
def get_lineList(self, Txt, mark, after):
|
||||
circuit = []
|
||||
origin = Txt.find(mark)
|
||||
while origin > 8:
|
||||
end = Txt.find(after, origin)
|
||||
circuit.append(Txt[origin:end])
|
||||
origin = Txt.find(mark, end)
|
||||
return circuit
|
||||
|
||||
def get_RegexGetTextLine(self, Text, RegexText, Index):
|
||||
returnTxt = []
|
||||
pattern = re.compile(RegexText, re.M | re.S)
|
||||
ListRe = pattern.findall(Text)
|
||||
if len(ListRe) < 1:
|
||||
return returnTxt
|
||||
for value in ListRe:
|
||||
returnTxt.append(value)
|
||||
return returnTxt
|
||||
|
||||
def searchContent(self, key, quick, pg=1):
|
||||
key = urllib.parse.quote(key)
|
||||
Url = 'https://search.cctv.com/ifsearch.php?page=1&qtext={0}&sort=relevance&pageSize=20&type=video&vtime=-1&datepid=1&channel=&pageflag=0&qtext_str={0}'.format(
|
||||
key)
|
||||
# htmlTxt = self.webReadFile(urlStr=Url, header=self.header)
|
||||
htmlTxt = self.fetch(Url).text
|
||||
videos = self.get_list_search(html=htmlTxt, tid='搜索')
|
||||
result = {
|
||||
'list': videos
|
||||
}
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
result = {}
|
||||
url = ''
|
||||
parse = 0
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
|
||||
}
|
||||
if flag == 'CCTV':
|
||||
url = self.get_m3u8(urlTxt=id)
|
||||
elif flag == '道长在线直播':
|
||||
# _url = id
|
||||
title = id.split('||')[0] # 获取标题
|
||||
_url = f'https://vdn.live.cntv.cn/api2/liveHtml5.do?channel=pc://cctv_p2p_hd{title}&channel_id={title}'
|
||||
htmlTxt = self.fetch(_url).text
|
||||
# print(htmlTxt)
|
||||
vdata = self.regStr(htmlTxt, "var .*?=.*?'(.*?)';")
|
||||
vdata = self.str2json(vdata)
|
||||
print(vdata)
|
||||
url = vdata['hls_url']['hls1']
|
||||
print(url)
|
||||
url = self.fixm3u8_url(url)
|
||||
else:
|
||||
try:
|
||||
# htmlTxt = self.webReadFile(urlStr=id, header=self.header)
|
||||
htmlTxt = self.fetch(id).text
|
||||
guid = self.get_RegexGetText(Text=htmlTxt, RegexText=r'var\sguid\s*=\s*"(.+?)";', Index=1)
|
||||
url = self.get_m3u8(urlTxt=guid)
|
||||
except:
|
||||
url = id
|
||||
parse = 1
|
||||
if url.find('https:') < 0:
|
||||
url = id
|
||||
parse = 1
|
||||
result["parse"] = parse # 1=嗅探,0=播放
|
||||
result["playUrl"] = ''
|
||||
result["url"] = url
|
||||
result["header"] = headers
|
||||
return result
|
||||
|
||||
# 分类抓取地址:
|
||||
# 栏目大全:https://tv.cctv.com/lm/index.shtml?spm=C28340.Pu9TN9YUsfNZ.E2PQtIunpEaz.24
|
||||
# 电视剧:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=dsj&datafl=&datadq=&fc=%E7%94%B5%E8%A7%86%E5%89%A7&datanf=&dataszm=
|
||||
# 动画片:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=dhp&datafl=&datadq=&fc=%E5%8A%A8%E7%94%BB%E7%89%87&dataszm=
|
||||
# 记录片:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=jlp&datapd=&datafl=&fc=%E7%BA%AA%E5%BD%95%E7%89%87&datanf=&dataszm=
|
||||
# 特别节目:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=tbjm&datapd=&datafl=&fc=%E7%89%B9%E5%88%AB%E8%8A%82%E7%9B%AE&datajr=&dataszm=
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {
|
||||
"电视剧": [
|
||||
{"key": "datafl-sc", "name": "类型",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "谍战", "v": "谍战"}, {"n": "悬疑", "v": "悬疑"},
|
||||
{"n": "刑侦", "v": "刑侦"}, {"n": "历史", "v": "历史"}, {"n": "古装", "v": "古装"},
|
||||
{"n": "武侠", "v": "武侠"}, {"n": "军旅", "v": "军旅"}, {"n": "战争", "v": "战争"},
|
||||
{"n": "喜剧", "v": "喜剧"}, {"n": "青春", "v": "青春"}, {"n": "言情", "v": "言情"},
|
||||
{"n": "偶像", "v": "偶像"}, {"n": "家庭", "v": "家庭"}, {"n": "年代", "v": "年代"},
|
||||
{"n": "革命", "v": "革命"}, {"n": "农村", "v": "农村"}, {"n": "都市", "v": "都市"},
|
||||
{"n": "其他", "v": "其他"}]},
|
||||
{"key": "datadq-area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "香港"},
|
||||
{"n": "美国", "v": "美国"}, {"n": "欧洲", "v": "欧洲"}, {"n": "泰国", "v": "泰国"}]},
|
||||
{"key": "datanf-year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"},
|
||||
{"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"},
|
||||
{"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"},
|
||||
{"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"},
|
||||
{"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"},
|
||||
{"n": "2006", "v": "2006"}, {"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"},
|
||||
{"n": "2003", "v": "2003"}, {"n": "2002", "v": "2002"}, {"n": "2001", "v": "2001"},
|
||||
{"n": "2000", "v": "2000"}, {"n": "1999", "v": "1999"}, {"n": "1998", "v": "1998"},
|
||||
{"n": "1997", "v": "1997"}]},
|
||||
{"key": "dataszm-letter", "name": "字母",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"},
|
||||
{"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"},
|
||||
{"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"},
|
||||
{"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"},
|
||||
{"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"},
|
||||
{"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"},
|
||||
{"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]}
|
||||
],
|
||||
"动画片": [
|
||||
{"key": "datafl-sc", "name": "类型",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "亲子", "v": "亲子"}, {"n": "搞笑", "v": "搞笑"},
|
||||
{"n": "冒险", "v": "冒险"}, {"n": "动作", "v": "动作"}, {"n": "宠物", "v": "宠物"},
|
||||
{"n": "体育", "v": "体育"}, {"n": "益智", "v": "益智"}, {"n": "历史", "v": "历史"},
|
||||
{"n": "教育", "v": "教育"}, {"n": "校园", "v": "校园"}, {"n": "言情", "v": "言情"},
|
||||
{"n": "武侠", "v": "武侠"}, {"n": "经典", "v": "经典"}, {"n": "未来", "v": "未来"},
|
||||
{"n": "古代", "v": "古代"}, {"n": "神话", "v": "神话"}, {"n": "真人", "v": "真人"},
|
||||
{"n": "励志", "v": "励志"}, {"n": "热血", "v": "热血"}, {"n": "奇幻", "v": "奇幻"},
|
||||
{"n": "童话", "v": "童话"}, {"n": "剧情", "v": "剧情"}, {"n": "夺宝", "v": "夺宝"},
|
||||
{"n": "其他", "v": "其他"}]},
|
||||
{"key": "datadq-area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "美国", "v": "美国"},
|
||||
{"n": "欧洲", "v": "欧洲"}]},
|
||||
{"key": "dataszm-letter", "name": "字母",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"},
|
||||
{"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"},
|
||||
{"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"},
|
||||
{"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"},
|
||||
{"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"},
|
||||
{"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"},
|
||||
{"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]}
|
||||
],
|
||||
"纪录片": [
|
||||
{"key": "datafl-sc", "name": "类型",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "人文历史", "v": "人文历史"}, {"n": "人物", "v": "人物"},
|
||||
{"n": "军事", "v": "军事"}, {"n": "探索", "v": "探索"}, {"n": "社会", "v": "社会"},
|
||||
{"n": "时政", "v": "时政"}, {"n": "经济", "v": "经济"}, {"n": "科技", "v": "科技"}]},
|
||||
{"key": "datanf-year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"},
|
||||
{"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"},
|
||||
{"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"},
|
||||
{"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"},
|
||||
{"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}]},
|
||||
{"key": "dataszm-letter", "name": "字母",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"},
|
||||
{"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"},
|
||||
{"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"},
|
||||
{"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"},
|
||||
{"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"},
|
||||
{"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"},
|
||||
{"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]}
|
||||
],
|
||||
"特别节目": [
|
||||
{"key": "datafl-sc", "name": "类型",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "全部", "v": "全部"}, {"n": "新闻", "v": "新闻"},
|
||||
{"n": "经济", "v": "经济"}, {"n": "综艺", "v": "综艺"}, {"n": "体育", "v": "体育"},
|
||||
{"n": "军事", "v": "军事"}, {"n": "影视", "v": "影视"}, {"n": "科教", "v": "科教"},
|
||||
{"n": "戏曲", "v": "戏曲"}, {"n": "青少", "v": "青少"}, {"n": "音乐", "v": "音乐"},
|
||||
{"n": "社会", "v": "社会"}, {"n": "公益", "v": "公益"}, {"n": "其他", "v": "其他"}]},
|
||||
{"key": "dataszm-letter", "name": "字母",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"},
|
||||
{"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"},
|
||||
{"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"},
|
||||
{"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"},
|
||||
{"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"},
|
||||
{"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"},
|
||||
{"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]}
|
||||
],
|
||||
"栏目大全": [{"key": "cid", "name": "频道",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "CCTV-1综合", "v": "EPGC1386744804340101"},
|
||||
{"n": "CCTV-2财经", "v": "EPGC1386744804340102"},
|
||||
{"n": "CCTV-3综艺", "v": "EPGC1386744804340103"},
|
||||
{"n": "CCTV-4中文国际", "v": "EPGC1386744804340104"},
|
||||
{"n": "CCTV-5体育", "v": "EPGC1386744804340107"},
|
||||
{"n": "CCTV-6电影", "v": "EPGC1386744804340108"},
|
||||
{"n": "CCTV-7国防军事", "v": "EPGC1386744804340109"},
|
||||
{"n": "CCTV-8电视剧", "v": "EPGC1386744804340110"},
|
||||
{"n": "CCTV-9纪录", "v": "EPGC1386744804340112"},
|
||||
{"n": "CCTV-10科教", "v": "EPGC1386744804340113"},
|
||||
{"n": "CCTV-11戏曲", "v": "EPGC1386744804340114"},
|
||||
{"n": "CCTV-12社会与法", "v": "EPGC1386744804340115"},
|
||||
{"n": "CCTV-13新闻", "v": "EPGC1386744804340116"},
|
||||
{"n": "CCTV-14少儿", "v": "EPGC1386744804340117"},
|
||||
{"n": "CCTV-15音乐", "v": "EPGC1386744804340118"},
|
||||
{"n": "CCTV-16奥林匹克", "v": "EPGC1634630207058998"},
|
||||
{"n": "CCTV-17农业农村", "v": "EPGC1563932742616872"},
|
||||
{"n": "CCTV-5+体育赛事", "v": "EPGC1468294755566101"}]},
|
||||
{"key": "fc", "name": "分类",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "新闻", "v": "新闻"}, {"n": "体育", "v": "体育"},
|
||||
{"n": "综艺", "v": "综艺"}, {"n": "健康", "v": "健康"}, {"n": "生活", "v": "生活"},
|
||||
{"n": "科教", "v": "科教"}, {"n": "经济", "v": "经济"}, {"n": "农业", "v": "农业"},
|
||||
{"n": "法治", "v": "法治"}, {"n": "军事", "v": "军事"}, {"n": "少儿", "v": "少儿"},
|
||||
{"n": "动画", "v": "动画"}, {"n": "纪实", "v": "纪实"}, {"n": "戏曲", "v": "戏曲"},
|
||||
{"n": "音乐", "v": "音乐"}, {"n": "影视", "v": "影视"}]},
|
||||
{"key": "fl", "name": "字母",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "B", "v": "B"},
|
||||
{"n": "C", "v": "C"}, {"n": "D", "v": "D"}, {"n": "E", "v": "E"},
|
||||
{"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"},
|
||||
{"n": "I", "v": "I"}, {"n": "J", "v": "J"}, {"n": "K", "v": "K"},
|
||||
{"n": "L", "v": "L"}, {"n": "M", "v": "M"}, {"n": "N", "v": "N"},
|
||||
{"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"},
|
||||
{"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"},
|
||||
{"n": "U", "v": "U"}, {"n": "V", "v": "V"}, {"n": "W", "v": "W"},
|
||||
{"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, {"n": "Z", "v": "Z"}]},
|
||||
]
|
||||
}
|
||||
}
|
||||
header = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
|
||||
"Host": "tv.cctv.com",
|
||||
"Referer": "https://tv.cctv.com/"
|
||||
}
|
||||
|
||||
def localProxy(self, params):
|
||||
return [200, "video/MP2T", ""]
|
||||
|
||||
# -----------------------------------------------自定义函数-----------------------------------------------
|
||||
# 访问网页
|
||||
def webReadFile(self, urlStr, header):
|
||||
html = ''
|
||||
req = urllib.request.Request(url=urlStr) # ,headers=header
|
||||
with urllib.request.urlopen(req) as response:
|
||||
html = response.read().decode('utf-8')
|
||||
return html
|
||||
|
||||
# 判断网络地址是否存在
|
||||
def TestWebPage(self, urlStr, header):
|
||||
html = ''
|
||||
req = urllib.request.Request(url=urlStr, method='HEAD') # ,headers=header
|
||||
with urllib.request.urlopen(req) as response:
|
||||
html = response.getcode()
|
||||
return html
|
||||
|
||||
# 正则取文本
|
||||
def get_RegexGetText(self, Text, RegexText, Index):
|
||||
returnTxt = ""
|
||||
Regex = re.search(RegexText, Text, re.M | re.S)
|
||||
if Regex is None:
|
||||
returnTxt = ""
|
||||
else:
|
||||
returnTxt = Regex.group(Index)
|
||||
return returnTxt
|
||||
|
||||
# 取集数
|
||||
def get_EpisodesList(self, jsonList):
|
||||
videos = []
|
||||
for vod in jsonList:
|
||||
url = vod['guid']
|
||||
title = vod['title']
|
||||
if len(url) == 0:
|
||||
continue
|
||||
videos.append(title + "$" + url)
|
||||
return videos
|
||||
|
||||
# 取集数
|
||||
def get_EpisodesList_re(self, htmlTxt, patternTxt):
|
||||
ListRe = re.finditer(patternTxt, htmlTxt, re.M | re.S)
|
||||
videos = []
|
||||
for vod in ListRe:
|
||||
url = vod.group('url')
|
||||
title = vod.group('title')
|
||||
if len(url) == 0:
|
||||
continue
|
||||
videos.append(title + "$" + url)
|
||||
return videos
|
||||
|
||||
# 取剧集区
|
||||
def get_lineList(self, Txt, mark, after):
|
||||
circuit = []
|
||||
origin = Txt.find(mark)
|
||||
while origin > 8:
|
||||
end = Txt.find(after, origin)
|
||||
circuit.append(Txt[origin:end])
|
||||
origin = Txt.find(mark, end)
|
||||
return circuit
|
||||
|
||||
# 正则取文本,返回数组
|
||||
def get_RegexGetTextLine(self, Text, RegexText, Index):
|
||||
returnTxt = []
|
||||
pattern = re.compile(RegexText, re.M | re.S)
|
||||
ListRe = pattern.findall(Text)
|
||||
if len(ListRe) < 1:
|
||||
return returnTxt
|
||||
for value in ListRe:
|
||||
returnTxt.append(value)
|
||||
return returnTxt
|
||||
|
||||
# 删除html标签
|
||||
def removeHtml(self, txt):
|
||||
soup = re.compile(r'<[^>]+>', re.S)
|
||||
txt = soup.sub('', txt)
|
||||
return txt.replace(" ", " ")
|
||||
|
||||
def hookM3u8(self, url):
|
||||
"""
|
||||
https://www.52pojie.cn/thread-1932358-1-1.html
|
||||
JavaScript:$.ajaxSettings.async = false; var s = ""; let a = $.get(vodh5player.playerList[0].ads.contentSrc); for (var m = 0; m < a.responseText.match(/asp.*?m3u8/g).length; m++) { s = s + "https://hls.cntv.myalicdn.com//asp" + a.responseText.match(/asp.*?m3u8/g)[m].slice(7) + "\n\n" }; var blob = new Blob([s], { type: "text/plain" }); var url = URL.createObjectURL(blob); window.open(url);
|
||||
@param url:
|
||||
@return:
|
||||
"""
|
||||
url = url or ''
|
||||
hook1 = lambda x: x.replace('asp/', 'asp//', 1)
|
||||
hook2 = lambda x: x.replace('hls/', 'hls//', 1)
|
||||
hook3 = lambda x: x.replace('https://newcntv.qcloudcdn.com', 'https://hls.cntv.myalicdn.com/', 1)
|
||||
hooks = [hook1, hook2, hook3]
|
||||
hook = random.choice(hooks)
|
||||
return hook(url)
|
||||
|
||||
# 取m3u8
|
||||
def get_m3u8(self, urlTxt):
|
||||
"""
|
||||
https://blog.csdn.net/panwang666/article/details/135347859
|
||||
|
||||
JavaScript:jQuery.getJSON("https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid="+guid,function(result){document.writeln(result.hls_url.link(result.hls_url));});
|
||||
|
||||
https://newcntv.qcloudcdn.com/asp/hls/main/0303000a/3/default/3628bb15af644f588dc91ec68425b9ac/main.m3u8?maxbr=2048
|
||||
@param urlTxt:
|
||||
@return:
|
||||
"""
|
||||
url = "https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid={0}".format(urlTxt)
|
||||
# htmlTxt = self.webReadFile(urlStr=url, header=self.header)
|
||||
htmlTxt = self.fetch(url).text
|
||||
jo = json.loads(htmlTxt)
|
||||
link = jo['hls_url'].strip()
|
||||
# print('hls_url:',link)
|
||||
# 获取域名前缀
|
||||
urlPrefix = self.get_RegexGetText(Text=link, RegexText='(http[s]?://[a-zA-z0-9.]+)/', Index=1)
|
||||
# 域名前缀指定替换,然后可以获取到更高质量的视频列表
|
||||
# /asp/h5e/hls/2000/0303000a/3/default/3628bb15af644f588dc91ec68425b9ac/2000.m3u8
|
||||
new_link = link.replace(f'{urlPrefix}/asp/hls/', 'https://dh5.cntv.qcloudcdn.com/asp/h5e/hls/').split('?')[0]
|
||||
# print('new_link:',new_link)
|
||||
html = self.webReadFile(urlStr=new_link, header=self.header)
|
||||
content = html.strip()
|
||||
arr = content.split('\n')
|
||||
subUrl = arr[-1].split('/')
|
||||
# hdUrl = urlPrefix + arr[-1]
|
||||
|
||||
# subUrl[3] = '2000'
|
||||
# subUrl[-1] = '2000.m3u8'
|
||||
# hdUrl = urlPrefix + '/'.join(subUrl)
|
||||
maxVideo = subUrl[-1].replace('.m3u8', '')
|
||||
hdUrl = link.replace('main', maxVideo)
|
||||
hdUrl = hdUrl.replace(urlPrefix, 'https://newcntv.qcloudcdn.com')
|
||||
hdRsp = self.TestWebPage(urlStr=hdUrl, header=self.header)
|
||||
if hdRsp == 200:
|
||||
url = hdUrl.split('?')[0]
|
||||
url = self.hookM3u8(url)
|
||||
self.log(f'视频链接: {url}')
|
||||
else:
|
||||
url = ''
|
||||
return url
|
||||
|
||||
def fixm3u8_url(self, url):
|
||||
# 获取域名前缀
|
||||
urlPrefix = self.get_RegexGetText(Text=url, RegexText='(http[s]?://[a-zA-z0-9.]+)/', Index=1)
|
||||
# 域名前缀指定替换,然后可以获取到更高质量的视频列表
|
||||
new_link = url.split('?')[0]
|
||||
# print(new_link)
|
||||
html = self.webReadFile(urlStr=new_link, header=self.header)
|
||||
content = html.strip()
|
||||
# print(content)
|
||||
arr = content.split('\n')
|
||||
subUrl = arr[3] if 'EXT-X-VERSION' in content else arr[2]
|
||||
hdUrl = self.urljoin(new_link, subUrl).split('?')[0]
|
||||
# hdUrl = hdUrl.replace(urlPrefix, 'https://newcntv.qcloudcdn.com')
|
||||
hdRsp = self.TestWebPage(urlStr=hdUrl, header=self.header)
|
||||
if hdRsp == 200:
|
||||
url = hdUrl
|
||||
self.log(f'视频链接: {url}')
|
||||
else:
|
||||
url = ''
|
||||
return url
|
||||
|
||||
# 搜索
|
||||
def get_list_search(self, html, tid):
|
||||
jRoot = json.loads(html)
|
||||
jsonList = jRoot['list']
|
||||
videos = []
|
||||
for vod in jsonList:
|
||||
url = vod['urllink']
|
||||
title = self.removeHtml(txt=vod['title'])
|
||||
img = vod['imglink']
|
||||
id = vod['id']
|
||||
brief = vod['channel']
|
||||
year = vod['uploadtime']
|
||||
if len(url) == 0:
|
||||
continue
|
||||
guids = [tid, title, url, img, id, year, '', brief]
|
||||
guid = "||".join(guids)
|
||||
videos.append({
|
||||
"vod_id": guid,
|
||||
"vod_name": title,
|
||||
"vod_pic": img,
|
||||
"vod_remarks": year
|
||||
})
|
||||
return videos
|
||||
|
||||
def get_list1(self, html, tid, year_prefix=None):
|
||||
jRoot = json.loads(html)
|
||||
videos = []
|
||||
data = jRoot['response']
|
||||
if data is None:
|
||||
return []
|
||||
jsonList = data['docs']
|
||||
for vod in jsonList:
|
||||
id = vod['lastVIDE']['videoSharedCode']
|
||||
desc = vod['lastVIDE']['videoTitle']
|
||||
title = vod['column_name']
|
||||
url = vod['column_website']
|
||||
img = vod['column_logo']
|
||||
year = vod['column_playdate']
|
||||
brief = vod['column_brief']
|
||||
actors = ''
|
||||
if len(url) == 0:
|
||||
continue
|
||||
guids = [tid, title, url, img, id, year, actors, brief]
|
||||
guid = "||".join(guids)
|
||||
# print(vod_id)
|
||||
videos.append({
|
||||
"vod_id": year_prefix + '$$$' + guid if year_prefix else guid,
|
||||
"vod_name": title,
|
||||
"vod_pic": img,
|
||||
"vod_remarks": desc.split('》')[1].strip() if '》' in desc else desc.strip()
|
||||
})
|
||||
# print(videos)
|
||||
return videos
|
||||
|
||||
# 分类取结果
|
||||
def get_list(self, html, tid):
|
||||
jRoot = json.loads(html)
|
||||
videos = []
|
||||
data = jRoot['data']
|
||||
if data is None:
|
||||
return []
|
||||
jsonList = data['list']
|
||||
for vod in jsonList:
|
||||
url = vod['url']
|
||||
title = vod['title']
|
||||
img = vod['image']
|
||||
id = vod['id']
|
||||
try:
|
||||
brief = vod['brief']
|
||||
except:
|
||||
brief = ''
|
||||
try:
|
||||
year = vod['year']
|
||||
except:
|
||||
year = ''
|
||||
try:
|
||||
actors = vod['actors']
|
||||
except:
|
||||
actors = ''
|
||||
if len(url) == 0:
|
||||
continue
|
||||
guids = [tid, title, url, img, id, year, actors, brief]
|
||||
guid = "||".join(guids)
|
||||
# print(vod_id)
|
||||
videos.append({
|
||||
"vod_id": guid,
|
||||
"vod_name": title,
|
||||
"vod_pic": img,
|
||||
"vod_remarks": ''
|
||||
})
|
||||
return videos
|
||||
|
||||
# 4k分类取结果
|
||||
def get_list_4k(self, html, tid):
|
||||
jRoot = json.loads(html)
|
||||
videos = []
|
||||
data = jRoot['data']
|
||||
if data is None:
|
||||
return []
|
||||
jsonList = data['list']
|
||||
for vod in jsonList:
|
||||
vod_remarks = vod['title']
|
||||
id = vod['id']
|
||||
vod = vod['last_video']
|
||||
img = vod['image']
|
||||
url = vod['url']
|
||||
title = vod['title']
|
||||
brief = vod.get('brief') or ''
|
||||
year = vod.get('year') or ''
|
||||
actors = vod.get('actors') or ''
|
||||
if len(url) == 0:
|
||||
continue
|
||||
guids = [tid, title, url, img, id, year, actors, brief]
|
||||
guid = "||".join(guids)
|
||||
# print(vod_id)
|
||||
videos.append({
|
||||
"vod_id": guid,
|
||||
"vod_name": title,
|
||||
"vod_pic": img,
|
||||
"vod_remarks": vod_remarks
|
||||
})
|
||||
return videos
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from t4.core.loader import t4_spider_init
|
||||
|
||||
spider = Spider()
|
||||
t4_spider_init(spider)
|
||||
# print(spider.homeContent(True))
|
||||
# print(spider.homeVideoContent())
|
||||
# spider.init_api_ext_file()
|
||||
# url = 'https://api.cntv.cn/lanmu/columnSearch?&fl=&fc=%E6%96%B0%E9%97%BB&cid=&p=1&n=20&serviceId=tvcctv&t=jsonp&cb=Callback'
|
||||
# url = 'https://api.cntv.cn/lanmu/columnSearch?&fl=&fc=&cid=&p=1&n=20&serviceId=tvcctv&t=json&cb=ko'
|
||||
# r = spider.fetch(url)
|
||||
# print(r.text)
|
||||
# home_content = spider.homeContent(None)
|
||||
# print(home_content)
|
||||
cate_content = spider.categoryContent('栏目大全', 1, {'cid': 'n'}, {})
|
||||
# cate_content = spider.categoryContent('频道直播', 1, None, None)
|
||||
print(cate_content)
|
||||
vid = cate_content['list'][0]['vod_id']
|
||||
print(vid)
|
||||
detail_content = spider.detailContent([vid])
|
||||
print(detail_content)
|
||||
# #
|
||||
vod_play_from = detail_content['list'][0]['vod_play_from']
|
||||
vod_play_url = detail_content['list'][0]['vod_play_url']
|
||||
print(vod_play_from, vod_play_url)
|
||||
_url = vod_play_url.split('#')[0].split('$')[1]
|
||||
print(_url)
|
||||
print('vod_play_from:', vod_play_from, ' vod_play_url:', _url)
|
||||
play = spider.playerContent(vod_play_from, _url, None)
|
||||
print(play)
|
||||
|
||||
# play = spider.playerContent('道长在线直播', 'cctv1||https://tv.cctv.com/live/cctv1/', None)
|
||||
# print(play)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"分类1": [{"key": "letter", "name": "首字母", "value": [{"n": "A", "v": "A"}, {"n": "B", "v": "B"}]}], "分类2": [{"key": "letter", "name": "首字母", "value": [{"n": "A", "v": "A"}, {"n": "B", "v": "B"}]}, {"key": "year", "name": "年份", "value": [{"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}]}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"特别节目": [{"key": "datapd-channel", "name": "频道", "value": [{"n": "全部", "v": ""}, {"n": "CCTV-1 综合", "v": "CCTV-1综合,CCTV-1高清,CCTV-1综合高清"}, {"n": "CCTV-2 财经", "v": "CCTV-2财经,CCTV-2高清,CCTV-2财经高清"}, {"n": "CCTV-3 综艺", "v": "CCTV-3综艺,CCTV-3高清,CCTV-3综艺高清"}, {"n": "CCTV-4 中文国际", "v": "CCTV-4中文国际,CCTV-4高清,CCTV-4中文国际(亚)高清"}, {"n": "CCTV-5 体育", "v": "CCTV-5体育,CCTV-5高清,CCTV-5体育高清"}, {"n": "CCTV-6 电影", "v": "CCTV-6电影,CCTV-6高清,CCTV-6电影高清"}, {"n": "CCTV-7 国防军事", "v": "CCTV-7军事农业,CCTV-7高清,CCTV-7军事农业高清,CCTV-7国防军事高清"}, {"n": "CCTV-8 电视剧", "v": "CCTV-8电视剧,CCTV-8高清,CCTV-8电视剧高清"}, {"n": "CCTV-9 纪录", "v": "CCTV-9纪录,CCTV-9高清,CCTV-9纪录高清"}, {"n": "CCTV-10 科教", "v": "CCTV-10科教,CCTV-10高清,CCTV-10科教高清"}, {"n": "CCTV-11 戏曲", "v": "CCTV-11戏曲,CCTV-11高清,CCTV-11戏曲高清"}, {"n": "CCTV-12 社会与法", "v": "CCTV-12社会与法,CCTV-12高清,CCTV-12社会与法高清"}, {"n": "CCTV-13 新闻", "v": "CCTV-13新闻,CCTV-13高清,CCTV-13新闻高清"}, {"n": "CCTV-14 少儿", "v": "CCTV-14少儿,CCTV-14高清,CCTV-14少儿高清"}, {"n": "CCTV-15 音乐", "v": "CCTV-15音乐,CCTV-15高清,CCTV-15音乐高清"}, {"n": "CCTV-17 农业农村", "v": "CCTV-17农业农村高清"}]}, {"key": "datafl-sc", "name": "类型", "value": [{"n": "全部", "v": ""}, {"n": "新闻", "v": "新闻"}, {"n": "经济", "v": "经济"}, {"n": "综艺", "v": "综艺"}, {"n": "体育", "v": "体育"}, {"n": "军事", "v": "军事"}, {"n": "影视", "v": "影视"}, {"n": "科教", "v": "科教"}, {"n": "戏曲", "v": "戏曲"}, {"n": "青少", "v": "青少"}, {"n": "音乐", "v": "音乐"}, {"n": "社会", "v": "社会"}, {"n": "文化", "v": "文化"}, {"n": "公益", "v": "公益"}, {"n": "其他", "v": "其他"}]}, {"key": "dataszm-letter", "name": "首字母", "value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "B", "v": "B"}, {"n": "C", "v": "C"}, {"n": "D", "v": "D"}, {"n": "E", "v": "E"}, {"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"}, {"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"}, {"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, {"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"}, {"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, {"n": "Z", "v": "Z"}]}], "纪录片": [{"key": "datapd-channel", "name": "频道", "value": [{"n": "全部", "v": ""}, {"n": "CCTV-1 综合", "v": "CCTV-1综合,CCTV-1高清,CCTV-1综合高清"}, {"n": "CCTV-2 财经", "v": "CCTV-2财经,CCTV-2高清,CCTV-2财经高清"}, {"n": "CCTV-3 综艺", "v": "CCTV-3综艺,CCTV-3综艺高清"}, {"n": "CCTV-4 中文国际", "v": "CCTV-4中文国际,CCTV-4高清,CCTV-4中文国际(亚)高清"}, {"n": "CCTV-5 体育", "v": "CCTV-5体育,CCTV-5体育高清"}, {"n": "CCTV-6 电影", "v": "CCTV-6电影,CCTV-6电影高清"}, {"n": "CCTV-7 国防军事", "v": "CCTV-7军事农业,CCTV-7军事农业高清,CCTV-7国防军事高清"}, {"n": "CCTV-8 电视剧", "v": "CCTV-8电视剧,CCTV-8电视剧高清"}, {"n": "CCTV-9 纪录", "v": "CCTV-9纪录,CCTV-9高清,CCTV-9纪录高清"}, {"n": "CCTV-10 科教", "v": "CCTV-10科教,CCTV-10高清,CCTV-10科教高清"}, {"n": "CCTV-11 戏曲", "v": "CCTV-11戏曲"}, {"n": "CCTV-12 社会与法", "v": "CCTV-12社会与法,CCTV-12社会与法高清"}, {"n": "CCTV-13 新闻", "v": "CCTV-13新闻"}, {"n": "CCTV-14 少儿", "v": "CCTV-14少儿,CCTV-14少儿高清"}, {"n": "CCTV-15 音乐", "v": "CCTV-15音乐,CCTV-15音乐高清"}, {"n": "CCTV-17 农业农村", "v": "CCTV-17农业农村高清"}]}, {"key": "datafl-sc", "name": "类型", "value": [{"n": "全部", "v": ""}, {"n": "人文历史", "v": "人文历史"}, {"n": "人物", "v": "人物"}, {"n": "军事", "v": "军事"}, {"n": "探索", "v": "探索"}, {"n": "社会", "v": "社会"}, {"n": "自然", "v": "自然"}, {"n": "时政", "v": "时政"}, {"n": "经济", "v": "经济"}, {"n": "科技", "v": "科技"}]}, {"key": "datanf-year", "name": "年份", "value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}]}, {"key": "dataszm-letter", "name": "首字母", "value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "B", "v": "B"}, {"n": "C", "v": "C"}, {"n": "D", "v": "D"}, {"n": "E", "v": "E"}, {"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"}, {"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"}, {"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, {"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"}, {"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, {"n": "Z", "v": "Z"}]}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"movie_bt": [{"key": "cat", "name": "地区", "value": [{"n": "全部", "v": ""}, {"n": "不丹", "v": "/movie_bt_cat/%e4%b8%8d%e4%b8%b9"}, {"n": "东南亚", "v": "/movie_bt_cat/ny"}, {"n": "中国", "v": "/movie_bt_cat/zhonji"}, {"n": "中国台湾", "v": "/movie_bt_cat/zhogngtw"}, {"n": "中国大陆", "v": "/movie_bt_cat/dl"}, {"n": "中国香港", "v": "/movie_bt_cat/zhongguoxg"}, {"n": "丹麦", "v": "/movie_bt_cat/dm"}, {"n": "乌克兰", "v": "/movie_bt_cat/wuklan"}, {"n": "以色列", "v": "/movie_bt_cat/yisl"}, {"n": "伊朗", "v": "/movie_bt_cat/yl"}, {"n": "俄罗斯", "v": "/movie_bt_cat/els"}, {"n": "保加利亚", "v": "/movie_bt_cat/baojialiya"}, {"n": "克罗地亚", "v": "/movie_bt_cat/%e5%85%8b%e7%bd%97%e5%9c%b0%e4%ba%9a"}, {"n": "冰岛", "v": "/movie_bt_cat/bingda"}, {"n": "加拿大", "v": "/movie_bt_cat/jnd"}, {"n": "匈牙利", "v": "/movie_bt_cat/%e5%8c%88%e7%89%99%e5%88%a9"}, {"n": "南斯拉夫", "v": "/movie_bt_cat/nasilafu"}, {"n": "南非", "v": "/movie_bt_cat/nanfei"}, {"n": "卡塔尔", "v": "/movie_bt_cat/kaer"}, {"n": "卢森堡", "v": "/movie_bt_cat/luob"}, {"n": "印度", "v": "/movie_bt_cat/yindu"}, {"n": "印度尼西亚", "v": "/movie_bt_cat/%e5%8d%b0%e5%ba%a6%e5%b0%bc%e8%a5%bf%e4%ba%9a"}, {"n": "台湾", "v": "/movie_bt_cat/taiwan"}, {"n": "哥伦比亚", "v": "/movie_bt_cat/gelunbiya"}, {"n": "土耳其", "v": "/movie_bt_cat/tuerqi"}, {"n": "塞尔维亚", "v": "/movie_bt_cat/saierweiya"}, {"n": "墨西哥", "v": "/movie_bt_cat/moxige"}, {"n": "奥地利", "v": "/movie_bt_cat/aodili"}, {"n": "尼日利亚", "v": "/movie_bt_cat/nirily"}, {"n": "巴西", "v": "/movie_bt_cat/bx"}, {"n": "希腊", "v": "/movie_bt_cat/xl"}, {"n": "德国", "v": "/movie_bt_cat/%e5%be%b7%e5%9b%bd"}, {"n": "意大利", "v": "/movie_bt_cat/ydl"}, {"n": "挪威", "v": "/movie_bt_cat/nw"}, {"n": "捷克", "v": "/movie_bt_cat/jirker"}, {"n": "摩洛哥", "v": "/movie_bt_cat/%e6%91%a9%e6%b4%9b%e5%93%a5"}, {"n": "斯洛伐克", "v": "/movie_bt_cat/siluofake"}, {"n": "新加坡", "v": "/movie_bt_cat/xinjip"}, {"n": "新西兰", "v": "/movie_bt_cat/xinxilan"}, {"n": "日本", "v": "/movie_bt_cat/rb"}, {"n": "日韩", "v": "/movie_bt_cat/rihan"}, {"n": "欧美", "v": "/movie_bt_cat/omei"}, {"n": "比利时", "v": "/movie_bt_cat/bilishi"}, {"n": "法国", "v": "/movie_bt_cat/fg"}, {"n": "波兰", "v": "/movie_bt_cat/bolan"}, {"n": "波多黎各", "v": "/movie_bt_cat/%e6%b3%a2%e5%a4%9a%e9%bb%8e%e5%90%84"}, {"n": "泰国", "v": "/movie_bt_cat/taigyo"}, {"n": "港台", "v": "/movie_bt_cat/gangtai"}, {"n": "澳大利亚", "v": "/movie_bt_cat/adly"}, {"n": "爱尔兰", "v": "/movie_bt_cat/arl"}, {"n": "爱沙尼亚", "v": "/movie_bt_cat/asny"}, {"n": "瑞典", "v": "/movie_bt_cat/%e7%91%9e%e5%85%b8"}, {"n": "瑞士", "v": "/movie_bt_cat/ruishi"}, {"n": "白俄罗斯", "v": "/movie_bt_cat/baierls"}, {"n": "秘鲁", "v": "/movie_bt_cat/%e7%a7%98%e9%b2%81"}, {"n": "突尼斯", "v": "/movie_bt_cat/tunisi"}, {"n": "立陶宛", "v": "/movie_bt_cat/ltwan"}, {"n": "罗马尼亚", "v": "/movie_bt_cat/lmny"}, {"n": "美国", "v": "/movie_bt_cat/mg"}, {"n": "芬兰", "v": "/movie_bt_cat/%e8%8a%ac%e5%85%b0"}, {"n": "英国", "v": "/movie_bt_cat/yg"}, {"n": "荷兰", "v": "/movie_bt_cat/hl"}, {"n": "荷属安的列斯", "v": "/movie_bt_cat/lsadlsi"}, {"n": "菲律宾", "v": "/movie_bt_cat/feilb"}, {"n": "葡萄牙", "v": "/movie_bt_cat/pty"}, {"n": "西德", "v": "/movie_bt_cat/dide"}, {"n": "西班牙", "v": "/movie_bt_cat/xby"}, {"n": "越南", "v": "/movie_bt_cat/yeun"}, {"n": "阿根廷", "v": "/movie_bt_cat/ageiting"}, {"n": "阿联酋", "v": "/movie_bt_cat/alq"}, {"n": "韩国", "v": "/movie_bt_cat/hg"}, {"n": "香港", "v": "/movie_bt_cat/xiangg"}, {"n": "马来西亚", "v": "/movie_bt_cat/malaxy"}, {"n": "马耳他", "v": "/movie_bt_cat/%e9%a9%ac%e8%80%b3%e4%bb%96"}]}, {"key": "year", "name": "年份", "value": [{"n": "全部", "v": ""}, {"n": "1921", "v": "/year/1921"}, {"n": "1925", "v": "/year/1925"}, {"n": "1931", "v": "/year/1931"}, {"n": "1938", "v": "/year/1938"}, {"n": "1949", "v": "/year/1949"}, {"n": "1952", "v": "/year/1952"}, {"n": "1953", "v": "/year/1953"}, {"n": "1954", "v": "/year/1954"}, {"n": "1955", "v": "/year/1955"}, {"n": "1956", "v": "/year/1956"}, {"n": "1957", "v": "/year/1957"}, {"n": "1958", "v": "/year/1958"}, {"n": "1959", "v": "/year/1959"}, {"n": "1960", "v": "/year/1960"}, {"n": "1961", "v": "/year/1961"}, {"n": "1962", "v": "/year/1962"}, {"n": "1963", "v": "/year/1963"}, {"n": "1969", "v": "/year/1969"}, {"n": "1970", "v": "/year/1970"}, {"n": "1972", "v": "/year/1972"}, {"n": "1973", "v": "/year/1973"}, {"n": "1974", "v": "/year/1974"}, {"n": "1975", "v": "/year/1975"}, {"n": "1976", "v": "/year/1976"}, {"n": "1977", "v": "/year/1977"}, {"n": "1978", "v": "/year/1978"}, {"n": "1980", "v": "/year/1980"}, {"n": "1982", "v": "/year/1982"}, {"n": "1983", "v": "/year/1983"}, {"n": "1984", "v": "/year/1984"}, {"n": "1985", "v": "/year/1985"}, {"n": "1986", "v": "/year/1986"}, {"n": "1987", "v": "/year/1987"}, {"n": "1988", "v": "/year/1988"}, {"n": "1989", "v": "/year/1989"}, {"n": "1990", "v": "/year/1990"}, {"n": "1991", "v": "/year/1991"}, {"n": "1992", "v": "/year/1992"}, {"n": "1993", "v": "/year/1993"}, {"n": "1994", "v": "/year/1994"}, {"n": "1995", "v": "/year/1995"}, {"n": "1996", "v": "/year/1996"}, {"n": "1997", "v": "/year/1997"}, {"n": "1998", "v": "/year/1998"}, {"n": "1999", "v": "/year/1999"}, {"n": "2000", "v": "/year/2000"}, {"n": "2001", "v": "/year/2001"}, {"n": "2002", "v": "/year/2002"}, {"n": "2003", "v": "/year/2003"}, {"n": "2004", "v": "/year/2004"}, {"n": "2005", "v": "/year/2005"}, {"n": "2006", "v": "/year/2006"}, {"n": "2007", "v": "/year/2007"}, {"n": "2008", "v": "/year/2008"}, {"n": "2009", "v": "/year/2009"}, {"n": "2010", "v": "/year/2010"}, {"n": "2011", "v": "/year/2011"}, {"n": "2012", "v": "/year/2012"}, {"n": "2013", "v": "/year/2013"}, {"n": "2014", "v": "/year/2014"}, {"n": "2015", "v": "/year/2015"}, {"n": "2016", "v": "/year/2016"}, {"n": "20165", "v": "/year/20165"}, {"n": "2017", "v": "/year/2017"}, {"n": "2018", "v": "/year/2018"}, {"n": "2019", "v": "/year/2019"}, {"n": "2020", "v": "/year/2020"}, {"n": "2021", "v": "/year/2021"}, {"n": "2022", "v": "/year/2022"}, {"n": "2023", "v": "/year/2023"}, {"n": "2024", "v": "/year/2024"}]}, {"key": "tags", "name": "影片类型", "value": [{"n": "全部", "v": ""}, {"n": "传记", "v": "/movie_bt_tags/zj"}, {"n": "儿童", "v": "/movie_bt_tags/ertong"}, {"n": "冒险", "v": "/movie_bt_tags/adt"}, {"n": "剧情", "v": "/movie_bt_tags/juqing"}, {"n": "动作", "v": "/movie_bt_tags/at"}, {"n": "动画", "v": "/movie_bt_tags/donghua"}, {"n": "历史", "v": "/movie_bt_tags/lishi"}, {"n": "古装", "v": "/movie_bt_tags/guzhuang"}, {"n": "同性", "v": "/movie_bt_tags/tongxing"}, {"n": "喜剧", "v": "/movie_bt_tags/xiju"}, {"n": "奇幻", "v": "/movie_bt_tags/qihuan"}, {"n": "家庭", "v": "/movie_bt_tags/jiating"}, {"n": "恐怖", "v": "/movie_bt_tags/kongbu"}, {"n": "悬疑", "v": "/movie_bt_tags/xuanni"}, {"n": "情色", "v": "/movie_bt_tags/qingse"}, {"n": "惊悚", "v": "/movie_bt_tags/jingsong"}, {"n": "戏曲", "v": "/movie_bt_tags/%e6%88%8f%e6%9b%b2"}, {"n": "战争", "v": "/movie_bt_tags/zhanzheng"}, {"n": "歌舞", "v": "/movie_bt_tags/gw"}, {"n": "武侠", "v": "/movie_bt_tags/wuxia"}, {"n": "灾难", "v": "/movie_bt_tags/zhannan"}, {"n": "爱情", "v": "/movie_bt_tags/aiqing"}, {"n": "犯罪", "v": "/movie_bt_tags/fanzui"}, {"n": "短片", "v": "/movie_bt_tags/%e7%9f%ad%e7%89%87"}, {"n": "科幻", "v": "/movie_bt_tags/kehuan"}, {"n": "纪录片", "v": "/movie_bt_tags/jilu"}, {"n": "西部", "v": "/movie_bt_tags/xibu"}, {"n": "运动", "v": "/movie_bt_tags/yd"}, {"n": "音乐", "v": "/movie_bt_tags/yinyue"}, {"n": "黑色电影", "v": "/movie_bt_tags/%e9%bb%91%e8%89%b2%e7%94%b5%e5%bd%b1"}]}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"1": [{"key": "class", "name": "分类", "value": [{"n": "全部", "v": ""}]}, {"key": "area", "name": "地区", "value": [{"n": "全部", "v": ""}, {"n": "日本", "v": "日本"}]}, {"key": "lang", "name": "语言", "value": [{"n": "全部", "v": ""}, {"n": "国语", "v": "国语"}, {"n": "英语", "v": "英语"}, {"n": "粤语", "v": "粤语"}, {"n": "闽南语", "v": "闽南语"}, {"n": "韩语", "v": "韩语"}, {"n": "日语", "v": "日语"}, {"n": "法语", "v": "法语"}, {"n": "德语", "v": "德语"}, {"n": "其它", "v": "其它"}]}, {"key": "year", "name": "年份", "value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}]}, {"key": "star", "name": "明星", "value": [{"n": "全部", "v": ""}, {"n": "王宝强", "v": "王宝强"}, {"n": "黄渤", "v": "黄渤"}, {"n": "周迅", "v": "周迅"}, {"n": "周冬雨", "v": "周冬雨"}, {"n": "范冰冰", "v": "范冰冰"}, {"n": "陈学冬", "v": "陈学冬"}, {"n": "陈伟霆", "v": "陈伟霆"}, {"n": "郭采洁", "v": "郭采洁"}, {"n": "邓超", "v": "邓超"}, {"n": "成龙", "v": "成龙"}, {"n": "葛优", "v": "葛优"}, {"n": "林正英", "v": "林正英"}, {"n": "张家辉", "v": "张家辉"}, {"n": "梁朝伟", "v": "梁朝伟"}, {"n": "徐峥", "v": "徐峥"}, {"n": "郑恺", "v": "郑恺"}, {"n": "吴彦祖", "v": "吴彦祖"}, {"n": "刘德华", "v": "刘德华"}, {"n": "周星驰", "v": "周星驰"}, {"n": "林青霞", "v": "林青霞"}, {"n": "周润发", "v": "周润发"}, {"n": "李连杰", "v": "李连杰"}, {"n": "甄子丹", "v": "甄子丹"}, {"n": "古天乐", "v": "古天乐"}, {"n": "洪金宝", "v": "洪金宝"}, {"n": "姚晨", "v": "姚晨"}, {"n": "倪妮", "v": "倪妮"}, {"n": "黄晓明", "v": "黄晓明"}, {"n": "彭于晏", "v": "彭于晏"}, {"n": "汤唯", "v": "汤唯"}, {"n": "陈小春", "v": "陈小春"}]}, {"key": "director", "name": "导演", "value": [{"n": "全部", "v": ""}, {"n": "冯小刚", "v": "冯小刚"}, {"n": "张艺谋", "v": "张艺谋"}, {"n": "吴宇森", "v": "吴宇森"}, {"n": "陈凯歌", "v": "陈凯歌"}, {"n": "徐克", "v": "徐克"}, {"n": "王家卫", "v": "王家卫"}, {"n": "姜文", "v": "姜文"}, {"n": "周星驰", "v": "周星驰"}, {"n": "李安", "v": "李安"}]}, {"key": "state", "name": "状态", "value": [{"n": "全部", "v": ""}, {"n": "正片", "v": "正片"}, {"n": "预告片", "v": "预告片"}, {"n": "花絮", "v": "花絮"}]}, {"key": "version", "name": "版本", "value": [{"n": "全部", "v": ""}, {"n": "高清版", "v": "高清版"}, {"n": "剧场版", "v": "剧场版"}, {"n": "抢先版", "v": "抢先版"}, {"n": "OVA", "v": "OVA"}, {"n": "TV", "v": "TV"}, {"n": "影院版", "v": "影院版"}]}], "2": [{"key": "class", "name": "分类", "value": [{"n": "全部", "v": ""}]}, {"key": "area", "name": "地区", "value": [{"n": "全部", "v": ""}, {"n": "大陆", "v": "大陆"}]}, {"key": "lang", "name": "语言", "value": [{"n": "全部", "v": ""}, {"n": "国语", "v": "国语"}]}, {"key": "year", "name": "年份", "value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}]}, {"key": "star", "name": "明星", "value": [{"n": "全部", "v": ""}, {"n": "王宝强", "v": "王宝强"}, {"n": "胡歌", "v": "胡歌"}, {"n": "霍建华", "v": "霍建华"}, {"n": "赵丽颖", "v": "赵丽颖"}, {"n": "刘涛", "v": "刘涛"}, {"n": "刘诗诗", "v": "刘诗诗"}, {"n": "陈伟霆", "v": "陈伟霆"}, {"n": "吴奇隆", "v": "吴奇隆"}, {"n": "陆毅", "v": "陆毅"}, {"n": "唐嫣", "v": "唐嫣"}, {"n": "关晓彤", "v": "关晓彤"}, {"n": "孙俪", "v": "孙俪"}, {"n": "李易峰", "v": "李易峰"}, {"n": "张翰", "v": "张翰"}, {"n": "李晨", "v": "李晨"}, {"n": "范冰冰", "v": "范冰冰"}, {"n": "林心如", "v": "林心如"}, {"n": "文章", "v": "文章"}, {"n": "马伊琍", "v": "马伊琍"}, {"n": "佟大为", "v": "佟大为"}, {"n": "孙红雷", "v": "孙红雷"}, {"n": "陈建斌", "v": "陈建斌"}, {"n": "李小璐", "v": "李小璐"}]}, {"key": "director", "name": "导演", "value": [{"n": "全部", "v": ""}, {"n": "张纪中", "v": "张纪中"}, {"n": "李少红", "v": "李少红"}, {"n": "刘江", "v": "刘江"}, {"n": "孔笙", "v": "孔笙"}, {"n": "张黎", "v": "张黎"}, {"n": "康洪雷", "v": "康洪雷"}, {"n": "高希希", "v": "高希希"}, {"n": "胡玫", "v": "胡玫"}, {"n": "赵宝刚", "v": "赵宝刚"}, {"n": "郑晓龙", "v": "郑晓龙"}]}, {"key": "state", "name": "状态", "value": [{"n": "全部", "v": ""}, {"n": "正片", "v": "正片"}, {"n": "预告片", "v": "预告片"}, {"n": "花絮", "v": "花絮"}]}, {"key": "version", "name": "版本", "value": [{"n": "全部", "v": ""}, {"n": "高清版", "v": "高清版"}, {"n": "剧场版", "v": "剧场版"}, {"n": "抢先版", "v": "抢先版"}, {"n": "OVA", "v": "OVA"}, {"n": "TV", "v": "TV"}, {"n": "影院版", "v": "影院版"}]}], "3": [{"key": "class", "name": "分类", "value": [{"n": "全部", "v": ""}, {"n": "韓國動漫", "v": "韓國動漫"}, {"n": "歐美動漫", "v": "歐美動漫"}]}, {"key": "area", "name": "地区", "value": [{"n": "全部", "v": ""}, {"n": "日本", "v": "日本"}, {"n": "大陆", "v": "大陆"}, {"n": "韩国", "v": "韩国"}, {"n": "美国", "v": "美国"}, {"n": "英国", "v": "英国"}, {"n": "加拿大", "v": "加拿大"}, {"n": "墨西哥", "v": "墨西哥"}, {"n": "欧美", "v": "欧美"}, {"n": "其他", "v": "其他"}]}, {"key": "lang", "name": "语言", "value": [{"n": "全部", "v": ""}, {"n": "国语", "v": "国语"}, {"n": "英语", "v": "英语"}, {"n": "粤语", "v": "粤语"}, {"n": "闽南语", "v": "闽南语"}, {"n": "韩语", "v": "韩语"}, {"n": "日语", "v": "日语"}, {"n": "其它", "v": "其它"}]}, {"key": "year", "name": "年份", "value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}]}, {"key": "star", "name": "明星", "value": [{"n": "全部", "v": ""}, {"n": "何炅", "v": "何炅"}, {"n": "汪涵", "v": "汪涵"}, {"n": "谢娜", "v": "谢娜"}, {"n": "周立波", "v": "周立波"}, {"n": "陈鲁豫", "v": "陈鲁豫"}, {"n": "孟非", "v": "孟非"}, {"n": "李静", "v": "李静"}, {"n": "朱军", "v": "朱军"}, {"n": "朱丹", "v": "朱丹"}, {"n": "华少", "v": "华少"}, {"n": "郭德纲", "v": "郭德纲"}, {"n": "杨澜", "v": "杨澜"}]}, {"key": "director", "name": "导演", "value": [{"n": "全部", "v": ""}]}, {"key": "state", "name": "状态", "value": [{"n": "全部", "v": ""}]}, {"key": "version", "name": "版本", "value": [{"n": "全部", "v": ""}]}], "26": [{"key": "class", "name": "分类", "value": [{"n": "全部", "v": ""}]}, {"key": "area", "name": "地区", "value": [{"n": "全部", "v": ""}, {"n": "日本", "v": "日本"}, {"n": "大陆", "v": "大陆"}, {"n": "韩国", "v": "韩国"}, {"n": "美国", "v": "美国"}, {"n": "英国", "v": "英国"}, {"n": "加拿大", "v": "加拿大"}, {"n": "欧美", "v": "欧美"}, {"n": "其他", "v": "其他"}]}, {"key": "lang", "name": "语言", "value": [{"n": "全部", "v": ""}]}, {"key": "year", "name": "年份", "value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}]}, {"key": "star", "name": "明星", "value": [{"n": "全部", "v": ""}]}, {"key": "director", "name": "导演", "value": [{"n": "全部", "v": ""}]}, {"key": "state", "name": "状态", "value": [{"n": "全部", "v": ""}]}, {"key": "version", "name": "版本", "value": [{"n": "全部", "v": ""}]}]}
|
||||
@@ -0,0 +1,538 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# File : 两个BT.py
|
||||
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
|
||||
# Author's Blog: https://blog.csdn.net/qq_32394351
|
||||
# Date : 2024/1/8
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
import json
|
||||
import time
|
||||
import base64
|
||||
import re
|
||||
from pathlib import Path
|
||||
import io
|
||||
import tokenize
|
||||
from urllib.parse import quote
|
||||
from Crypto.Cipher import AES, PKCS1_v1_5 as PKCS1_cipher
|
||||
from Crypto.Util.Padding import unpad
|
||||
|
||||
"""
|
||||
配置示例:
|
||||
t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾
|
||||
api里会自动含有ext参数是base64编码后的选中的筛选条件
|
||||
{
|
||||
"key":"hipy_t4_两个BT",
|
||||
"name":"两个BT(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/两个BT?api_ext={{host}}/txt/hipy/两个BT.json",
|
||||
"searchable":1,
|
||||
"quickSearch":0,
|
||||
"filterable":1,
|
||||
"ext":"{{host}}/files/hipy/两个BT.json"
|
||||
},
|
||||
{
|
||||
"key": "hipy_t3_两个BT",
|
||||
"name": "两个BT(hipy_t3)",
|
||||
"type": 3,
|
||||
"api": "{{host}}/txt/hipy/两个BT.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 0,
|
||||
"filterable": 1,
|
||||
"ext": "{{host}}/files/hipy/两个BT.json"
|
||||
},
|
||||
"""
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
api: str = 'https://www.bttwo.org'
|
||||
api_ext_file: str = api + '/movie_bt/'
|
||||
search_api: str = ''
|
||||
|
||||
def getName(self):
|
||||
return "规则名称如:基础示例"
|
||||
|
||||
def init_api_ext_file(self):
|
||||
"""
|
||||
这个函数用于初始化py文件对应的json文件,用于存筛选规则。
|
||||
执行此函数会自动生成筛选文件
|
||||
@return:
|
||||
"""
|
||||
ext_file = __file__.replace('.py', '.json')
|
||||
print(f'ext_file:{ext_file}')
|
||||
|
||||
# 全部电影网页: https://www.bttwo.org/movie_bt/
|
||||
# ==================== 获取全部电影筛选条件 ======================
|
||||
r = self.fetch(self.api_ext_file)
|
||||
html = r.text
|
||||
html = self.html(html)
|
||||
|
||||
filter_movie_bt = []
|
||||
lis = html.xpath('//*[@id="beautiful-taxonomy-filters-tax-movie_bt_cat"]/a')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./text()')),
|
||||
'v': ''.join(li.xpath('@cat-url')).replace(self.api, ''),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_movie_bt.append({
|
||||
"key": "cat",
|
||||
"name": "地区",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
lis = html.xpath('//*[@id="beautiful-taxonomy-filters-tax-movie_bt_year"]/a')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./text()')),
|
||||
'v': ''.join(li.xpath('@cat-url')).replace(self.api, ''),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_movie_bt.append({
|
||||
"key": "year",
|
||||
"name": "年份",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
lis = html.xpath('//*[@id="beautiful-taxonomy-filters-tax-movie_bt_tags"]/a')
|
||||
li_value = []
|
||||
for li in lis:
|
||||
li_value.append({
|
||||
'n': ''.join(li.xpath('./text()')),
|
||||
'v': ''.join(li.xpath('@cat-url')).replace(self.api, ''),
|
||||
})
|
||||
# print(li_value)
|
||||
filter_movie_bt.append({
|
||||
"key": "tags",
|
||||
"name": "影片类型",
|
||||
"value": li_value
|
||||
})
|
||||
|
||||
print(filter_movie_bt)
|
||||
|
||||
ext_file_dict = {
|
||||
"movie_bt": filter_movie_bt,
|
||||
}
|
||||
with open(ext_file, mode='w+', encoding='utf-8') as f:
|
||||
f.write(json.dumps(ext_file_dict, ensure_ascii=False))
|
||||
|
||||
def init(self, extend=""):
|
||||
"""
|
||||
初始化加载extend,一般与py文件名同名的json文件作为扩展筛选
|
||||
@param extend:
|
||||
@return:
|
||||
"""
|
||||
|
||||
def init_file(ext_file):
|
||||
"""
|
||||
根据与py对应的json文件去扩展规则的筛选条件
|
||||
"""
|
||||
ext_file = Path(ext_file).as_posix()
|
||||
if os.path.exists(ext_file):
|
||||
with open(ext_file, mode='r', encoding='utf-8') as f:
|
||||
try:
|
||||
ext_dict = json.loads(f.read())
|
||||
self.config['filter'].update(ext_dict)
|
||||
except Exception as e:
|
||||
print(f'更新扩展筛选条件发生错误:{e}')
|
||||
|
||||
ext = self.extend
|
||||
print(f"============{extend}============")
|
||||
if isinstance(ext, str):
|
||||
if ext.startswith('./'):
|
||||
ext_file = os.path.join(os.path.dirname(__file__), ext)
|
||||
init_file(ext_file)
|
||||
elif ext.startswith('http'):
|
||||
try:
|
||||
r = self.fetch(ext)
|
||||
self.config['filter'].update(r.json())
|
||||
except Exception as e:
|
||||
print(f'更新扩展筛选条件发生错误:{e}')
|
||||
elif not ext.startswith('./') and not ext.startswith('http'):
|
||||
ext_file = os.path.join(os.path.dirname(__file__), './' + ext + '.json')
|
||||
init_file(ext_file)
|
||||
|
||||
# 装载模块,这里只要一个就够了
|
||||
if isinstance(extend, list):
|
||||
for lib in extend:
|
||||
if '.Spider' in str(type(lib)):
|
||||
self.module = lib
|
||||
break
|
||||
|
||||
def isVideo(self):
|
||||
"""
|
||||
返回是否为视频的匹配字符串
|
||||
@return: None空 reg:正则表达式 js:input js代码
|
||||
"""
|
||||
# return 'js:input.includes("https://zf.13to.com/")?true:false'
|
||||
return 'reg:zf\.13to\.com'
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filterable=False):
|
||||
"""
|
||||
获取首页分类及筛选数据
|
||||
@param filterable: 能否筛选,跟t3/t4配置里的filterable参数一致
|
||||
@return:
|
||||
"""
|
||||
class_name = '影片库&最新电影&热门下载&本月热门&国产剧&美剧&日韩剧' # 静态分类名称拼接
|
||||
class_url = 'movie_bt&new-movie&hot&hot-month&zgjun&meiju&jpsrtv' # 静态分类标识拼接
|
||||
|
||||
result = {}
|
||||
classes = []
|
||||
|
||||
if all([class_name, class_url]):
|
||||
class_names = class_name.split('&')
|
||||
class_urls = class_url.split('&')
|
||||
cnt = min(len(class_urls), len(class_names))
|
||||
for i in range(cnt):
|
||||
classes.append({
|
||||
'type_name': class_names[i],
|
||||
'type_id': class_urls[i]
|
||||
})
|
||||
|
||||
result['class'] = classes
|
||||
if filterable:
|
||||
result['filters'] = self.config['filter']
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
"""
|
||||
首页推荐列表
|
||||
@return:
|
||||
"""
|
||||
r = self.fetch(self.api)
|
||||
html = r.text
|
||||
html = self.html(html)
|
||||
d = []
|
||||
|
||||
self.search_api = "".join(html.xpath('//*[contains(@class,"w-search-form")]/@action')).strip()
|
||||
lis = html.xpath('//*[contains(@class,"leibox")]/ul/li')
|
||||
print(len(lis))
|
||||
for li in lis:
|
||||
d.append({
|
||||
'vod_name': ''.join(li.xpath('h3//text()')),
|
||||
'vod_id': ''.join(li.xpath('a/@href')),
|
||||
'vod_pic': ''.join(li.xpath('.//img//@data-original')),
|
||||
'vod_remarks': ''.join(li.xpath('.//*[contains(@class,"jidi")]//text()')),
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filterable, extend):
|
||||
"""
|
||||
返回一级列表页数据
|
||||
@param tid: 分类id
|
||||
@param pg: 当前页数
|
||||
@param filterable: 能否筛选
|
||||
@param extend: 当前筛选数据
|
||||
@return:
|
||||
"""
|
||||
page_count = 24 # 默认赋值一页列表24条数据
|
||||
if tid != 'movie_bt':
|
||||
url = self.api + f'/{tid}/page/{pg}'
|
||||
else:
|
||||
fls = extend.keys() # 哪些刷新数据
|
||||
url = self.api + f'/{tid}'
|
||||
if 'cat' in fls:
|
||||
url += extend['cat']
|
||||
if 'year' in fls:
|
||||
url += extend['year']
|
||||
if 'tags' in fls:
|
||||
url += extend['tags']
|
||||
url += f'/page/{pg}'
|
||||
print(url)
|
||||
|
||||
r = self.fetch(url)
|
||||
html = r.text
|
||||
html = self.html(html)
|
||||
d = []
|
||||
lis = html.xpath('//*[contains(@class,"bt_img")]/ul/li')
|
||||
# print(len(lis))
|
||||
for li in lis:
|
||||
d.append({
|
||||
'vod_name': ''.join(li.xpath('h3//text()')),
|
||||
'vod_id': ''.join(li.xpath('a/@href')),
|
||||
'vod_pic': ''.join(li.xpath('.//img//@data-original')),
|
||||
'vod_remarks': ''.join(li.xpath('.//*[contains(@class,"hdinfo")]//text()')),
|
||||
})
|
||||
|
||||
result = {
|
||||
'list': d,
|
||||
'page': pg,
|
||||
'pagecount': 9999 if len(d) >= page_count else pg,
|
||||
'limit': 90,
|
||||
'total': 999999,
|
||||
}
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
"""
|
||||
返回二级详情页数据
|
||||
@param ids: 一级传过来的vod_id列表
|
||||
@return:
|
||||
"""
|
||||
vod_id = ids[0]
|
||||
r = self.fetch(vod_id)
|
||||
html = r.text
|
||||
html = self.html(html)
|
||||
lis = html.xpath('//*[contains(@class,"dytext")]/ul/li')
|
||||
plis = html.xpath('//*[contains(@class,"paly_list_btn")]/a')
|
||||
vod = {"vod_id": vod_id,
|
||||
"vod_name": ''.join(html.xpath('//*[contains(@class,"dytext")]//h1//text()')),
|
||||
"vod_pic": ''.join(html.xpath('//*[contains(@class,"dyimg")]/img/@src')),
|
||||
"type_name": ''.join(lis[0].xpath('.//text()')) if len(lis) > 0 else '',
|
||||
"vod_year": ''.join(lis[2].xpath('.//text()')) if len(lis) > 2 else '',
|
||||
"vod_area": ''.join(lis[1].xpath('.//text()')) if len(lis) > 1 else '',
|
||||
"vod_remarks": ''.join(lis[4].xpath('.//text()')) if len(lis) > 4 else '',
|
||||
"vod_actor": ''.join(lis[7].xpath('.//text()')) if len(lis) > 7 else '',
|
||||
"vod_director": ''.join(lis[5].xpath('.//text()')) if len(lis) > 5 else '',
|
||||
"vod_content": ''.join(html.xpath('//*[contains(@class,"yp_context")]/p//text()')),
|
||||
"vod_play_from": '在线播放',
|
||||
"vod_play_url": '选集播放1$1.mp4#选集播放2$2.mp4$$$选集播放3$3.mp4#选集播放4$4.mp4'}
|
||||
vod_play_urls = []
|
||||
for pli in plis:
|
||||
vname = ''.join(pli.xpath('./text()'))
|
||||
vurl = ''.join(pli.xpath('./@href'))
|
||||
vod_play_urls.append(vname + '$' + vurl)
|
||||
vod['vod_play_url'] = '#'.join(vod_play_urls)
|
||||
result = {
|
||||
'list': [vod]
|
||||
}
|
||||
return result
|
||||
|
||||
def searchContent(self, wd, quick=False, pg=1):
|
||||
"""
|
||||
返回搜索列表
|
||||
@param wd: 搜索关键词
|
||||
@param quick: 是否来自快速搜索。t3/t4配置里启用了快速搜索,在快速搜索在执行才会是True
|
||||
@return:
|
||||
"""
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
|
||||
"Host": "www.bttwo.net",
|
||||
"Referer": self.api
|
||||
}
|
||||
self.log(f'self.search_api:{self.search_api}')
|
||||
search_api = self.search_api or f'{self.api}/xsssearch'
|
||||
url = f'{search_api}?q={quote(wd)}'
|
||||
print(url)
|
||||
r = self.fetch(url, headers=headers)
|
||||
cookies = ['myannoun=1']
|
||||
for key, value in r.headers.items():
|
||||
if str(key).lower() == 'set-cookie':
|
||||
cookies.append(value.split(';')[0])
|
||||
new_headers = {
|
||||
'Cookie': ';'.join(cookies),
|
||||
# 'Pragma': 'no-cache',
|
||||
# 'Origin': 'https://www.bttwo.org',
|
||||
# 'Referer': url,
|
||||
# 'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
|
||||
# 'Sec-Ch-Ua-Mobile': '?0',
|
||||
# 'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
# 'Sec-Fetch-Dest': 'document',
|
||||
# 'Sec-Fetch-Mode': 'navigate',
|
||||
# 'Sec-Fetch-Site': 'same-origin',
|
||||
# 'Sec-Fetch-User': '?1',
|
||||
# 'Upgrade-Insecure-Requests': '1',
|
||||
}
|
||||
headers.update(new_headers)
|
||||
# print(headers)
|
||||
html = self.html(r.text)
|
||||
captcha = ''.join(html.xpath('//*[@class="erphp-search-captcha"]/form/text()')).strip()
|
||||
# print('验证码:', captcha)
|
||||
answer = self.eval_computer(captcha)
|
||||
# print('回答:', captcha, answer)
|
||||
data = {'result': str(answer)}
|
||||
# print('待post数据:', data)
|
||||
self.post(url, data=data, headers=headers, cookies=None)
|
||||
r = self.fetch(url, headers=headers)
|
||||
# print(r.text)
|
||||
html = self.html(r.text)
|
||||
lis = html.xpath('//*[contains(@class,"search_list")]/ul/li')
|
||||
print('搜索结果数:', len(lis))
|
||||
d = []
|
||||
if len(lis) < 1:
|
||||
d.append({
|
||||
'vod_name': wd,
|
||||
'vod_id': 'index.html',
|
||||
'vod_pic': 'https://gitee.com/CherishRx/imagewarehouse/raw/master/image/13096725fe56ce9cf643a0e4cd0c159c.gif',
|
||||
'vod_remarks': '测试搜索',
|
||||
})
|
||||
else:
|
||||
for li in lis:
|
||||
d.append({
|
||||
'vod_name': ''.join(li.xpath('h3//text()')),
|
||||
'vod_id': ''.join(li.xpath('a/@href')),
|
||||
'vod_pic': ''.join(li.xpath('a/img/@data-original')),
|
||||
'vod_remarks': ''.join(li.xpath('p//text()')),
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
# print(result)
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
"""
|
||||
解析播放,返回json。壳子视情况播放直链或进行嗅探
|
||||
@param flag: vod_play_from 播放来源线路
|
||||
@param id: vod_play_url 播放的链接
|
||||
@param vipFlags: vip标识
|
||||
@return:
|
||||
"""
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux;; Android 11;; M2007J3SC Build/RKQ1.200826.002;; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.48 Mobile Safari/537.36',
|
||||
'Referer': id,
|
||||
}
|
||||
# return {
|
||||
# 'parse': 1, # 1=嗅探,0=播放
|
||||
# 'playUrl': '', # 解析链接
|
||||
# 'url': id, # 直链或待嗅探地址
|
||||
# 'header': headers, # 播放UA
|
||||
# }
|
||||
r = self.fetch(id)
|
||||
html = r.text
|
||||
text = html.split('window.wp_nonce=')[1].split('eval')[0]
|
||||
# print(text)
|
||||
code = self.regStr(text, 'var .*?=.*?"(.*?)"')
|
||||
key = self.regStr(text, 'var .*?=md5.enc.Utf8.parse\\("(.*?)"')
|
||||
iv = self.regStr(text, 'var iv=.*?\\((\\d+)')
|
||||
text = self.aes_cbs_decode(code, key, iv)
|
||||
# print(code)
|
||||
# print(key,iv)
|
||||
# print(text)
|
||||
url = self.regStr(text, 'url: "(.*?)"')
|
||||
# print(url)
|
||||
parse = 0
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux;; Android 11;; M2007J3SC Build/RKQ1.200826.002;; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.48 Mobile Safari/537.36',
|
||||
'Referer': url,
|
||||
}
|
||||
result = {
|
||||
'parse': parse, # 1=嗅探,0=播放
|
||||
'playUrl': '', # 解析链接
|
||||
'url': url, # 直链或待嗅探地址
|
||||
'header': headers, # 播放UA
|
||||
}
|
||||
print(result)
|
||||
return result
|
||||
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {}
|
||||
}
|
||||
header = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
|
||||
"Host": "www.bttwo.net",
|
||||
"Referer": "https://www.bttwo.org/"
|
||||
}
|
||||
|
||||
def localProxy(self, params):
|
||||
return [200, "video/MP2T", ""]
|
||||
|
||||
# -----------------------------------------------自定义函数-----------------------------------------------
|
||||
def eval_computer(self, text):
|
||||
"""
|
||||
自定义的字符串安全计算器
|
||||
@param text:字符串的加减乘除
|
||||
@return:计算后得到的值
|
||||
"""
|
||||
localdict = {}
|
||||
self.safe_eval(f'ret={text.replace("=", "")}', localdict)
|
||||
ret = localdict.get('ret') or None
|
||||
return ret
|
||||
|
||||
def safe_eval(self, code: str = '', localdict: dict = None):
|
||||
code = code.strip()
|
||||
if not code:
|
||||
return {}
|
||||
if localdict is None:
|
||||
localdict = {}
|
||||
builtins = __builtins__
|
||||
if not isinstance(builtins, dict):
|
||||
builtins = builtins.__dict__.copy()
|
||||
else:
|
||||
builtins = builtins.copy()
|
||||
for key in ['__import__', 'eval', 'exec', 'globals', 'dir', 'copyright', 'open', 'quit']:
|
||||
del builtins[key] # 删除不安全的关键字
|
||||
# print(builtins)
|
||||
global_dict = {'__builtins__': builtins,
|
||||
'json': json, 'print': print,
|
||||
're': re, 'time': time, 'base64': base64
|
||||
} # 禁用内置函数,不允许导入包
|
||||
try:
|
||||
self.check_unsafe_attributes(code)
|
||||
exec(code, global_dict, localdict)
|
||||
return localdict
|
||||
except Exception as e:
|
||||
return {'error': f'执行报错:{e}'}
|
||||
|
||||
# ==================== 静态函数 ======================
|
||||
@staticmethod
|
||||
def aes_cbs_decode(ciphertext, key, iv):
|
||||
# 将密文转换成byte数组
|
||||
ciphertext = base64.b64decode(ciphertext)
|
||||
# 构建AES解密器
|
||||
decrypter = AES.new(key.encode(), AES.MODE_CBC, iv.encode())
|
||||
# 解密
|
||||
plaintext = decrypter.decrypt(ciphertext)
|
||||
# 去除填充
|
||||
plaintext = unpad(plaintext, AES.block_size)
|
||||
# 输出明文
|
||||
# print(plaintext.decode('utf-8'))
|
||||
return plaintext.decode('utf-8')
|
||||
|
||||
@staticmethod
|
||||
def check_unsafe_attributes(string):
|
||||
"""
|
||||
安全检测需要exec执行的python代码
|
||||
:param string:
|
||||
:return:
|
||||
"""
|
||||
g = tokenize.tokenize(io.BytesIO(string.encode('utf-8')).readline)
|
||||
pre_op = ''
|
||||
for toktype, tokval, _, _, _ in g:
|
||||
if toktype == tokenize.NAME and pre_op == '.' and tokval.startswith('_'):
|
||||
attr = tokval
|
||||
msg = "access to attribute '{0}' is unsafe.".format(attr)
|
||||
raise AttributeError(msg)
|
||||
elif toktype == tokenize.OP:
|
||||
pre_op = tokval
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from t4.core.loader import t4_spider_init
|
||||
|
||||
spider = Spider()
|
||||
t4_spider_init(spider)
|
||||
# spider.init_api_ext_file() # 生成筛选对应的json文件
|
||||
|
||||
print(spider.homeVideoContent())
|
||||
# print(spider.categoryContent('movie_bt', 1, True, {}))
|
||||
print(spider.searchContent('斗罗大陆'))
|
||||
# print(spider.detailContent(['https://www.bttwo.org/movie/20107.html']))
|
||||
# print(spider.playerContent('在线播放', spider.decodeStr('https%3A%2F%2Fwww.bttwo.net%2Fv_play%2FbXZfMzY4Nzgtbm1fMQ%3D%3D.html','utf-8'), None))
|
||||
# print(spider.playerContent('在线播放', spider.decodeStr('https://www.bttwo.org/v_play/bXZfMTMyNjkwLW5tXzE=.html','utf-8'), None))
|
||||
# print(spider.playerContent('在线播放', 'https://www.bttwo.org/v_play/bXZfMTMyNjA2LW5tXzE=.html', None))
|
||||
|
||||
# ciphertext = '+T77kORPkp6wtgdzcqQgPmUXomqshgO6IfTIGE8/40Iht0nDYW9pcGGUk/1157KS876b7FW1m6JMjPY2G+pwtscUjTcCq2G2NTnAX+1iMIexjK+nfTobgi2qYMtke/sWWe51RH/9IxqvoosAhH4dlN+QT/TIHKFFa6OyFiFp2hlUvPNpukbtZcHHshHMolQc9JmW3av+Js9AcyKDLuoFg9N38jrBidnUadw/9Pog/lsoRXUp7JFhdiVujAIkxTJjabvQXT2jGQS88MY7/kiem5SikAh/D+zVPnwO3E7z87o3GIC4agtWKbjTCfeRsUCGg20fEiEl79YoJAaBofZ67cHYNvjcvu6DPSE1Nf29keNMoZlSCLvJPOzSv1+nBi4aVz4s5M2puSDczFyFPPE6aW4Zpr1tVRstr/RuMPLZoDu2D/p6Znxrvwcgj8N6g997Y8P6jNGhdSdmLaFQNgjJT/4cBV1X8W3UzohaapewK3Zum6lmyzcNRlXHHdoCyM4WNYoEOTjln0oKexGIXEBoGijjTzVpng9eGAjMyjYoPKAC0ZCAPTMv94UlLRruUbEtCxlMN0AYzNB2mC/otT6bu/063/ECzCvBS7LjJuamYX+2zsSomIUMiNzfx4S4/ZY9M8tGdVclNKKCzCQ+ovWUPMvEtKDW+g/qUdfx8a/cXMYkEeR66D5ChMGlEVwayytjjJDn4a0/4SxpcOkNVwRMFfhyuFNAPyS65m7ieJe+r5QuwlMa67DwQdBRkw4t2bmt3CXU+qPvfeCchNcVKjHPAwWaHbI3NGN+/4sZ5aa9aLV/r0jIwL8ThWHwbbvox/VCfCLtrtNX1JW7VPnqHudvuqDb2VE5nYPU96VdNGUoGSNUJraXPQ2J1YG0x6DKOznfPiwrK6pD0emY3mtCQcN1UB62q0nTvavI3GBpFKd5y9w4idS+pjHBpdedL4lFc9ynq9oYNgd4xuGNj35a+SgZfdR7DqiaxIU9kDA1yW5nzOw05ui0h8TbPWJX9YypLm/CZu5AQxkS92gbzxXYGwjBrEqqgrAoWFxAUb1FsU5WZZl4+soOYbbKUwSe4zXj+agwpSQs6XuV+b4OKB9GOLYlxSxrLMPnGGBObl8qHmren1Drdw3UtF55MEgV402fvj/ClPCeWIlgUaZdD2c802qd8cc9lzTEwyuLUVvtfrMGCxJV1tbe0w4i+WFVaxXX/cIfzQ7QNxUHfYNDW/zp80f5jaL9zbbPo3aKUroWrhlsM7ecT1M78PG4orVC3stAoNRo3mURlHQepkjVvaiufvxb2Zf/ofao9ou1vlHN0+CFyM8vCRLnH1zY3E3gyCGHMJCPAiRyZGOMIsECw5w/+K+FkcLWBTz9CnYCcIsyIaQGUyoMecYE+RZSbYYoC5xhI18xzZZZ1UJCjnKJRhdAumb5y3aAnOOX5Hj2KL6CD3PmPbSzE08ihcwxaRbME+2/zIxErr1j0MJmSvHBi9L1KCfGhizwFtJmu0MG0laGskYJflJUsIJE9BmuG7GCvCl4CKHYueKgpGn0ogd5QVDg5F/R3/tinEcw4n1Re0qlhKKyKhg8rCnOigAZCgET68/EOSMLxTlP4wY3Jtts12Zc5bL1MB6HkANlbwGryiiej4I8HmoH13AaS65cWmfZw9bJ4PffJYdhyns0qScbzGxQBiwJHZn7/mO6Yc7c0bfrevUeM4HogAHZTZYd7QIeH5ehmEUnPHv11GXtVJcN4sHhaaxDA4RVV5aN+4vRA3OgUhbuqebYcB5rVuMx7t3fw5kwQzQP7lnkPcXjjCLrLueCYyWJgUAKHi5TrAS9YtgHaIOA1lH0dIKAq+V8SoZPBxjxPr7AywT0d8qZc321NCbavu4voMZfh5ylrAuP7hYe1n9qGCFwZ/mQUoYLhPW0T6t3zmLEJgI9S0vm8SE0Z7BHam8O1P4xD9gFk/O1AumNs9rxFQT+exE+pZKJPKDXAgfEG11oUuB8sW/cgEwRZeLy3J543uWVS/LWY08SbVovKVWaTzm8JVGlwz2puLt5amzTLKUc'
|
||||
# key = 'ae05c73de8a193cf'
|
||||
# iv = '1234567890983456'
|
||||
# print(spider.aes_cbs_decode(ciphertext, key, iv))
|
||||
@@ -0,0 +1,310 @@
|
||||
# coding=utf-8
|
||||
# !/usr/bin/python
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
import base64
|
||||
import hashlib
|
||||
import requests
|
||||
from Crypto.Cipher import AES
|
||||
import urllib
|
||||
# import re
|
||||
import json
|
||||
|
||||
|
||||
# from base.htmlParser import jsoup
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
def getName(self):
|
||||
return "厂长资源"
|
||||
|
||||
def init(self, extend=""):
|
||||
print("============{0}============".format(extend))
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
cateManual = {
|
||||
"国产剧": "gcj",
|
||||
"最新电影": "zuixindianying",
|
||||
"电视剧": "dsj",
|
||||
"美剧": "meijutt",
|
||||
"韩剧": "hanjutv",
|
||||
"番剧": "fanju",
|
||||
"动漫": "dm",
|
||||
"豆瓣电影Top250": "dbtop250"
|
||||
}
|
||||
classes = []
|
||||
for k in cateManual:
|
||||
classes.append({
|
||||
'type_name': k,
|
||||
'type_id': cateManual[k]
|
||||
})
|
||||
result['class'] = classes
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
url = "https://www.czys.pro"
|
||||
header = {
|
||||
"Connection": "keep-alive",
|
||||
"Referer": url,
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
|
||||
}
|
||||
rsp = self.getCookie(url)
|
||||
root = self.html(self.cleanText(rsp.text))
|
||||
aList = root.xpath("//div[contains(@class,'leibox')]//ul/li")
|
||||
videos = []
|
||||
for a in aList:
|
||||
name = a.xpath('./a/img/@alt')[0]
|
||||
pic = a.xpath('./a/img/@data-original')[0]
|
||||
mark = ''.join(a.xpath(".//*[@class='hdinfo']//span/text()"))
|
||||
sid = a.xpath("./a/@href")[0]
|
||||
sid = self.regStr(sid, "/movie/(\\S+).html")
|
||||
videos.append({
|
||||
"vod_id": sid,
|
||||
"vod_name": name,
|
||||
"vod_pic": pic,
|
||||
"vod_remarks": mark
|
||||
})
|
||||
result = {
|
||||
'list': videos
|
||||
}
|
||||
return result
|
||||
|
||||
def getCookie(self, url):
|
||||
header = {
|
||||
"Referer": 'https://www.czys.pro/',
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
|
||||
}
|
||||
session = requests.session()
|
||||
rsp = session.get(url)
|
||||
if '人机验证' in rsp.text:
|
||||
append = self.regStr(rsp.text, 'src=\"(/.*?)\"')
|
||||
nurl = 'https://www.czys.pro' + append
|
||||
nrsp = session.get(nurl, headers=header)
|
||||
key = self.regStr(nrsp.text, 'var key=\"(.*?)\"')
|
||||
avalue = self.regStr(nrsp.text, 'value=\"(.*?)\"')
|
||||
c = ''
|
||||
for i in range(0, len(avalue)):
|
||||
a = avalue[i]
|
||||
b = ord(a)
|
||||
c = c + str(b)
|
||||
value = hashlib.md5(c.encode()).hexdigest()
|
||||
session.get(
|
||||
'https://www.czys.pro/a20be899_96a6_40b2_88ba_32f1f75f1552_yanzheng_ip.php?type=96c4e20a0e951f471d32dae103e83881&key={0}&value={1}'.format(
|
||||
key, value), headers=header)
|
||||
return session.get(url, headers=header)
|
||||
elif '检测中' in rsp.text:
|
||||
append = self.regStr(rsp.text, 'href =\"(/.*?)\"')
|
||||
session.get('https://www.czys.pro{0}'.format(append), headers=header)
|
||||
return session.get(url, headers=header)
|
||||
else:
|
||||
return rsp
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
result = {}
|
||||
url = 'https://www.czys.pro/{0}/page/{1}'.format(tid, pg)
|
||||
rsp = self.getCookie(url)
|
||||
root = self.html(self.cleanText(rsp.text))
|
||||
aList = root.xpath("//div[contains(@class,'bt_img mi_ne_kd mrb')]/ul/li")
|
||||
videos = []
|
||||
for a in aList:
|
||||
name = a.xpath('./a/img/@alt')[0]
|
||||
pic = a.xpath('./a/img/@data-original')[0]
|
||||
mark = ''.join(a.xpath(".//div[@class='jidi']//span/text()"))
|
||||
if not mark:
|
||||
mark = ''.join(a.xpath("./div[@class='hdinfo']//span/text()"))
|
||||
sid = a.xpath("./a/@href")[0]
|
||||
sid = self.regStr(sid, "/movie/(\\S+).html")
|
||||
videos.append({
|
||||
"vod_id": sid,
|
||||
"vod_name": name,
|
||||
"vod_pic": pic,
|
||||
"vod_remarks": mark
|
||||
})
|
||||
result['list'] = videos
|
||||
result['page'] = pg
|
||||
result['pagecount'] = 9999
|
||||
result['limit'] = 90
|
||||
result['total'] = 999999
|
||||
return result
|
||||
|
||||
def detailContent(self, array):
|
||||
tid = array[0]
|
||||
url = 'https://www.czys.pro/movie/{0}.html'.format(tid)
|
||||
rsp = self.getCookie(url)
|
||||
root = self.html(self.cleanText(rsp.text))
|
||||
node = root.xpath("//div[@class='dyxingq']")[0]
|
||||
pic = node.xpath(".//div[@class='dyimg fl']/img/@src")[0]
|
||||
title = node.xpath('.//h1/text()')[0]
|
||||
detail = root.xpath(".//div[@class='yp_context']//p/text()")[0]
|
||||
vod = {
|
||||
"vod_id": tid,
|
||||
"vod_name": title,
|
||||
"vod_pic": pic,
|
||||
"type_name": "",
|
||||
"vod_year": "",
|
||||
"vod_area": "",
|
||||
"vod_remarks": "",
|
||||
"vod_actor": "",
|
||||
"vod_director": "",
|
||||
"vod_content": detail
|
||||
}
|
||||
infoArray = node.xpath(".//ul[@class='moviedteail_list']/li")
|
||||
for info in infoArray:
|
||||
content = info.xpath('string(.)')
|
||||
if content.startswith('地区'):
|
||||
tpyeare = ''
|
||||
for inf in info:
|
||||
tn = inf.text
|
||||
tpyeare = tpyeare + '/' + '{0}'.format(tn)
|
||||
vod['vod_area'] = tpyeare.strip('/')
|
||||
if content.startswith('年份'):
|
||||
vod['vod_year'] = content.replace("年份:", "")
|
||||
if content.startswith('主演'):
|
||||
tpyeact = ''
|
||||
for inf in info:
|
||||
tn = inf.text
|
||||
tpyeact = tpyeact + '/' + '{0}'.format(tn)
|
||||
vod['vod_actor'] = tpyeact.strip('/')
|
||||
if content.startswith('导演'):
|
||||
tpyedire = ''
|
||||
for inf in info:
|
||||
tn = inf.text
|
||||
tpyedire = tpyedire + '/' + '{0}'.format(tn)
|
||||
vod['vod_director'] = tpyedire.strip('/')
|
||||
vod_play_from = '$$$'
|
||||
playFrom = ['厂长']
|
||||
vod_play_from = vod_play_from.join(playFrom)
|
||||
vod_play_url = '$$$'
|
||||
playList = []
|
||||
vodList = root.xpath("//div[@class='paly_list_btn']")
|
||||
for vl in vodList:
|
||||
vodItems = []
|
||||
aList = vl.xpath('./a')
|
||||
for tA in aList:
|
||||
href = tA.xpath('./@href')[0]
|
||||
name = tA.xpath('./text()')[0].replace('\xa0', '')
|
||||
tId = self.regStr(href, '/v_play/(\\S+).html')
|
||||
vodItems.append(name + "$" + tId)
|
||||
joinStr = '#'
|
||||
joinStr = joinStr.join(vodItems)
|
||||
playList.append(joinStr)
|
||||
vod_play_url = vod_play_url.join(playList)
|
||||
|
||||
vod['vod_play_from'] = vod_play_from
|
||||
vod['vod_play_url'] = vod_play_url
|
||||
result = {
|
||||
'list': [
|
||||
vod
|
||||
]
|
||||
}
|
||||
return result
|
||||
|
||||
def searchContent(self, wd, quick=False, pg=1):
|
||||
url = 'https://www.czys.pro/daoyongjiekoshibushiyoubing?q={0}'.format(urllib.parse.quote(wd))
|
||||
rsp = self.getCookie(url)
|
||||
root = self.html(self.cleanText(rsp.text))
|
||||
vodList = root.xpath("//div[contains(@class,'mi_ne_kd')]/ul/li/a")
|
||||
videos = []
|
||||
for vod in vodList:
|
||||
name = vod.xpath('./img/@alt')[0]
|
||||
pic = vod.xpath('./img/@data-original')[0]
|
||||
href = vod.xpath('./@href')[0]
|
||||
tid = self.regStr(href, 'movie/(\\S+).html')
|
||||
res = vod.xpath('./div[@class="jidi"]/span/text()')
|
||||
if len(res) == 0:
|
||||
remark = '全1集'
|
||||
else:
|
||||
remark = vod.xpath('./div[@class="jidi"]/span/text()')[0]
|
||||
videos.append({
|
||||
"vod_id": tid,
|
||||
"vod_name": name,
|
||||
"vod_pic": pic,
|
||||
"vod_remarks": remark
|
||||
})
|
||||
result = {
|
||||
'list': videos
|
||||
}
|
||||
return result
|
||||
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {}
|
||||
}
|
||||
header = {
|
||||
"Referer": "https://www.czys.pro/",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36"
|
||||
}
|
||||
|
||||
def parseCBC(self, enc, key, iv):
|
||||
keyBytes = key.encode("utf-8")
|
||||
ivBytes = iv.encode("utf-8")
|
||||
cipher = AES.new(keyBytes, AES.MODE_CBC, ivBytes)
|
||||
msg = cipher.decrypt(enc)
|
||||
paddingLen = msg[len(msg) - 1]
|
||||
return msg[0:-paddingLen]
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
result = {}
|
||||
url = 'https://www.czys.pro/v_play/{0}.html'.format(id)
|
||||
rsp = self.getCookie(url)
|
||||
pat = '\\"([^\\"]+)\\";var [\\d\\w]+=function dncry.*md5.enc.Utf8.parse\\(\\"([\\d\\w]+)\\".*md5.enc.Utf8.parse\\(([\\d]+)\\)'
|
||||
html = rsp.text
|
||||
print(html)
|
||||
content = self.regStr(html, pat)
|
||||
if content == '':
|
||||
url = self.regStr(reg='<iframe.*?src=\"(.*?)\".*?</iframe>', src=html)
|
||||
config = self.fetch(url).text
|
||||
# jsp=jsoup()
|
||||
# url=jsp.pdfh(html, "body&&iframe&&src")
|
||||
# self.log(url)
|
||||
# config=jsp.pdfh(self.fetch(url).text,'body&&script&&Html')
|
||||
# self.log(config)
|
||||
player = self.regStr(reg='var rand = \"(.*?)\".*var player = \"(.*?)\"', src=config.replace('\n', ''),
|
||||
group=2)
|
||||
rand = self.regStr(reg='var rand = \"(.*?)\".*var player = \"(.*?)\"', src=config.replace('\n', ''),
|
||||
group=1)
|
||||
decontent = self.parseCBC(base64.b64decode(player), 'VFBTzdujpR9FWBhe', rand).decode()
|
||||
str3 = json.loads(decontent)['url']
|
||||
pars = 0
|
||||
header = ''
|
||||
else:
|
||||
key = self.regStr(html, pat, 2)
|
||||
iv = self.regStr(html, pat, 3)
|
||||
decontent = self.parseCBC(base64.b64decode(content), key, iv).decode()
|
||||
urlPat = 'video: \\{url: \\\"([^\\\"]+)\\\"'
|
||||
vttPat = 'subtitle: \\{url:\\\"([^\\\"]+\\.vtt)\\\"'
|
||||
str3 = self.regStr(decontent, urlPat)
|
||||
str4 = self.regStr(decontent, vttPat)
|
||||
self.loadVtt(str3)
|
||||
pars = 0
|
||||
header = ''
|
||||
if len(str4) > 0:
|
||||
result['subf'] = '/vtt/utf-8'
|
||||
result['subt'] = ''
|
||||
result = {
|
||||
'parse': pars,
|
||||
'playUrl': '',
|
||||
'url': str3,
|
||||
'header': header
|
||||
}
|
||||
return result
|
||||
|
||||
def loadVtt(self, url):
|
||||
pass
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def localProxy(self, param):
|
||||
action = {}
|
||||
return [200, "video/MP2T", action, ""]
|
||||
@@ -0,0 +1,480 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# File : 哔滴影视.py
|
||||
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
|
||||
# Author's Blog: https://blog.csdn.net/qq_32394351
|
||||
# Date : 2024/1/10
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
import requests
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
|
||||
from pathlib import Path
|
||||
import base64
|
||||
from cachetools import cached, TTLCache # 可以缓存curd的函数,指定里面的key
|
||||
|
||||
"""
|
||||
配置示例:
|
||||
t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾
|
||||
api里会自动含有ext参数是base64编码后的选中的筛选条件
|
||||
{
|
||||
"key":"hipy_t4_哔滴影视",
|
||||
"name":"哔滴影视(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/哔滴影视?api_ext={{host}}/txt/hipy/bidi.jar",
|
||||
"searchable":1,
|
||||
"quickSearch":0,
|
||||
"filterable":1,
|
||||
"ext":"{{host}}/files/hipy/jars/bidi.jar"
|
||||
},
|
||||
{
|
||||
"key": "hipy_t3_哔滴影视",
|
||||
"name": "哔滴影视(hipy_t3)",
|
||||
"type": 3,
|
||||
"api": "{{host}}/txt/hipy/哔滴影视.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 0,
|
||||
"filterable": 1,
|
||||
"ext": "{{host}}/files/hipy/jars/bidi.jar"
|
||||
},
|
||||
"""
|
||||
|
||||
|
||||
def envkey(self, url: str):
|
||||
return url
|
||||
|
||||
|
||||
# 全局变量
|
||||
gParam = {
|
||||
"inited": False,
|
||||
}
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
|
||||
api: str = 'https://www.yjys.me/api/v1'
|
||||
|
||||
javar = None
|
||||
|
||||
def getDependence(self):
|
||||
return ['base_java_loader']
|
||||
|
||||
def getName(self):
|
||||
return "哔滴影视"
|
||||
|
||||
@cached(cache=TTLCache(maxsize=3, ttl=3600), key=envkey)
|
||||
def get_init_api(self, url):
|
||||
try:
|
||||
print('get_init_api请求URL:', url)
|
||||
r = self.fetch(url)
|
||||
ret = None
|
||||
if r.status_code == 200:
|
||||
self.log(f'url:{url},文件体积:{len(r.content)}')
|
||||
ret = r.content
|
||||
return ret
|
||||
except Exception as e:
|
||||
print(f'get_init_api请求URL发生错误:{e}')
|
||||
return {}
|
||||
|
||||
def init_api_ext_file(self):
|
||||
"""
|
||||
这个函数用于初始化py文件对应的json文件,用于存筛选规则。
|
||||
执行此函数会自动生成筛选文件
|
||||
@return:
|
||||
"""
|
||||
pass
|
||||
|
||||
def init(self, extend=""):
|
||||
"""
|
||||
初始化加载extend,一般与py文件名同名的json文件作为扩展筛选
|
||||
@param extend:
|
||||
@return:
|
||||
"""
|
||||
global gParam
|
||||
ext = self.extend
|
||||
|
||||
if isinstance(ext, str) and ext:
|
||||
if ext.endswith('.jar'):
|
||||
jar_path = os.path.join(os.path.dirname(__file__), './jars')
|
||||
os.makedirs(jar_path, exist_ok=True)
|
||||
# jar_file = os.path.join(os.path.dirname(__file__), './jars/bdys.jar')
|
||||
jar_file = os.path.join(os.path.dirname(__file__), './jars/bidi.jar')
|
||||
jar_file = Path(jar_file).as_posix()
|
||||
need_down = False
|
||||
msg = ''
|
||||
if not gParam['inited'] and not os.path.exists(jar_file):
|
||||
need_down = True
|
||||
msg = f'未inited,且文件不存在。开始下载文件'
|
||||
elif gParam['inited'] and not os.path.exists(jar_file):
|
||||
need_down = True
|
||||
msg = f'已inited,但文件不存在。开始下载文件'
|
||||
# elif not gParam['inited'] and os.path.exists(jar_file):
|
||||
# need_down = True
|
||||
# msg = f'未inited,但文件已存在。重新下载文件'
|
||||
|
||||
if need_down:
|
||||
self.log(msg)
|
||||
if self.ENV.lower() == 't3':
|
||||
# ext = ext.replace('.jar', '.dex')
|
||||
pass
|
||||
content = self.get_init_api(ext)
|
||||
with open(jar_file, mode='wb+') as f:
|
||||
f.write(content)
|
||||
|
||||
# 装载模块,这里只要一个就够了
|
||||
if isinstance(extend, list):
|
||||
for lib in extend:
|
||||
if '.Spider' in str(type(lib)):
|
||||
self.javar = lib
|
||||
break
|
||||
|
||||
if self.javar:
|
||||
# jar_file = os.path.join(os.path.dirname(__file__), './jars/bdys.jar')
|
||||
jar_file = os.path.join(os.path.dirname(__file__), './jars/bidi.jar')
|
||||
jar_file = Path(jar_file).as_posix()
|
||||
self.javar.init_jar(jar_file)
|
||||
# self.class1 = self.javar.jClass('com.C4355b')
|
||||
self.token = str(self.javar.call_java('com.EncryptionUtils', 'getToken'))
|
||||
# self.class1 = self.javar.jClass('com.EncryptionUtils')
|
||||
# # class1 = self.class1() # 类实例化
|
||||
# class1 = self.class1
|
||||
# self.token = str(class1.getToken())
|
||||
# print(self.token)
|
||||
# # self.token = str(self.class1.getToken())
|
||||
self.headers.update({'token': self.token})
|
||||
|
||||
gParam['inited'] = True
|
||||
|
||||
def isVideo(self):
|
||||
"""
|
||||
返回是否为视频的匹配字符串
|
||||
@return: None空 reg:正则表达式 js:input js代码
|
||||
"""
|
||||
return 'js:input.includes(".m3u8)?true:false'
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filterable=False):
|
||||
"""
|
||||
获取首页分类及筛选数据
|
||||
@param filterable: 能否筛选,跟t3/t4配置里的filterable参数一致
|
||||
@return:
|
||||
"""
|
||||
class_name = '电影&电视剧&动漫&综艺' # 静态分类名称拼接
|
||||
class_url = '0&1001&21&35' # 静态分类标识拼接
|
||||
|
||||
result = {}
|
||||
classes = []
|
||||
|
||||
if all([class_name, class_url]):
|
||||
class_names = class_name.split('&')
|
||||
class_urls = class_url.split('&')
|
||||
cnt = min(len(class_urls), len(class_names))
|
||||
for i in range(cnt):
|
||||
classes.append({
|
||||
'type_name': class_names[i],
|
||||
'type_id': class_urls[i]
|
||||
})
|
||||
|
||||
result['class'] = classes
|
||||
if filterable:
|
||||
result['filters'] = self.config['filter']
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
"""
|
||||
首页推荐列表
|
||||
@return:
|
||||
"""
|
||||
d = []
|
||||
d.append({
|
||||
'vod_name': '测试',
|
||||
'vod_id': 'index.html',
|
||||
'vod_pic': 'https://gitee.com/CherishRx/imagewarehouse/raw/master/image/13096725fe56ce9cf643a0e4cd0c159c.gif',
|
||||
'vod_remarks': '原始hipy',
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filterable, extend):
|
||||
"""
|
||||
返回一级列表页数据
|
||||
@param tid: 分类id
|
||||
@param pg: 当前页数
|
||||
@param filterable: 能否筛选
|
||||
@param extend: 当前筛选数据
|
||||
@return:
|
||||
"""
|
||||
url = self.api + f'/category/{tid}/{pg}?type=0'
|
||||
r = self.fetch(url, headers=self.headers)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
page_count = 12 # 默认赋值一页列表12条数据|这个值一定要写正确看他默认一页多少条
|
||||
|
||||
d = [{
|
||||
'vod_name': vod['movieName'],
|
||||
'vod_id': vod['id'],
|
||||
'vod_pic': vod['cdnCover'],
|
||||
'vod_remarks': vod['rank'],
|
||||
'vod_content': vod['title'],
|
||||
} for vod in data['list']]
|
||||
result = {
|
||||
'list': d,
|
||||
'page': pg,
|
||||
'pagecount': 9999 if len(d) >= page_count else pg,
|
||||
'limit': 90,
|
||||
'total': 999999,
|
||||
}
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
"""
|
||||
返回二级详情页数据
|
||||
@param ids: 一级传过来的vod_id列表
|
||||
@return:
|
||||
"""
|
||||
vod_id = ids[0]
|
||||
url = self.api + f'/detail/{vod_id}'
|
||||
r = self.fetch(url, headers=self.headers)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(self.json2str(data))
|
||||
|
||||
vod = data['movie']
|
||||
playlist = data['playlist']
|
||||
titles = []
|
||||
plays = {}
|
||||
for p in playlist: # 选集列表
|
||||
title = p['title']
|
||||
titles.append(title)
|
||||
if not plays.get(title):
|
||||
plays[title] = []
|
||||
|
||||
_type = '1' if p.get('tosId') else '0'
|
||||
purl = self.api + '/playurl/' + str(p['id']) + '?type=' + _type
|
||||
plays[title].append({'name': '至尊线路', 'url': f'vip://{purl}'})
|
||||
|
||||
# if p.get('tosId'):
|
||||
# purl = self.api + '/playurl/' + str(p['id']) + '?type=' + str(p.get('tosId') or '0')
|
||||
# plays[title].append({'name': '至尊线路', 'url': f'vip://{purl}'})
|
||||
|
||||
if p.get('url'):
|
||||
for p0 in p['url'].split(','):
|
||||
plays[title].append(
|
||||
{'name': p0.split('#')[1] if len(p0.split('#')) > 1 else '道长线路', 'url': p0.split('#')[0]})
|
||||
|
||||
if p.get('url1'):
|
||||
for p1 in p['url1'].split(','):
|
||||
plays[title].append(
|
||||
{'name': p1.split('#')[1] if len(p1.split('#')) > 1 else '道长线路', 'url': p1.split('#')[0]})
|
||||
|
||||
if p.get('url2'):
|
||||
for p2 in p['url2'].split(','):
|
||||
plays[title].append(
|
||||
{'name': p2.split('#')[1] if len(p2.split('#')) > 1 else '道长线路', 'url': p2.split('#')[0]})
|
||||
|
||||
tabs = {}
|
||||
# key 选集列表 value是线路列表
|
||||
for key, value in plays.items():
|
||||
for tab in value:
|
||||
if not tab['name'] in tabs:
|
||||
tabs[tab['name']] = []
|
||||
|
||||
tabs[tab['name']].append(f"{key}${tab['url']}")
|
||||
|
||||
vod_play_from = '$$$'.join(tabs.keys())
|
||||
|
||||
vod_play_urls = []
|
||||
for key, value in tabs.items():
|
||||
vod_play_urls.append('#'.join(value))
|
||||
vod_play_url = '$$$'.join(vod_play_urls)
|
||||
|
||||
vod = {"vod_id": vod_id,
|
||||
"vod_name": vod['title'],
|
||||
"vod_pic": vod['cdnCover'],
|
||||
"type_name": ','.join(vod['m_type']),
|
||||
"vod_year": '',
|
||||
"vod_area": vod['area'],
|
||||
"vod_remarks": f"{vod['movieName']} {vod['rank']}",
|
||||
"vod_actor": ','.join(vod['m_performer']),
|
||||
"vod_director": ','.join(vod['m_director']),
|
||||
"vod_content": vod['intro'],
|
||||
"vod_play_from": vod_play_from,
|
||||
"vod_play_url": vod_play_url}
|
||||
result = {
|
||||
'list': [vod]
|
||||
}
|
||||
return result
|
||||
|
||||
def searchContent(self, wd, quick=False, pg=1):
|
||||
"""
|
||||
返回搜索列表
|
||||
@param wd: 搜索关键词
|
||||
@param quick: 是否来自快速搜索。t3/t4配置里启用了快速搜索,在快速搜索在执行才会是True
|
||||
@return:
|
||||
"""
|
||||
url = self.api + f'/search/{wd}/{pg}'
|
||||
r = self.fetch(url, headers=self.headers)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
d = []
|
||||
for li in data['list']:
|
||||
d.append({
|
||||
'vod_name': li['movieName'],
|
||||
'vod_id': li['id'],
|
||||
'vod_pic': li['cdnCover'],
|
||||
'vod_remarks': li['curEp'],
|
||||
'vod_content': li['intro'],
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
# print(result)
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
"""
|
||||
解析播放,返回json。壳子视情况播放直链或进行嗅探
|
||||
@param flag: vod_play_from 播放来源线路
|
||||
@param id: vod_play_url 播放的链接
|
||||
@param vipFlags: vip标识
|
||||
@return:
|
||||
"""
|
||||
url = str(id)
|
||||
# 至尊线路
|
||||
if url.startswith('vip://'):
|
||||
purl = url.split('vip://')[1]
|
||||
# print(purl)
|
||||
r = self.fetch(purl, headers=self.headers)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
url = data.get('url') or ''
|
||||
if not url:
|
||||
self.log(data)
|
||||
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1'
|
||||
}
|
||||
parse = 0
|
||||
if 'm3u8' in url:
|
||||
proxyUrl = self.getProxyUrl()
|
||||
if proxyUrl:
|
||||
url = proxyUrl + '&url=' + url + '&name=1.m3u8'
|
||||
elif '/obj/' in url:
|
||||
headers.update({
|
||||
'Cookie': 'm=1',
|
||||
'app': '1',
|
||||
'Referer': 'https://doc.weixin.qq.com/',
|
||||
})
|
||||
result = {
|
||||
'parse': parse, # 1=嗅探,0=播放
|
||||
'playUrl': '', # 解析链接
|
||||
'url': url, # 直链或待嗅探地址
|
||||
'header': headers, # 播放UA
|
||||
}
|
||||
|
||||
# print(result)
|
||||
return result
|
||||
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {}
|
||||
}
|
||||
headers = {
|
||||
"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 7.0; HUAWEI MLA-AL10 Build/HUAWEIMLA-AL10)",
|
||||
"token": ""
|
||||
}
|
||||
|
||||
def localProxy(self, params):
|
||||
# print(params)
|
||||
url = params.get('url')
|
||||
if not url:
|
||||
# return [302, 'text/html', None, {'location': 'https://www.baidu.com'}]
|
||||
# return [404, 'text/plain', 'Not Found']
|
||||
return [403, 'text/plain', '403 forbidden. url is required']
|
||||
|
||||
name = params.get('name') or 'm3u8'
|
||||
burl = 'https://www.yjys.me'
|
||||
new_url = url.replace("www.bde4.cc", "www.yjys.me")
|
||||
self.log(f'原始url:{url},替换域名后url:{new_url}')
|
||||
headers = {
|
||||
"User-Agent": "BDPlayer",
|
||||
"Referer": burl,
|
||||
"Origin": burl,
|
||||
}
|
||||
r = self.fetch(new_url, headers=headers)
|
||||
pdata = self.process_data(r.content).decode('utf-8')
|
||||
# pdata = re.sub(r'(.*?ts)', r'https://www.yjys.me/\1', pdata)
|
||||
pdata = self.replaceAll(pdata, r'(.*?ts)', r'https://vod.bdys.me/\1')
|
||||
content = pdata.strip()
|
||||
|
||||
media_type = 'text/plain' if 'txt' in name else 'video/MP2T'
|
||||
return [200, media_type, content]
|
||||
|
||||
# -----------------------------------------------自定义函数-----------------------------------------------
|
||||
def decode(self, text):
|
||||
bt = base64.b64decode(text)
|
||||
# self.log(self.headers)
|
||||
if self.ENV.lower() == 't3':
|
||||
bt = self.javar.jarBytes(bt)
|
||||
res = self.javar.call_java('com.EncryptionUtils', 'dec', bt)
|
||||
# res = self.class1.dec(bt)
|
||||
# print(str(res))
|
||||
return self.str2json(str(res)) if res else None
|
||||
|
||||
def process_data(self, req_bytes):
|
||||
"""
|
||||
个性化方法:跳过req返回的content 3354之前的字节并进行gzip解压
|
||||
@param req_bytes:
|
||||
@return:
|
||||
"""
|
||||
stream = self.skip_bytes(req_bytes, 3354)
|
||||
decrypted_data = self.gzipCompress(stream)
|
||||
return decrypted_data
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from t4.core.loader import t4_spider_init
|
||||
|
||||
spider = Spider()
|
||||
t4_spider_init(spider)
|
||||
print(spider.ENV)
|
||||
# spider.init_api_ext_file() # 生成筛选对应的json文件
|
||||
# spider.log({'key': 'value'})
|
||||
# spider.log('====文本内容====')
|
||||
# print(spider.homeContent(True))
|
||||
# print(spider.homeVideoContent())
|
||||
# r = requests.head(
|
||||
# 'http://192.168.31.49:5707/api/v1/vod/%E5%93%94%E6%BB%B4%E5%BD%B1%E8%A7%86?proxy=1&do=py&url=https://www.bde4.cc/10E79044B82A84F70BE1308FFA5232E4DC3D0CA9EC2BF6B1D4EF56B2CE5B67CF238965CCAE17F859665B7E166720986D.m3u8')
|
||||
# print(r.headers, r.content)
|
||||
# r = requests.get('https://www.bdys10.com/obj/63BEE3B148E464F16EE62435C53087B994902679D844EA9CC3615658CF55E01D',
|
||||
# headers={
|
||||
# 'Cookie': 'm=1',
|
||||
# 'app': '1',
|
||||
# 'Referer': 'https://doc.weixin.qq.com/',
|
||||
# })
|
||||
# print(r.text)
|
||||
# print(spider.categoryContent('0', 1, False, None))
|
||||
# print(spider.detailContent([24420]))
|
||||
print(spider.searchContent('斗罗大陆'))
|
||||
# print(spider.playerContent('至尊线路', 'vip://https://www.yjys.me/api/v1/playurl/174296?type=1', None))
|
||||
# print(spider.playerContent('需要解析',
|
||||
# 'https://www.bde4.cc/10E79044B82A84F70BE1308FFA5232E4DC3D0CA9EC2BF6B1D4EF56B2CE5B67CF238965CCAE17F859665B7E166720986D.m3u8',
|
||||
# None))
|
||||
@@ -0,0 +1,398 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# File : 喵次元.py
|
||||
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
|
||||
# Author's Blog: https://blog.csdn.net/qq_32394351
|
||||
# Date : 2024/1/17
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
|
||||
"""
|
||||
配置示例:
|
||||
t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾
|
||||
api里会自动含有ext参数是base64编码后的选中的筛选条件
|
||||
{
|
||||
"key":"hipy_t4_喵次元",
|
||||
"name":"喵次元(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/喵次元",
|
||||
"searchable":1,
|
||||
"quickSearch":0,
|
||||
"filterable":1,
|
||||
"ext":""
|
||||
},
|
||||
{
|
||||
"key": "hipy_t3_喵次元",
|
||||
"name": "喵次元(hipy_t3)",
|
||||
"type": 3,
|
||||
"api": "{{host}}/txt/hipy/喵次元.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 0,
|
||||
"filterable": 1,
|
||||
"ext": ""
|
||||
},
|
||||
"""
|
||||
|
||||
# 全局变量
|
||||
gParam = {
|
||||
"HomeDict": {},
|
||||
"TypeDict": {},
|
||||
}
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
key: str = 'sLunqcoH85Nm/jDmFKns7A== '
|
||||
key_str: str = 'sLunqcoH85Nm/jDmFKns7A=='
|
||||
iv: str = 'fedcba9876543210'
|
||||
token: str = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBcHBUbyIsImlhdCI6MTcwMDA3MTcwMiwiZXhwIjoxNzMxNjA3NzAyLCJuYmYiOjE3MDAwNzE3MDIsInN1YiI6IkFwcFRvIiwianRpIjoiYzRjNTAzOTQxYTM4NWI1MDMyMTAyYmY3Yzk1OGY4MzEiLCJkYXRhIjp7InVzZXJfaWQiOjI0ODc1NCwidXNlcl9jaGVjayI6ImUzYmQ3NmNhNTJhMGY4NjAwMTdjNjdkZGUwN2QzZTM3IiwidXNlcl9uYW1lIjoiaGV6aWh1aSJ9fQ.4LWs3rNL-os8_Pqa9LgKtvVG5f0aIxVyAjYIagvO1F4'
|
||||
ic: str = 'bmXes2xsCWvsSdfYav0s9D78Ly7w1o%2BOYXApKx6SUd4NWKsTsapbS52l7y%2FsTVCM2kcoLws2jryaDQlHLse5fxD2B2VXZXfaQo0eMTOv2Xq7CKoPa51uVt8WiIY2SPztc7wxGE89%2Fcw2Q3n85uUT3A%3D%3D'
|
||||
api: str = 'https://cym.zhui.la/api.php'
|
||||
api_cofig: str = api + '/type/get_list'
|
||||
api_home: str = api + '/video/index'
|
||||
api_cate: str = api + '/video/get_list'
|
||||
api_search: str = api + '/video/get_list'
|
||||
api_detail: str = api + '/video/get_detail'
|
||||
api_tabs: str = api + '/video/get_player'
|
||||
api_parse: str = api + '/video/get_definition'
|
||||
params: dict = {"versionName": "5.6.9", "uuid": "9cc01079c64e2495", "version": "4835d0a2", "versionCode": "35"}
|
||||
|
||||
def getName(self):
|
||||
return "喵次元"
|
||||
|
||||
def init(self, extend=""):
|
||||
"""
|
||||
初始化加载extend,一般与py文件名同名的json文件作为扩展筛选
|
||||
@param extend:
|
||||
@return:
|
||||
"""
|
||||
ext = self.extend
|
||||
self.log(f'ext:{ext}')
|
||||
key = self.key_str
|
||||
# 转hex
|
||||
key_hex_str = self.bytesToHexString(key.encode('utf-8'))
|
||||
# 右侧补16个0
|
||||
key_hex_str += '0' * 16
|
||||
key_hex = key_hex_str
|
||||
# key_hex = '734C756E71636F4838354E6D2F6A446D464B6E7337413D3D0000000000000000'
|
||||
# 转回来
|
||||
key = self.hexStringTobytes(key_hex).decode('utf-8')
|
||||
self.key = key
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filterable=False):
|
||||
"""
|
||||
获取首页分类及筛选数据
|
||||
@param filterable: 能否筛选,跟t3/t4配置里的filterable参数一致
|
||||
@return:
|
||||
"""
|
||||
filter_names = {
|
||||
'class': '分类',
|
||||
'area': '地区',
|
||||
'lang': '语言',
|
||||
'year': '年份',
|
||||
'star': '明星',
|
||||
'director': '导演',
|
||||
'state': '状态',
|
||||
'version': '版本',
|
||||
}
|
||||
ret = self.fetch(self.api_cofig).json()
|
||||
data = self.decode(ret['data'])
|
||||
result = {}
|
||||
classes = []
|
||||
filters = {}
|
||||
type_dict = {}
|
||||
for tp in data.get('list') or []:
|
||||
classes.append({
|
||||
'type_name': tp['type_name'],
|
||||
'type_id': tp['type_id']
|
||||
})
|
||||
type_dict[str(tp['type_id'])] = tp['type_name']
|
||||
tp_filters = []
|
||||
for key, value in tp['type_extend'].items():
|
||||
if value:
|
||||
tp_filters.append({
|
||||
'key': key,
|
||||
'name': filter_names.get(key) or key,
|
||||
'value': [{'n': '全部', 'v': ''}] + [{'n': i, 'v': i} for i in value.split(',') if i]
|
||||
})
|
||||
filters[tp['type_id']] = tp_filters
|
||||
|
||||
result['class'] = classes
|
||||
if filterable:
|
||||
result['filters'] = filters
|
||||
global gParam
|
||||
gParam['HomeDict'].update(result)
|
||||
gParam['TypeDict'].update(type_dict)
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
"""
|
||||
首页推荐列表
|
||||
@return:
|
||||
"""
|
||||
ret = self.fetch(self.api_home).json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
d = []
|
||||
for cate_data in data:
|
||||
items = cate_data['video']
|
||||
for item in items:
|
||||
d.append({
|
||||
'vod_name': item['vod_name'],
|
||||
'vod_id': item['vod_id'],
|
||||
'vod_pic': item['vod_pic'],
|
||||
'vod_remarks': item['vod_remarks'],
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filterable, extend):
|
||||
"""
|
||||
返回一级列表页数据
|
||||
@param tid: 分类id
|
||||
@param pg: 当前页数
|
||||
@param filterable: 能否筛选
|
||||
@param extend: 当前筛选数据
|
||||
@return:
|
||||
"""
|
||||
page_count = 21 # 默认赋值一页列表21条数据|这个值一定要写正确看他默认一页多少条
|
||||
fls = extend.keys() # 哪些刷新数据
|
||||
new_params = self.params.copy()
|
||||
new_params.update({'type_id': str(tid), 'limit': str(page_count), 'page': str(pg),
|
||||
'orderby': '', 'ctime': str(int(time.time()))
|
||||
})
|
||||
for fl in fls:
|
||||
new_params[f'vod_{fl}'] = extend[fl]
|
||||
|
||||
params = self.get_sign_params(new_params)
|
||||
# print(params)
|
||||
r = self.postJson(self.api_cate, json=params)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
d = data['list']
|
||||
result = {
|
||||
'list': d,
|
||||
'page': pg,
|
||||
'pagecount': 9999 if len(d) >= page_count else pg,
|
||||
'limit': 90,
|
||||
'total': data['count'],
|
||||
}
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
"""
|
||||
返回二级详情页数据
|
||||
@param ids: 一级传过来的vod_id列表
|
||||
@return:
|
||||
"""
|
||||
# id=110102
|
||||
vod_id = ids[0]
|
||||
new_params = self.params.copy()
|
||||
new_params.update({'vod_id': str(vod_id), 'ctime': str(int(time.time()))})
|
||||
params = self.get_sign_params(new_params)
|
||||
# print(params)
|
||||
r = self.postJson(self.api_detail, json=params)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
vod = {"vod_id": vod_id,
|
||||
"vod_name": data['vod_name'],
|
||||
"vod_pic": data['vod_pic'],
|
||||
"type_name": data['vod_en'],
|
||||
"vod_year": data['vod_year'],
|
||||
"vod_area": data['vod_area'],
|
||||
"vod_remarks": data['vod_remarks'],
|
||||
"vod_actor": data['vod_actor'],
|
||||
"vod_director": data['vod_director'],
|
||||
"vod_content": data['vod_blurb'],
|
||||
}
|
||||
episodes = data['player']
|
||||
play_map = {}
|
||||
play_from = []
|
||||
play_list = []
|
||||
for ep in episodes:
|
||||
player = ep["code"]
|
||||
source = ep["name"]
|
||||
new_params = self.params.copy()
|
||||
new_params.update({
|
||||
'vod_id': str(vod_id), 'ctime': str(int(time.time())),
|
||||
'limit': str(5000), 'page': str(1),
|
||||
'player': player,
|
||||
})
|
||||
params = self.get_sign_params(new_params)
|
||||
r = self.postJson(self.api_tabs, json=params)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
for playurl in data['list']:
|
||||
if source not in play_map:
|
||||
play_map[source] = []
|
||||
play_map[source].append(
|
||||
playurl["drama"] + "$" + '&'.join(
|
||||
[str(playurl["ju_id"]), str(playurl["plyer"]), str(playurl["video_id"])]))
|
||||
|
||||
for key, value in play_map.items():
|
||||
play_from.append(key)
|
||||
play_list.append('#'.join(value))
|
||||
|
||||
vod['vod_play_from'] = '$$$'.join(play_from)
|
||||
vod['vod_play_url'] = '$$$'.join(play_list)
|
||||
result = {
|
||||
'list': [vod]
|
||||
}
|
||||
# print(vod)
|
||||
return result
|
||||
|
||||
def searchContent(self, wd, quick=False, pg=1):
|
||||
"""
|
||||
返回搜索列表
|
||||
@param wd: 搜索关键词
|
||||
@param quick: 是否来自快速搜索。t3/t4配置里启用了快速搜索,在快速搜索在执行才会是True
|
||||
@param pg: 页数
|
||||
@return:
|
||||
"""
|
||||
page_count = 21 # 默认赋值一页列表21条数据|这个值一定要写正确看他默认一页多少条
|
||||
new_params = self.params.copy()
|
||||
new_params.update({
|
||||
'orderby': 'up', 'ctime': str(int(time.time())),
|
||||
'limit': str(page_count), 'page': str(pg), 'vod_name': str(wd)
|
||||
})
|
||||
params = self.get_sign_params(new_params)
|
||||
# print(params)
|
||||
r = self.postJson(self.api_cate, json=params)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
d = data['list']
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
"""
|
||||
解析播放,返回json。壳子视情况播放直链或进行嗅探
|
||||
@param flag: vod_play_from 播放来源线路
|
||||
@param id: vod_play_url 播放的链接
|
||||
@param vipFlags: vip标识
|
||||
@return:
|
||||
"""
|
||||
_v = id.split('&')
|
||||
ju_id = _v[0]
|
||||
plyer = _v[1]
|
||||
video_id = _v[2]
|
||||
new_params = self.params.copy()
|
||||
new_params.update({
|
||||
'player_id': str(plyer), 'ctime': str(int(time.time())),
|
||||
'ju_id': str(ju_id), 'vod_id': str(video_id)
|
||||
})
|
||||
params = self.get_sign_params(new_params)
|
||||
# print(params)
|
||||
r = self.postJson(self.api_parse, json=params)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
# 列表里第1条的分辨率最高
|
||||
url = data[0]['url']
|
||||
# print(url)
|
||||
|
||||
"""
|
||||
|
||||
# 原始key
|
||||
key = 'sLunqcoH85Nm/jDmFKns7A=='
|
||||
# 转hex
|
||||
key_hex_str = self.bytesToHexString(key.encode('utf-8')).replace(' ', '')
|
||||
# 右侧补16个0
|
||||
key_hex_str += '0'*16
|
||||
key_hex = key_hex_str
|
||||
# key_hex = '734C756E71636F4838354E6D2F6A446D464B6E7337413D3D0000000000000000'
|
||||
# 转回来
|
||||
key = self.hexStringTobytes(key_hex).decode('utf-8')
|
||||
# print(key)
|
||||
iv = 'fedcba9876543210'
|
||||
|
||||
"""
|
||||
|
||||
# key = self.key
|
||||
# iv = self.iv
|
||||
# input = self.aes_cbc_decode(url,key,iv)
|
||||
|
||||
input = self.decode_aes(url)
|
||||
parse = 0
|
||||
result = {
|
||||
'parse': parse, # 1=嗅探,0=播放
|
||||
'playUrl': '', # 解析链接
|
||||
'url': input, # 直链或待嗅探地址
|
||||
# 'header': headers, # 播放UA
|
||||
}
|
||||
return result
|
||||
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {}
|
||||
}
|
||||
header = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
|
||||
"Host": "www.baidu.com",
|
||||
"Referer": "https://www.baidu.com/"
|
||||
}
|
||||
|
||||
def localProxy(self, params):
|
||||
return [200, "video/MP2T", ""]
|
||||
|
||||
# -----------------------------------------------自定义函数-----------------------------------------------
|
||||
def get_sign_params(self, params: dict):
|
||||
keys = list(params.keys())
|
||||
keys.sort()
|
||||
str_list = []
|
||||
for key in keys:
|
||||
if params.get(key):
|
||||
str_list.append(params[key])
|
||||
str_list.append('alskeuscli')
|
||||
sign = self.md5(''.join(str_list))
|
||||
params['sign'] = sign
|
||||
return params
|
||||
|
||||
def decode(self, text):
|
||||
return text
|
||||
# return self.str2json(self.aes_cbc_decode(text, self.key, self.iv))
|
||||
|
||||
def decode_aes(self, text):
|
||||
key = self.key
|
||||
iv = self.iv
|
||||
input = self.aes_cbc_decode(text, key, iv)
|
||||
return input
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 在线aes测试 https://config.net.cn/tools/AES.html
|
||||
# 分类页:http://60.204.185.245:7090/appto/v1/home/cateData?id=1
|
||||
# 推荐页:http://60.204.185.245:7090/appto/v1/config/get?p=android
|
||||
from t4.core.loader import t4_spider_init
|
||||
|
||||
spider = Spider()
|
||||
t4_spider_init(spider)
|
||||
# spider.init_api_ext_file() # 生成筛选对应的json文件
|
||||
|
||||
# print(spider.homeContent(True))
|
||||
# print(spider.homeVideoContent())
|
||||
# print(spider.categoryContent('23', 1, True, {'year': '2024'}))
|
||||
# print(spider.detailContent([7533]))
|
||||
# print(spider.searchContent('斗罗大陆'))
|
||||
print(spider.playerContent('线路J', '1&duoduan&7533', None))
|
||||
print(spider.playerContent('线路Z', '1&ziru&7533', None))
|
||||
@@ -0,0 +1,425 @@
|
||||
# coding=utf-8
|
||||
# !/usr/bin/python
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
import json
|
||||
import re
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
def getName(self):
|
||||
return "在线之家"
|
||||
|
||||
def init(self, extend=""):
|
||||
print("============{0}============".format(extend))
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
cateManual = {
|
||||
"电影": "1",
|
||||
"美剧": "2",
|
||||
"韩剧": "3",
|
||||
"日剧": "4",
|
||||
"泰剧": "5",
|
||||
"动漫": "6"
|
||||
}
|
||||
classes = []
|
||||
for k in cateManual:
|
||||
classes.append({
|
||||
'type_name': k,
|
||||
'type_id': cateManual[k]
|
||||
})
|
||||
|
||||
result['class'] = classes
|
||||
if (filter):
|
||||
result['filters'] = self.config['filter']
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
rsp = self.fetch("https://www.zxzjhd.com/")
|
||||
root = self.html(rsp.text)
|
||||
aList = root.xpath("//div[@class='stui-vodlist__box']/a")
|
||||
|
||||
videos = []
|
||||
for a in aList:
|
||||
name = a.xpath('./@title')[0]
|
||||
pic = a.xpath('./@data-original')[0]
|
||||
mark = a.xpath("./span[@class='pic-text text-right']/text()")[0]
|
||||
sid = a.xpath("./@href")[0]
|
||||
sid = self.regStr(sid, "/detail/(\\S+).html")
|
||||
videos.append({
|
||||
"vod_id": sid,
|
||||
"vod_name": name,
|
||||
"vod_pic": pic,
|
||||
"vod_remarks": mark
|
||||
})
|
||||
result = {
|
||||
'list': videos
|
||||
}
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
result = {}
|
||||
if 'id' not in extend.keys():
|
||||
extend['id'] = tid
|
||||
extend['page'] = pg
|
||||
filterParams = ["id", "area", "by", "class", "lang", "", "", "", "page", "", "", "year"]
|
||||
params = ["", "", "", "", "", "", "", "", "", "", "", ""]
|
||||
for idx in range(len(filterParams)):
|
||||
fp = filterParams[idx]
|
||||
if fp in extend.keys():
|
||||
params[idx] = str(extend[fp])
|
||||
suffix = '-'.join(params)
|
||||
url = 'https://www.zxzjhd.com/vodshow/{0}.html'.format(suffix)
|
||||
rsp = self.fetch(url)
|
||||
root = self.html(rsp.text)
|
||||
aList = root.xpath("//div[@class='stui-vodlist__box']/a")
|
||||
videos = []
|
||||
for a in aList:
|
||||
name = a.xpath('./@title')[0]
|
||||
pic = a.xpath('./@data-original')[0]
|
||||
mark = a.xpath("./span[@class='pic-text text-right']/text()")[0]
|
||||
sid = a.xpath("./@href")[0]
|
||||
sid = self.regStr(sid, "/detail/(\\S+).html")
|
||||
videos.append({
|
||||
"vod_id": sid,
|
||||
"vod_name": name,
|
||||
"vod_pic": pic,
|
||||
"vod_remarks": mark
|
||||
})
|
||||
|
||||
result['list'] = videos
|
||||
result['page'] = pg
|
||||
result['pagecount'] = 9999
|
||||
result['limit'] = 90
|
||||
result['total'] = 999999
|
||||
return result
|
||||
|
||||
def detailContent(self, array):
|
||||
tid = array[0]
|
||||
url = 'https://www.zxzjhd.com/detail/{0}.html'.format(tid)
|
||||
rsp = self.fetch(url)
|
||||
root = self.html(rsp.text)
|
||||
node = root.xpath("//div[@class='stui-content']")[0]
|
||||
|
||||
pic = node.xpath(".//img/@data-original")[0]
|
||||
title = node.xpath('.//h1/text()')[0]
|
||||
detail = node.xpath(".//span[@class='detail-content']/text()")[0]
|
||||
|
||||
vod = {
|
||||
"vod_id": tid,
|
||||
"vod_name": title,
|
||||
"vod_pic": pic,
|
||||
"type_name": "",
|
||||
"vod_year": "",
|
||||
"vod_area": "",
|
||||
"vod_remarks": "",
|
||||
"vod_actor": "",
|
||||
"vod_director": "",
|
||||
"vod_content": detail
|
||||
}
|
||||
|
||||
infoArray = node.xpath(".//div[@class='stui-content__detail']/p")
|
||||
for info in infoArray:
|
||||
content = info.xpath('string(.)')
|
||||
if content.startswith('类型'):
|
||||
vod['type_name'] = content
|
||||
# if content.startswith('年份'):
|
||||
# vod['vod_year'] = content
|
||||
# if content.startswith('地区'):
|
||||
# vod['vod_area'] = content
|
||||
# if content.startswith('更新'):
|
||||
# vod['vod_remarks'] = content.replace('\n','').replace('\t','')
|
||||
if content.startswith('主演'):
|
||||
vod['vod_actor'] = content.replace('\n', '').replace('\t', '')
|
||||
if content.startswith('导演'):
|
||||
vod['vod_director'] = content.replace('\n', '').replace('\t', '')
|
||||
# if content.startswith('剧情'):
|
||||
# vod['vod_content'] = content.replace('\n','').replace('\t','')
|
||||
|
||||
vod_play_from = '$$$'
|
||||
playFrom = []
|
||||
vodHeader = root.xpath("//div[@class='stui-vodlist__head']/h3/text()")
|
||||
for v in vodHeader:
|
||||
playFrom.append(v)
|
||||
vod_play_from = vod_play_from.join(playFrom)
|
||||
|
||||
vod_play_url = '$$$'
|
||||
playList = []
|
||||
vodList = root.xpath("//ul[contains(@class,'stui-content__playlist')]")
|
||||
for vl in vodList:
|
||||
vodItems = []
|
||||
aList = vl.xpath('./li/a')
|
||||
for tA in aList:
|
||||
href = tA.xpath('./@href')[0]
|
||||
name = tA.xpath('./text()')[0]
|
||||
tId = self.regStr(href, '/video/(\\S+).html')
|
||||
vodItems.append(name + "$" + tId)
|
||||
joinStr = '#'
|
||||
joinStr = joinStr.join(vodItems)
|
||||
playList.append(joinStr)
|
||||
vod_play_url = vod_play_url.join(playList)
|
||||
|
||||
vod['vod_play_from'] = vod_play_from
|
||||
vod['vod_play_url'] = vod_play_url
|
||||
|
||||
result = {
|
||||
'list': [
|
||||
vod
|
||||
]
|
||||
}
|
||||
return result
|
||||
|
||||
def searchContent(self, wd, quick=False, pg=1):
|
||||
url = 'https://www.zxzjhd.com/index.php/ajax/suggest?mid=1&wd={0}'.format(wd)
|
||||
# getHeader()
|
||||
rsp = self.fetch(url)
|
||||
jo = json.loads(rsp.text)
|
||||
result = {}
|
||||
jArray = []
|
||||
if int(jo['total']) > 0:
|
||||
for j in jo['list']:
|
||||
jArray.append({
|
||||
"vod_id": j['id'],
|
||||
"vod_name": j['name'],
|
||||
"vod_pic": j['pic'],
|
||||
"vod_remarks": ""
|
||||
})
|
||||
result = {
|
||||
'list': jArray
|
||||
}
|
||||
return result
|
||||
|
||||
config = {
|
||||
"player": {
|
||||
"dpp": {
|
||||
"sh": "DP播放",
|
||||
"pu": "https://jx.zxzj.vip/dplayer.php?url=",
|
||||
"sn": 1,
|
||||
"or": 999
|
||||
}
|
||||
},
|
||||
"filter": {"1": [{"key": "class", "name": "剧情",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "喜剧", "v": "喜剧"}, {"n": "爱情", "v": "爱情"},
|
||||
{"n": "恐怖", "v": "恐怖"}, {"n": "动作", "v": "动作"}, {"n": "科幻", "v": "科幻"},
|
||||
{"n": "剧情", "v": "剧情"}, {"n": "战争", "v": "战争"}, {"n": "警匪", "v": "警匪"},
|
||||
{"n": "犯罪", "v": "犯罪"}, {"n": "动画", "v": "动画"}, {"n": "奇幻", "v": "奇幻"},
|
||||
{"n": "冒险", "v": "冒险"}, {"n": "恐怖", "v": "恐怖"}, {"n": "悬疑", "v": "悬疑"},
|
||||
{"n": "惊悚", "v": "惊悚"}, {"n": "青春", "v": "青春"},
|
||||
{"n": "情色", "v": "情色"}]}, {"key": "area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "大陆", "v": "大陆"},
|
||||
{"n": "香港", "v": "香港"},
|
||||
{"n": "台湾", "v": "台湾"},
|
||||
{"n": "欧美", "v": "欧美"},
|
||||
{"n": "韩国", "v": "韩国"},
|
||||
{"n": "日本", "v": "日本"},
|
||||
{"n": "泰国", "v": "泰国"},
|
||||
{"n": "印度", "v": "印度"},
|
||||
{"n": "俄罗斯", "v": "俄罗斯"},
|
||||
{"n": "其他", "v": "其他"}]},
|
||||
{"key": "year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"},
|
||||
{"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}, {"n": "2006", "v": "2006"},
|
||||
{"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"}, {"n": "2003", "v": "2003"},
|
||||
{"n": "2002", "v": "2002"}, {"n": "2001", "v": "2001"},
|
||||
{"n": "2000", "v": "2000"}]}, {"key": "lang", "name": "语言",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "英语", "v": "英语"},
|
||||
{"n": "韩语", "v": "韩语"},
|
||||
{"n": "日语", "v": "日语"},
|
||||
{"n": "法语", "v": "法语"},
|
||||
{"n": "泰语", "v": "泰语"},
|
||||
{"n": "德语", "v": "德语"},
|
||||
{"n": "印度语", "v": "印度语"},
|
||||
{"n": "国语", "v": "国语"},
|
||||
{"n": "粤 语", "v": "粤语"},
|
||||
{"n": "俄语", "v": "俄语"},
|
||||
{"n": "西班牙语", "v": "西班牙语"},
|
||||
{"n": "意大利语", "v": "意大利语"},
|
||||
{"n": "其它", "v": "其它"}]},
|
||||
{"key": "by", "name": "排序", "value": [{"n": "最新", "v": "time"}, {"n": "最热", "v": "hits"},
|
||||
{"n": "评分", "v": "score"}]}], "2": [
|
||||
{"key": "class", "name": "剧情",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "剧 情", "v": "剧情"}, {"n": "喜剧", "v": "喜剧"},
|
||||
{"n": "爱情", "v": "爱情"}, {"n": "动作", "v": "动作"}, {"n": "悬疑", "v": "悬疑"},
|
||||
{"n": "恐怖", "v": "恐怖"}, {"n": "奇幻", "v": "奇幻"}, {"n": "惊悚", "v": "惊悚"},
|
||||
{"n": "犯罪", "v": "犯罪"}, {"n": "科幻", "v": "科幻"}, {"n": "音乐", "v": "音乐"},
|
||||
{"n": "其他", "v": "其他"}]}, {"key": "year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"},
|
||||
{"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"},
|
||||
{"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"},
|
||||
{"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"},
|
||||
{"n": "2006", "v": "2006"}, {"n": "2005", "v": "2005"},
|
||||
{"n": "2004", "v": "2004"}]},
|
||||
{"key": "lang", "name": "语言",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "英语", "v": "英语"}, {"n": "法语", "v": "法语"}]},
|
||||
{"key": "by", "name": "排序",
|
||||
"value": [{"n": "最新", "v": "time"}, {"n": "最热", "v": "hits"}, {"n": "评分", "v": "score"}]}], "3": [
|
||||
{"key": "class", "name": "剧情",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "剧情", "v": "剧情"}, {"n": "喜剧", "v": "喜剧"},
|
||||
{"n": "爱情", "v": "爱情"}, {"n": "动 作", "v": "动作"}, {"n": "悬疑", "v": "悬疑"},
|
||||
{"n": "恐怖", "v": "恐怖"}, {"n": "奇幻", "v": "奇幻"}, {"n": "惊悚", "v": "惊悚"},
|
||||
{"n": "犯罪", "v": "犯罪"}, {"n": "科幻", "v": "科幻"}, {"n": "音乐", "v": "音乐"},
|
||||
{"n": "其他", "v": "其他"}]}, {"key": "year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"},
|
||||
{"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"},
|
||||
{"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"},
|
||||
{"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"},
|
||||
{"n": "2007", "v": "2007"}, {"n": "2006", "v": "2006"},
|
||||
{"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"},
|
||||
{"n": "2003", "v": "2003"}, {"n": "2002", "v": "2002"},
|
||||
{"n": "2001", "v": "2001"},
|
||||
{"n": "2000", "v": "2000"}]},
|
||||
{"key": "by", "name": "排序",
|
||||
"value": [{"n": "最新", "v": "time"}, {"n": "最热", "v": "hits"}, {"n": "评分", "v": "score"}]}], "4": [
|
||||
{"key": "class", "name": "剧情",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "剧情", "v": "剧情"}, {"n": "喜剧", "v": "喜剧"},
|
||||
{"n": "爱情", "v": "爱情"}, {"n": "动作", "v": "动作"}, {"n": "悬疑", "v": "悬疑"},
|
||||
{"n": "恐怖", "v": "恐怖"}, {"n": "奇幻", "v": "奇幻"}, {"n": "惊悚", "v": "惊悚"},
|
||||
{"n": "犯罪", "v": "犯罪"}, {"n": "科幻", "v": "科幻"}, {"n": "音乐", "v": "音乐"},
|
||||
{"n": "其他", "v": "其他"}]}, {"key": "year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2022", "v": "2022"},
|
||||
{"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"},
|
||||
{"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"},
|
||||
{"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"},
|
||||
{"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"},
|
||||
{"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"},
|
||||
{"n": "2007", "v": "2007"}, {"n": "2006", "v": "2006"},
|
||||
{"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"},
|
||||
{"n": "2003", "v": "2003"}, {"n": "2002", "v": "2002"},
|
||||
{"n": "2001", "v": "2001"},
|
||||
{"n": "2000", "v": "2000"}]},
|
||||
{"key": "by", "name": "排序",
|
||||
"value": [{"n": "最新", "v": "time"}, {"n": "最热", "v": "hits"}, {"n": "评分", "v": "score"}]}], "5": [
|
||||
{"key": "year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"},
|
||||
{"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}, {"n": "2006", "v": "2006"},
|
||||
{"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"}, {"n": "2003", "v": "2003"},
|
||||
{"n": "2002", "v": "2002"}, {"n": "2001", "v": "2001"}, {"n": "2000", "v": "2000"}]},
|
||||
{"key": "by", "name": "排序",
|
||||
"value": [{"n": "最新", "v": "time"}, {"n": "最热", "v": "hits"}, {"n": "评分", "v": "score"}]}], "6": [
|
||||
{"key": "class", "name": "剧情",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "情感", "v": "情感"}, {"n": "科幻", "v": "科幻"},
|
||||
{"n": "热血", "v": "热血"}, {"n": "推理", "v": " 推理"}, {"n": "搞笑", "v": "搞笑"},
|
||||
{"n": "冒险", "v": "冒险"}, {"n": "萝莉", "v": "萝莉"}, {"n": "校园", "v": "校园"},
|
||||
{"n": "动作", "v": "动作"}, {"n": "机战", "v": "机战"}, {"n": "运动", "v": "运动"},
|
||||
{"n": "战争", "v": "战争"}, {"n": " 少年", "v": "少年"}, {"n": "少女", "v": "少女"},
|
||||
{"n": "社会", "v": "社会"}, {"n": "原创", "v": "原创"}, {"n": "亲子", "v": "亲子"},
|
||||
{"n": "益智", "v": "益智"}, {"n": "励志", "v": "励志"}, {"n": "其他", "v": "其他"}]},
|
||||
{"key": "area", "name": "地区",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "国产", "v": "国产"}, {"n": "日本", "v": "日本"},
|
||||
{"n": "欧美", "v": "欧美"}, {"n": "其他", "v": "其他"}]}, {"key": "lang", "name": "语言",
|
||||
"value": [{"n": "全部", "v": ""},
|
||||
{"n": "国语", "v": "国语"},
|
||||
{"n": "日语", "v": "日语"},
|
||||
{"n": "英语", "v": "英语"},
|
||||
{"n": "其他",
|
||||
"v": "其他"}]},
|
||||
{"key": "year", "name": "年份",
|
||||
"value": [{"n": "全部", "v": ""}, {"n": "2022", "v": "2022"}, {"n": "2021", "v": "2021"},
|
||||
{"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, {"n": "2018", "v": "2018"},
|
||||
{"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, {"n": "2015", "v": "2015"},
|
||||
{"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, {"n": "2012", "v": "2012"},
|
||||
{"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, {"n": "2009", "v": "2009"},
|
||||
{"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}, {"n": "2006", "v": "2006"},
|
||||
{"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"}, {"n": "2003", "v": "2003"},
|
||||
{"n": "2002", "v": "2002"}, {"n": "2001", "v": "2001"}, {"n": "2000", "v": "2000"}]},
|
||||
{"key": "by", "name": "排序",
|
||||
"value": [{"n": "最新", "v": "time"}, {"n": "最热", "v": "hits"}, {"n": "评分", "v": "score"}]}]}
|
||||
}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
result = {}
|
||||
url = 'https://www.zxzjhd.com/video/{0}.html'.format(id)
|
||||
rsp = self.fetch(url)
|
||||
root = self.html(rsp.text)
|
||||
scripts = root.xpath("//script/text()")
|
||||
jo = {}
|
||||
for script in scripts:
|
||||
if (script.startswith("var player_")):
|
||||
target = script[script.index('{'):]
|
||||
jo = json.loads(target)
|
||||
break;
|
||||
parseUrl = ''
|
||||
# src="(\S+url=)
|
||||
# playerConfig = self.config['player']
|
||||
# if jo['from'] in self.config['player']:
|
||||
# playerConfig = self.config['player'][jo['from']]
|
||||
# parseUrl = playerConfig['pu'] + jo['url']
|
||||
# scriptUrl = 'https://www.zxzjhd.com/static/player/{0}.js'.format(jo['from'])
|
||||
# scriptRsp = self.fetch(scriptUrl)
|
||||
# parseUrl = self.regStr(scriptRsp.text,'src="(\\S+url=)')
|
||||
if 'line5' in jo['from']:
|
||||
url = jo['url']
|
||||
header = {
|
||||
'Host': 'cx.zxzja.com:9876',
|
||||
'Referer': 'https://www.zxzjhd.com/',
|
||||
'sec-fetch-mode': 'navigate',
|
||||
'sec-fetch-site': 'cross-site',
|
||||
'sec-fetch-dest': 'iframe',
|
||||
'upgrade-insecure-requests': '1'
|
||||
}
|
||||
self.log(url)
|
||||
parseRsp = self.fetch(url, headers=header)
|
||||
self.log(parseRsp)
|
||||
resultv2 = re.findall(r'var result_v2 = {(.*?)};', parseRsp.text, re.S)[0]
|
||||
self.log(resultv2)
|
||||
data = json.loads('{' + resultv2 + '}')['data']
|
||||
data_list = [i for i in data]
|
||||
data_list.reverse()
|
||||
content = data_list
|
||||
self.log(content)
|
||||
playUrl = ''
|
||||
for i in range(0, len(content), 2):
|
||||
combinedChars = content[i] + content[i + 1]
|
||||
decimalValue = int(combinedChars, 16)
|
||||
playUrl += chr(decimalValue)
|
||||
pos = int((len(playUrl) - 7) / 2)
|
||||
realUrl = playUrl[:pos] + playUrl[pos + 7:]
|
||||
if len(realUrl) > 0:
|
||||
result["parse"] = 0
|
||||
result["playUrl"] = ""
|
||||
result["url"] = realUrl
|
||||
result["header"] = ''
|
||||
else:
|
||||
result["parse"] = 1
|
||||
result["playUrl"] = ""
|
||||
result["url"] = jo['url']
|
||||
result["header"] = ''
|
||||
return result
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def localProxy(self, params):
|
||||
action = {}
|
||||
return [200, "video/MP2T", action, ""]
|
||||
@@ -0,0 +1,413 @@
|
||||
# coding=utf-8
|
||||
# !/usr/bin/python
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
import time
|
||||
import re
|
||||
from urllib import request, parse
|
||||
import urllib
|
||||
import urllib.request
|
||||
from xml.etree.ElementTree import fromstring, ElementTree as et
|
||||
|
||||
"""
|
||||
配置示例:
|
||||
t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾
|
||||
api里会自动含有ext参数是base64编码后的选中的筛选条件
|
||||
{
|
||||
"key":"hipy_t4_新浪资源",
|
||||
"name":"新浪资源(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/新浪资源",
|
||||
"searchable":1,
|
||||
"quickSearch":0,
|
||||
"filterable":1,
|
||||
"ext":""
|
||||
},
|
||||
{
|
||||
"key": "hipy_t3_新浪资源",
|
||||
"name": "新浪资源(hipy_t3)",
|
||||
"type": 3,
|
||||
"api": "{{host}}/txt/hipy/新浪资源.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 0,
|
||||
"filterable": 1,
|
||||
"ext": ""
|
||||
},
|
||||
"""
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
def getName(self):
|
||||
return "新浪资源" # 除去少儿不宜的内容
|
||||
|
||||
filterate = False
|
||||
|
||||
def init(self, extend=""):
|
||||
print("============{0}============".format(extend))
|
||||
pass
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
timeClass = time.localtime(time.time())
|
||||
cateManual = {
|
||||
'动漫': '3',
|
||||
'动漫电影': '17',
|
||||
'综艺': '4',
|
||||
'纪录片': '5',
|
||||
'动作片': '6',
|
||||
'爱情片': '7',
|
||||
'科幻片': '8',
|
||||
'战争片': '9',
|
||||
'剧情片': '10',
|
||||
'恐怖片': '11',
|
||||
'喜剧片': '12',
|
||||
'大陆剧': '13',
|
||||
'港澳剧': '14',
|
||||
'台湾剧': '15',
|
||||
'欧美剧': '16',
|
||||
'韩剧': '18',
|
||||
'日剧': '20',
|
||||
'泰剧': '21',
|
||||
'体育': '23'
|
||||
}
|
||||
# if timeClass.tm_hour>22:
|
||||
# cateManual['伦理片']='22'
|
||||
# self.filterate=False
|
||||
classes = []
|
||||
for k in cateManual:
|
||||
classes.append({
|
||||
'type_name': k,
|
||||
'type_id': cateManual[k]
|
||||
})
|
||||
result['class'] = classes
|
||||
if (filter):
|
||||
result['filters'] = self.config['filter']
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
xmlTxt = self.custom_webReadFile(
|
||||
urlStr='https://api.xinlangapi.com/xinlangapi.php/provide/vod/from/xlyun/at/xml/?ac=list&h=24')
|
||||
tree = et(fromstring(xmlTxt))
|
||||
root = tree.getroot()
|
||||
listXml = root.iter('list')
|
||||
videos = self.custom_list(html=listXml)
|
||||
result = {
|
||||
'list': videos
|
||||
}
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
result = {}
|
||||
videos = []
|
||||
pagecount = 1
|
||||
limit = 20
|
||||
total = 9999
|
||||
Url = 'https://api.xinlangapi.com/xinlangapi.php/provide/vod/from/xlyun/at/xml/?ac=list&t={0}&pg={1}'.format(
|
||||
tid, pg)
|
||||
xmlTxt = self.custom_webReadFile(urlStr=Url)
|
||||
tree = et(fromstring(xmlTxt))
|
||||
root = tree.getroot()
|
||||
listXml = root.iter('list')
|
||||
for vod in listXml:
|
||||
pagecount = vod.attrib['pagecount']
|
||||
limit = vod.attrib['pagesize']
|
||||
total = vod.attrib['recordcount']
|
||||
videos = self.custom_list(html=root.iter('list'))
|
||||
result['list'] = videos
|
||||
result['page'] = pg
|
||||
result['pagecount'] = pagecount
|
||||
result['limit'] = limit
|
||||
result['total'] = total
|
||||
return result
|
||||
|
||||
def detailContent(self, array):
|
||||
result = {}
|
||||
aid = array[0].split('###')
|
||||
id = aid[1]
|
||||
logo = aid[2]
|
||||
title = aid[0]
|
||||
vod_play_from = ['播放线路', ]
|
||||
vod_year = ''
|
||||
vod_actor = ''
|
||||
vod_content = ''
|
||||
vod_director = ''
|
||||
type_name = ''
|
||||
vod_area = ''
|
||||
vod_lang = ''
|
||||
vodItems = []
|
||||
vod_play_url = []
|
||||
try:
|
||||
url = 'https://api.xinlangapi.com/xinlangapi.php/provide/vod/from/xlyun/at/xml/?ac=detail&ids=' + id
|
||||
xmlTxt = self.custom_webReadFile(urlStr=url)
|
||||
jRoot = et(fromstring(xmlTxt))
|
||||
xmlList = jRoot.iter('list')
|
||||
for vod in xmlList:
|
||||
for x in vod:
|
||||
for v in x:
|
||||
if v.tag == 'actor':
|
||||
vod_actor = v.text
|
||||
if v.tag == 'director':
|
||||
vod_director = v.text
|
||||
if v.tag == 'des':
|
||||
vod_content = v.text
|
||||
if v.tag == 'area':
|
||||
vod_area = v.text
|
||||
if v.tag == 'year':
|
||||
vod_year = v.text
|
||||
if v.tag == 'type':
|
||||
type_name = v.text
|
||||
if v.tag == 'lang':
|
||||
vod_lang = v.text
|
||||
|
||||
temporary = self.custom_RegexGetText(Text=xmlTxt, RegexText=r'<dd flag="xlyun">(.+?)</dd>', Index=1)
|
||||
temporary = temporary.replace('<![CDATA[', '').replace(']]>', '')
|
||||
vodItems = self.custom_EpisodesList(temporary)
|
||||
joinStr = "#".join(vodItems)
|
||||
vod_play_url.append(joinStr)
|
||||
except:
|
||||
pass
|
||||
vod = {
|
||||
"vod_id": array[0],
|
||||
"vod_name": title,
|
||||
"vod_pic": logo,
|
||||
"type_name": type_name,
|
||||
"vod_year": vod_year,
|
||||
"vod_area": vod_area,
|
||||
"vod_remarks": vod_lang,
|
||||
"vod_actor": vod_actor,
|
||||
"vod_director": vod_director,
|
||||
"vod_content": vod_content
|
||||
}
|
||||
vod['vod_play_from'] = "$$$".join(vod_play_from)
|
||||
vod['vod_play_url'] = "$$$".join(vod_play_url)
|
||||
result = {
|
||||
'list': [
|
||||
vod
|
||||
]
|
||||
}
|
||||
if self.filterate == True and self.custom_RegexGetText(Text=type_name, RegexText=r'(伦理|倫理|福利)',
|
||||
Index=1) != '':
|
||||
result = {'list': []}
|
||||
return result
|
||||
|
||||
def searchContent(self, key, quick, pg=1):
|
||||
Url = 'https://api.xinlangapi.com/xinlangapi.php/provide/vod/from/xlyun/at/xml/?ac=list&wd={0}&pg={1}'.format(
|
||||
urllib.parse.quote(key), '1')
|
||||
xmlTxt = self.custom_webReadFile(urlStr=Url)
|
||||
tree = et(fromstring(xmlTxt))
|
||||
root = tree.getroot()
|
||||
listXml = root.iter('list')
|
||||
videos = self.custom_list(html=listXml)
|
||||
result = {
|
||||
'list': videos
|
||||
}
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
result = {}
|
||||
parse = 1
|
||||
url = id
|
||||
htmlTxt = self.custom_webReadFile(urlStr=url, header=self.header)
|
||||
url = self.custom_RegexGetText(Text=htmlTxt, RegexText=r'(https{0,1}://.+?\.m3u8)', Index=1)
|
||||
if url.find('.m3u8') < 1:
|
||||
url = id
|
||||
parse = 0
|
||||
result["parse"] = parse # 0=直接播放、1=嗅探
|
||||
result["playUrl"] = ''
|
||||
result["url"] = url
|
||||
result['jx'] = 0 # VIP解析,0=不解析、1=解析
|
||||
result["header"] = ''
|
||||
return result
|
||||
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {}
|
||||
}
|
||||
header = {}
|
||||
|
||||
def localProxy(self, params):
|
||||
return [200, "video/MP2T", ""]
|
||||
|
||||
# -----------------------------------------------自定义函数-----------------------------------------------
|
||||
# 正则取文本
|
||||
def custom_RegexGetText(self, Text, RegexText, Index):
|
||||
returnTxt = ""
|
||||
Regex = re.search(RegexText, Text, re.M | re.S)
|
||||
if Regex is None:
|
||||
returnTxt = ""
|
||||
else:
|
||||
returnTxt = Regex.group(Index)
|
||||
return returnTxt
|
||||
|
||||
# 分类取结果
|
||||
def custom_list(self, html):
|
||||
ListRe = html
|
||||
videos = []
|
||||
temporary = []
|
||||
for vod in ListRe:
|
||||
for value in vod:
|
||||
for x in value:
|
||||
|
||||
if x.tag == 'name':
|
||||
title = x.text
|
||||
if x.tag == 'id':
|
||||
id = x.text
|
||||
if x.tag == 'type':
|
||||
tid = x.text
|
||||
if x.tag == 'last':
|
||||
last = x.text
|
||||
temporary.append({
|
||||
"name": title,
|
||||
"id": id,
|
||||
"last": last
|
||||
})
|
||||
|
||||
if len(temporary) > 0:
|
||||
idTxt = ''
|
||||
for vod in temporary:
|
||||
idTxt = idTxt + vod['id'] + ','
|
||||
if len(idTxt) > 1:
|
||||
idTxt = idTxt[0:-1]
|
||||
url = 'https://api.xinlangapi.com/xinlangapi.php/provide/vod/from/xlyun/at/xml/?ac=detail&ids=' + idTxt
|
||||
xmlTxt = self.custom_webReadFile(urlStr=url)
|
||||
jRoot = et(fromstring(xmlTxt))
|
||||
xmlList = jRoot.iter('list')
|
||||
for vod in xmlList:
|
||||
for x in vod:
|
||||
for v in x:
|
||||
if v.tag == 'name':
|
||||
title = v.text
|
||||
if v.tag == 'id':
|
||||
vod_id = v.text
|
||||
if v.tag == 'pic':
|
||||
img = v.text
|
||||
if v.tag == 'note':
|
||||
remarks = v.text
|
||||
if v.tag == 'year':
|
||||
vod_year = v.text
|
||||
if v.tag == 'type':
|
||||
type_name = v.text
|
||||
if self.filterate == True and self.custom_RegexGetText(Text=type_name,
|
||||
RegexText=r'(伦理|倫理|福利)',
|
||||
Index=1) != '':
|
||||
continue
|
||||
vod_id = '{0}###{1}###{2}'.format(title, vod_id, img)
|
||||
# vod_id='{0}###{1}###{2}###{3}###{4}###{5}###{6}###{7}###{8}###{9}###{10}'.format(title,vod_id,img,vod_actor,vod_director,'/'.join(type_name),'/'.join(vod_time),'/'.join(vod_area),vod_lang,vod_content,vod_play_url)
|
||||
# print(vod_id)
|
||||
videos.append({
|
||||
"vod_id": vod_id,
|
||||
"vod_name": title,
|
||||
"vod_pic": img,
|
||||
"vod_year": vod_year,
|
||||
"vod_remarks": remarks
|
||||
})
|
||||
return videos
|
||||
|
||||
# 访问网页
|
||||
def custom_webReadFile(self, urlStr, header=None, codeName='utf-8'):
|
||||
html = ''
|
||||
if header == None:
|
||||
header = {
|
||||
"Referer": urlStr,
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36',
|
||||
"Host": self.custom_RegexGetText(Text=urlStr, RegexText='https*://(.*?)(/|$)', Index=1)
|
||||
}
|
||||
# import ssl
|
||||
# ssl._create_default_https_context = ssl._create_unverified_context#全局取消证书验证
|
||||
req = urllib.request.Request(url=urlStr, headers=header) # ,headers=header
|
||||
with urllib.request.urlopen(req) as response:
|
||||
html = response.read().decode(codeName)
|
||||
return html
|
||||
|
||||
# 取剧集区
|
||||
def custom_lineList(self, Txt, mark, after):
|
||||
circuit = []
|
||||
origin = Txt.find(mark)
|
||||
while origin > 8:
|
||||
end = Txt.find(after, origin)
|
||||
circuit.append(Txt[origin:end])
|
||||
origin = Txt.find(mark, end)
|
||||
return circuit
|
||||
|
||||
# 正则取文本,返回数组
|
||||
def custom_RegexGetTextLine(self, Text, RegexText, Index):
|
||||
returnTxt = []
|
||||
pattern = re.compile(RegexText, re.M | re.S)
|
||||
ListRe = pattern.findall(Text)
|
||||
if len(ListRe) < 1:
|
||||
return returnTxt
|
||||
for value in ListRe:
|
||||
returnTxt.append(value)
|
||||
return returnTxt
|
||||
|
||||
# 取集数
|
||||
def custom_EpisodesList(self, html):
|
||||
ListRe = html.split('#')
|
||||
videos = []
|
||||
for vod in ListRe:
|
||||
t = vod.split('$')
|
||||
url = t[1]
|
||||
title = t[0]
|
||||
if len(url) == 0:
|
||||
continue
|
||||
videos.append(title + "$" + url)
|
||||
return videos
|
||||
|
||||
# 取分类
|
||||
def custom_classification(self):
|
||||
xmlTxt = self.custom_webReadFile(
|
||||
urlStr='https://api.xinlangapi.com/xinlangapi.php/provide/vod/from/xlyun/at/xml/')
|
||||
tree = et(fromstring(xmlTxt))
|
||||
root = tree.getroot()
|
||||
classXml = root.iter('class')
|
||||
temporaryClass = {}
|
||||
for vod in classXml:
|
||||
for value in vod:
|
||||
if self.custom_RegexGetText(Text=value.text, RegexText=r'(福利|倫理片|伦理片)', Index=1) != '':
|
||||
continue
|
||||
temporaryClass[value.text] = value.attrib['id']
|
||||
print("'{0}':'{1}',".format(value.text, value.attrib['id']))
|
||||
return temporaryClass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from t4.core.loader import t4_spider_init
|
||||
|
||||
spider = Spider()
|
||||
t4_spider_init(spider)
|
||||
print(spider.homeContent(True))
|
||||
print(spider.homeVideoContent())
|
||||
|
||||
# T=Spider()
|
||||
# T. homeContent(filter=False)
|
||||
# T.custom_classification()
|
||||
# l=T.homeVideoContent()
|
||||
# l=T.searchContent(key='柯南',quick='')
|
||||
# l=T.categoryContent(tid='22',pg='1',filter=False,extend={})
|
||||
# for x in l['list']:
|
||||
# print(x['vod_name'])
|
||||
# mubiao= l['list'][2]['vod_id']
|
||||
# # print(mubiao)
|
||||
# playTabulation=T.detailContent(array=[mubiao,])
|
||||
# # print(playTabulation)
|
||||
# vod_play_from=playTabulation['list'][0]['vod_play_from']
|
||||
# vod_play_url=playTabulation['list'][0]['vod_play_url']
|
||||
# url=vod_play_url.split('$$$')
|
||||
# vod_play_from=vod_play_from.split('$$$')[0]
|
||||
# url=url[0].split('$')
|
||||
# url=url[1].split('#')[0]
|
||||
# # print(url)
|
||||
# m3u8=T.playerContent(flag=vod_play_from,id=url,vipFlags=True)
|
||||
# print(m3u8)
|
||||
@@ -0,0 +1,375 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# File : 樱花动漫.py
|
||||
# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
|
||||
# Author's Blog: https://blog.csdn.net/qq_32394351
|
||||
# Date : 2024/1/7
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
try:
|
||||
# from base.spider import Spider as BaseSpider
|
||||
from base.spider import BaseSpider
|
||||
except ImportError:
|
||||
from t4.base.spider import BaseSpider
|
||||
|
||||
from cachetools import cached, TTLCache # 可以缓存curd的函数,指定里面的key
|
||||
|
||||
"""
|
||||
配置示例:
|
||||
t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾
|
||||
api里会自动含有ext参数是base64编码后的选中的筛选条件
|
||||
{
|
||||
"key":"hipy_t4_樱花动漫",
|
||||
"name":"樱花动漫(hipy_t4)",
|
||||
"type":4,
|
||||
"api":"http://192.168.31.49:5707/api/v1/vod/樱花动漫",
|
||||
"searchable":1,
|
||||
"quickSearch":0,
|
||||
"filterable":1,
|
||||
"ext":"https://jihulab.com/qiaoji/open/-/raw/main/yinghua"
|
||||
},
|
||||
{
|
||||
"key": "hipy_t3_樱花动漫",
|
||||
"name": "樱花动漫(hipy_t3)",
|
||||
"type": 3,
|
||||
"api": "{{host}}/txt/hipy/樱花动漫.py",
|
||||
"searchable": 1,
|
||||
"quickSearch": 0,
|
||||
"filterable": 1,
|
||||
"ext": "https://jihulab.com/qiaoji/open/-/raw/main/yinghua"
|
||||
},
|
||||
"""
|
||||
|
||||
|
||||
def envkey(self, url: str):
|
||||
return url
|
||||
|
||||
|
||||
# 全局变量
|
||||
gParam = {
|
||||
"HomeDict": {},
|
||||
"TypeDict": {},
|
||||
}
|
||||
|
||||
|
||||
class Spider(BaseSpider): # 元类 默认的元类 type
|
||||
api_qj: str = 'https://jihulab.com/qiaoji/open/-/raw/main/yinghua'
|
||||
private_key: str = 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDA+5YTt3w1q/0WGw+TWyCSHTAeYiwBqAqDWot1u/1hoeANpED8gtW1AxB1mYNDQ+9eR8Ml+JC13+ME6RHjEbN4+n9V9OP90c81G0qSjBQ/DKQiMIFjbTH97RjVMtswf96tqwe4Rs/DT2ym6MP4P7QvJcxrFz5VVQXyOtUxhpMc9oktWuk0XKE8Mozu1FM879RknlM6WmJL85Wl/BnZrd+/AQbzziceELGrBfjbc1UOFAxYq2kA10H3o+Z4oOIODxUtXeh4R2oH3vHb4Ynnw6reXED5KsE3u1EO5HMQZyN16TZMTIps32bPe+vQlAT6V5nGcqXGT9fntjqIxJB0T9G3AgMBAAECggEBAKP6Yuh4BZP5g0CwV8jHKuLc6FE469mwdtZsLooo5cF68c3Fnu6xIXQAmZDDk3SpmhCLe7edASF5jwZSIL/H/68xcteQEdZP2/htKy1g16dHT4Q5oQfh9hOkznACGZuZW5ZH+HRNvyZfK5ybtkEPqERTouHwSyfo6feMpDDD/+cf3h1//7JKXKA7JPEU420YucsjQwjMuu5xdPa0TPqEc5mIbOBj753Pzn4GCScM+FRqJWr2x8e+KDPcPY8CUDLBSWxGLsB0A7+bEq/EiAQkbx09QKTwwxRLgVXjBbvyPB8BOuJpPM9BHx+vFcm5WSbkJdRI4qVFtEdsN/gDfFkwcjkCgYEA8Z8i/fTFRnzyvp9Pp8E+bSaYlvpTLUZ1KYNStaDg/BqlYGgGK1Jh90qjvRbBoiIjeBQd3IFLT4pFdd7Z9drLFdvqB22SNeVQU57kir/B6NY5G7yOjXB4qN17F4S3GubYIEcjF0W1tG/uOqqzb8FxrLJTK8WiFudbBt2ioCO4pJsCgYEAzHd8MctmD1Z1eM/xusvX1yCwGpxBuHT+ymThzLXyI6Ej0Q50jOQlf3cTyY/FgGbvAMz+oBybkEwE80gu7CPi0WPs+yCpAIB4+Th7afsrRylQI1ZWoRovaRmsyjnkIw0Mnj06VYNYPtkzm/OViRIqf4ESTTGas24bDm5DuwM9gxUCgYBwg4BR7gdnWYvYRGtdXNlrDowD0jGlZaftWt/LAE2EWAwmpooo5kYEV9eDl/M3QtptckCti++77FGIH+wzVl03op6KMvXg7xXGurkF+2GawRb62YUwS+2EBQ7q1rxFZLXD4hxvG+EPUwgGfbLtGZGLr8aXHYLrU3TJ769pDvlOfQKBgAFlAzzXtU9/eHele3GZuFQoTeswi6Y1bhN1UrDxwMALdlITtinL2JGg/0qNp3wzt4ea3lW7PDhkvFfocyF7MS3ab6Ba3aw6NBkHEJhtdSMcHgbPrPGWWyJtYWdTs8GlciOWKVKx/aUYGCkFJUz1CcMq3zQVlYeJxbd4ew/Iet/tAoGBAMRfvG1iLQAlS3AGaQeRwVxnvpciDn+7/sUCf8DEOk8Bqg4/ytJDTDrWufCtwmpsXmp6AUQig9mNKj7z26wSNbwYdzPsncK+sGRlS7eLAzzcv1a+1pghOOGDuQNzwlFOcauhkrcqjeKmu7OiKD48pvh3ZICiIWS1YL7LuMfUwHRJ'
|
||||
key: str = 'fQiG3YWTpQEYHNFTxJXCBaZrcCkkpfxH'
|
||||
iv: str = '1238389483762837'
|
||||
token: str = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBcHBUbyIsImlhdCI6MTcwMDA3MTcwMiwiZXhwIjoxNzMxNjA3NzAyLCJuYmYiOjE3MDAwNzE3MDIsInN1YiI6IkFwcFRvIiwianRpIjoiYzRjNTAzOTQxYTM4NWI1MDMyMTAyYmY3Yzk1OGY4MzEiLCJkYXRhIjp7InVzZXJfaWQiOjI0ODc1NCwidXNlcl9jaGVjayI6ImUzYmQ3NmNhNTJhMGY4NjAwMTdjNjdkZGUwN2QzZTM3IiwidXNlcl9uYW1lIjoiaGV6aWh1aSJ9fQ.4LWs3rNL-os8_Pqa9LgKtvVG5f0aIxVyAjYIagvO1F4'
|
||||
ic: str = 'bmXes2xsCWvsSdfYav0s9D78Ly7w1o%2BOYXApKx6SUd4NWKsTsapbS52l7y%2FsTVCM2kcoLws2jryaDQlHLse5fxD2B2VXZXfaQo0eMTOv2Xq7CKoPa51uVt8WiIY2SPztc7wxGE89%2Fcw2Q3n85uUT3A%3D%3D'
|
||||
api: str = 'http://60.204.185.245:7090/appto/v1'
|
||||
api_cofig: str = api + '/config/get?p=android'
|
||||
api_home: str = api + '/home/cateData?id=1'
|
||||
api_cate: str = api + '/vod/getLists'
|
||||
api_search: str = api + '/vod/getVodSearch'
|
||||
api_detail: str = api + '/vod/getVod?__platform=android&__ic=' + ic
|
||||
api_parse: str = api + '/parsing/proxy'
|
||||
|
||||
def getName(self):
|
||||
return "樱花动漫"
|
||||
|
||||
@cached(cache=TTLCache(maxsize=3, ttl=3600), key=envkey)
|
||||
def get_init_api(self, url):
|
||||
try:
|
||||
print('get_init_api请求URL:', url)
|
||||
r = self.fetch(url)
|
||||
ret = self.decode_rsa(r.text[1:])
|
||||
return ret
|
||||
except Exception as e:
|
||||
print(f'get_init_api请求URL发生错误:{e}')
|
||||
return {}
|
||||
|
||||
def init_extend(self, url):
|
||||
ret = self.get_init_api(url)
|
||||
if ret.get('key'):
|
||||
self.key = ret.get('key')
|
||||
if ret.get('ic'):
|
||||
self.ic = ret.get('ic')
|
||||
if ret.get('token'):
|
||||
self.token = ret.get('token')
|
||||
if ret.get('url') and ret.get('api'):
|
||||
api = ret.get('url') + ret.get('api')
|
||||
self.api = api
|
||||
self.api_cofig: str = api + '/config/get?p=android'
|
||||
self.api_home: str = api + '/home/cateData?id=1'
|
||||
self.api_cate: str = api + '/vod/getLists'
|
||||
self.api_search: str = api + '/vod/getVodSearch'
|
||||
self.api_detail: str = api + '/vod/getVod?__platform=android&__ic=' + self.ic
|
||||
self.api_parse: str = api + '/parsing/proxy'
|
||||
|
||||
def init_api_ext_file(self):
|
||||
"""
|
||||
这个函数用于初始化py文件对应的json文件,用于存筛选规则。
|
||||
执行此函数会自动生成筛选文件
|
||||
@return:
|
||||
"""
|
||||
ext_file = __file__.replace('.py', '.json')
|
||||
print(f'ext_file:{ext_file}')
|
||||
ext_file_dict = self.homeContent(True)['filters']
|
||||
with open(ext_file, mode='w+', encoding='utf-8') as f:
|
||||
f.write(self.json2str(ext_file_dict))
|
||||
|
||||
def init(self, extend=""):
|
||||
"""
|
||||
初始化加载extend,一般与py文件名同名的json文件作为扩展筛选
|
||||
@param extend:
|
||||
@return:
|
||||
"""
|
||||
ext = self.extend
|
||||
if ext.startswith('http'):
|
||||
self.init_extend(ext)
|
||||
else:
|
||||
self.init_extend(self.api_qj)
|
||||
|
||||
# 装载模块,这里只要一个就够了
|
||||
if isinstance(extend, list):
|
||||
for lib in extend:
|
||||
if '.Spider' in str(type(lib)):
|
||||
self.module = lib
|
||||
break
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
pass
|
||||
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def homeContent(self, filterable=False):
|
||||
"""
|
||||
获取首页分类及筛选数据
|
||||
@param filterable: 能否筛选,跟t3/t4配置里的filterable参数一致
|
||||
@return:
|
||||
"""
|
||||
filter_names = {
|
||||
'area': '地区',
|
||||
'class': '分类',
|
||||
'director': '导演',
|
||||
'lang': '语言',
|
||||
'star': '明星',
|
||||
'state': '状态',
|
||||
'version': '版本',
|
||||
'year': '年份',
|
||||
}
|
||||
r = self.fetch(self.api_cofig)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
result = {}
|
||||
classes = []
|
||||
filters = {}
|
||||
type_dict = {}
|
||||
for tp in data.get('get_type') or []:
|
||||
classes.append({
|
||||
'type_name': tp['type_name'],
|
||||
'type_id': tp['type_id']
|
||||
})
|
||||
type_dict[str(tp['type_id'])] = tp['type_name']
|
||||
tp_filters = []
|
||||
for key, value in tp['type_extend'].items():
|
||||
if value:
|
||||
tp_filters.append({
|
||||
'key': key,
|
||||
'name': filter_names.get(key) or key,
|
||||
'value': [{'n': '全部', 'v': ''}] + [{'n': i, 'v': i} for i in value.split(',') if i]
|
||||
})
|
||||
filters[tp['type_id']] = tp_filters
|
||||
|
||||
result['class'] = classes
|
||||
if filterable:
|
||||
result['filters'] = filters
|
||||
global gParam
|
||||
gParam['HomeDict'].update(result)
|
||||
gParam['TypeDict'].update(type_dict)
|
||||
return result
|
||||
|
||||
def homeVideoContent(self):
|
||||
"""
|
||||
首页推荐列表
|
||||
@return:
|
||||
"""
|
||||
r = self.fetch(self.api_home)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
d = []
|
||||
for section in data['sections']:
|
||||
items = section['items']
|
||||
for item in items:
|
||||
d.append({
|
||||
'vod_name': item['vod_name'],
|
||||
'vod_id': item['vod_id'],
|
||||
'vod_pic': item['vod_pic'],
|
||||
'vod_remarks': item['vod_remarks'],
|
||||
})
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
return result
|
||||
|
||||
def categoryContent(self, tid, pg, filterable, extend):
|
||||
"""
|
||||
返回一级列表页数据
|
||||
@param tid: 分类id
|
||||
@param pg: 当前页数
|
||||
@param filterable: 能否筛选
|
||||
@param extend: 当前筛选数据
|
||||
@return:
|
||||
"""
|
||||
page_count = 21 # 默认赋值一页列表21条数据|这个值一定要写正确看他默认一页多少条
|
||||
fls = extend.keys() # 哪些刷新数据
|
||||
# ?type_id=1&area=&lang=&year=&order=time&type_name=&page=1&pageSize=21
|
||||
params = {'page': pg, 'pageSize': page_count, 'tid': tid, 'type_name': gParam['TypeDict'].get(str(tid)) or ''}
|
||||
for fl in fls:
|
||||
params[fl] = extend[fl]
|
||||
r = self.fetch(self.api_cate, data=params)
|
||||
print(r.url)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
d = data['data']
|
||||
result = {
|
||||
'list': d,
|
||||
'page': pg,
|
||||
'pagecount': 9999 if len(d) >= page_count else pg,
|
||||
'limit': 90,
|
||||
'total': data['total'],
|
||||
}
|
||||
return result
|
||||
|
||||
def detailContent(self, ids):
|
||||
"""
|
||||
返回二级详情页数据
|
||||
@param ids: 一级传过来的vod_id列表
|
||||
@return:
|
||||
"""
|
||||
# id=110102
|
||||
vod_id = ids[0]
|
||||
params = {'id': vod_id}
|
||||
r = self.fetch(self.api_detail, data=params)
|
||||
print(r.url)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
vod = {"vod_id": vod_id,
|
||||
"vod_name": data['vod_name'],
|
||||
"vod_pic": data['vod_pic'],
|
||||
"type_name": data['vod_en'],
|
||||
"vod_year": data['vod_year'],
|
||||
"vod_area": data['vod_area'],
|
||||
"vod_remarks": data['vod_remarks'],
|
||||
"vod_actor": data['vod_actor'],
|
||||
"vod_director": data['vod_director'],
|
||||
"vod_content": data['vod_blurb'],
|
||||
"vod_play_from": data['vod_play_from'],
|
||||
}
|
||||
vod_play_list = data['vod_play_list']
|
||||
vod_play_urls = []
|
||||
for vod_play in vod_play_list:
|
||||
v_from = vod_play['player_info']['from']
|
||||
v_show = vod_play['player_info']['show']
|
||||
vod_play_url = '#'.join(
|
||||
[url['name'] + '$' + '&&'.join([url['url'], v_from, v_show]) for url in vod_play['urls']])
|
||||
vod_play_urls.append(vod_play_url)
|
||||
vod['vod_play_url'] = '$$$'.join(vod_play_urls)
|
||||
result = {
|
||||
'list': [vod]
|
||||
}
|
||||
# print(vod)
|
||||
return result
|
||||
|
||||
def searchContent(self, wd, quick=False, pg=1):
|
||||
"""
|
||||
返回搜索列表
|
||||
@param wd: 搜索关键词
|
||||
@param quick: 是否来自快速搜索。t3/t4配置里启用了快速搜索,在快速搜索在执行才会是True
|
||||
@param pg: 页数
|
||||
@return:
|
||||
"""
|
||||
# ?wd=%E4%B8%89%E5%A4%A7%E9%98%9F&page=1&type=
|
||||
params = {'wd': wd, 'type': '', 'page': pg}
|
||||
r = self.fetch(self.api_search, data=params)
|
||||
print(r.url)
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
d = data['data']
|
||||
result = {
|
||||
'list': d
|
||||
}
|
||||
return result
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
"""
|
||||
解析播放,返回json。壳子视情况播放直链或进行嗅探
|
||||
@param flag: vod_play_from 播放来源线路
|
||||
@param id: vod_play_url 播放的链接
|
||||
@param vipFlags: vip标识
|
||||
@return:
|
||||
"""
|
||||
headers = {
|
||||
'Content-Type': 'multipart/form-data; boundary=--dio-boundary-1205762094',
|
||||
'token': self.token,
|
||||
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
|
||||
}
|
||||
if '&&' in id:
|
||||
_v = id.split('&&')
|
||||
params = {'play_url': _v[0], 'label': _v[2], 'key': _v[1]}
|
||||
else:
|
||||
params = {'play_url': id, 'label': '主线', 'key': 'mp4'}
|
||||
# print(params)
|
||||
r = self.postBinary(self.api_parse, data=params, boundary='--dio-boundary-1205762094', headers=headers)
|
||||
# print(r.request.body.decode())
|
||||
ret = r.json()
|
||||
data = self.decode(ret['data'])
|
||||
# print(data)
|
||||
url = data['url']
|
||||
parse = 0
|
||||
result = {
|
||||
'parse': parse, # 1=嗅探,0=播放
|
||||
'playUrl': '', # 解析链接
|
||||
'url': url, # 直链或待嗅探地址
|
||||
# 'header': headers, # 播放UA
|
||||
}
|
||||
return result
|
||||
|
||||
config = {
|
||||
"player": {},
|
||||
"filter": {}
|
||||
}
|
||||
header = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
|
||||
"Host": "www.baidu.com",
|
||||
"Referer": "https://www.baidu.com/"
|
||||
}
|
||||
|
||||
def localProxy(self, params):
|
||||
return [200, "video/MP2T", ""]
|
||||
|
||||
# -----------------------------------------------自定义函数-----------------------------------------------
|
||||
def decode(self, text):
|
||||
return self.str2json(self.aes_cbc_decode(text, self.key, self.iv))
|
||||
|
||||
def decode_rsa(self, text):
|
||||
return self.str2json(self.rsa_private_decode(text, self.private_key))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 在线aes测试 https://config.net.cn/tools/AES.html
|
||||
# 分类页:http://60.204.185.245:7090/appto/v1/home/cateData?id=1
|
||||
# 推荐页:http://60.204.185.245:7090/appto/v1/config/get?p=android
|
||||
from t4.core.loader import t4_spider_init
|
||||
|
||||
spider = Spider()
|
||||
t4_spider_init(spider, 'https://jihulab.com/qiaoji/open/-/raw/main/yinghua')
|
||||
# spider.init_api_ext_file() # 生成筛选对应的json文件
|
||||
|
||||
# print(spider.homeContent(True))
|
||||
# print(spider.homeVideoContent())
|
||||
# print(spider.categoryContent('1', 1, True, {'year': '2024'}))
|
||||
# print(spider.detailContent([110078]))
|
||||
print(spider.searchContent('斗罗大陆'))
|
||||
# print(spider.playerContent(None, 'f1d7d074f624e993e425f|11d1d091b0b28|31613145e4a7c|518737c8650978', None))
|
||||
# spider.searchContent('斗罗大陆')
|
||||
Reference in New Issue
Block a user