Sync all projects

This commit is contained in:
github-actions[bot]
2026-06-29 03:41:14 +00:00
parent 529cd7c625
commit 8b025e0766
11717 changed files with 6370622 additions and 0 deletions
+425
View File
@@ -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))
+1056
View File
@@ -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("&nbsp;", " ")
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.
+1
View File
@@ -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"}]}]}
+1
View File
@@ -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"}]}]}
+1
View File
@@ -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"}]}]}
+1
View File
@@ -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": ""}]}]}
+538
View File
@@ -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))
+310
View File
@@ -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, ""]
+480
View File
@@ -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))
+398
View File
@@ -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))
+425
View File
@@ -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, ""]
+413
View File
@@ -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)
+375
View File
@@ -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('斗罗大陆')