This commit is contained in:
Liu committed 2026-02-15 04:28:57 +08:00
1 parent f8decdcc4b
commit 7c15638cd9
33 files changed
+10691 -14528

No files matched your search

+74 -36
View File
@@ -1,39 +1,49 @@
# -*- coding: utf-8 -*-
# 本资源来源于互联网公开渠道,仅可用于个人学习及爬虫技术交流。
# 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
"""
{
"key": "xxx",
"name": "xxx",
"type": 3,
"api": "./ApptoV5无加密.py",
"ext": "http://domain.com"
}
"""
import re,sys,uuid
import sys, uuid, json
from base.spider import Spider
sys.path.append('..')
class Spider(Spider):
host,config,local_uuid,parsing_config = '','','',[]
local_uuid = ''
config = ''
parsing_config = []
token = ''
headers = {
'User-Agent': "Dart/2.19 (dart:io)",
'Accept-Encoding': "gzip",
'appto-local-uuid': local_uuid
'appto-local-uuid': local_uuid,
'token': ''
}
line_order = []
block_keywords = []
def init(self, extend=''):
def init(self, extend=""):
try:
host = extend.strip()
if not host.startswith('http'):
return {}
if not re.match(r'^https?://[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(:\d+)?/?$', host):
host_=self.fetch(host).json()
self.host = host_['domain']
self.token = ''
if extend.startswith('{') and extend.endswith('}'):
ext_dict = json.loads(extend.strip())
self.host = ext_dict.get('host', '').strip()
self.token = ext_dict.get('token', '').strip()
line_order_input = ext_dict.get('line_order', '')
line_order_str = ','.join(line_order_input) if isinstance(line_order_input, list) else str(line_order_input).strip()
if '@' in line_order_str:
order_part, block_part = line_order_str.split('@', 1)
self.line_order = list({kw.strip().lower() for kw in order_part.split(',') if kw.strip()})
self.block_keywords = list({kw.strip().lower() for kw in block_part.split(',') if kw.strip()})
else:
self.line_order = list({kw.strip().lower() for kw in line_order_str.split(',') if kw.strip()})
self.block_keywords = []
else:
self.host = host
self.host = extend.strip()
self.line_order = []
self.block_keywords = []
if not self.host.startswith('http'):
return {}
self.local_uuid = str(uuid.uuid4())
self.headers['appto-local-uuid'] = self.local_uuid
self.headers['token'] = self.token
response = self.fetch(f'{self.host}/apptov5/v1/config/get?p=android&__platform=android', headers=self.headers).json()
config = response['data']
self.config = config
@@ -45,31 +55,56 @@ class Spider(Spider):
for j in i['config']:
if j['type'] == 'json':
label.append(j['label'])
parsing_config.update({i['key']:label})
parsing_config.update({i['key']: label})
self.parsing_config = parsing_config
return None
except Exception as e:
print(f'初始化异常:{e}')
return {}
def detailContent(self, ids):
response = self.fetch(f"{self.host}/apptov5/v1/vod/getVod?id={ids[0]}",headers=self.headers).json()
response = self.fetch(f"{self.host}/apptov5/v1/vod/getVod?id={ids[0]}", headers=self.headers).json()
data3 = response['data']
videos = []
vod_play_url = ''
vod_play_from = ''
for i in data3['vod_play_list']:
vod_play_list = data3['vod_play_list']
if self.block_keywords:
filtered_list = []
for play_item in vod_play_list:
show_val = play_item['player_info'].get('show', '').strip().lower()
if not any(kw in show_val for kw in self.block_keywords):
filtered_list.append(play_item)
vod_play_list = filtered_list
if self.line_order:
def get_priority(play_item):
show_val = play_item['player_info'].get('show', '').strip().lower()
for idx, kw in enumerate(self.line_order):
if kw in show_val:
return idx
return float('inf')
vod_play_list = sorted(vod_play_list, key=get_priority, reverse=False)
for i in vod_play_list:
play_url = ''
line_name = i['player_info'].get('show', '未知线路')
from_value = i['player_info'].get('from', '')
display_name = f"{line_name}({from_value})" if from_value else line_name
for j in i['urls']:
play_url += f"{j['name']}${i['player_info']['from']}@{j['url']}#"
vod_play_from += i['player_info']['show'] + '$$$'
vod_play_from += display_name + '$$$'
vod_play_url += play_url.rstrip('#') + '$$$'
vod_play_url = vod_play_url.rstrip('$$$')
vod_play_from = vod_play_from.rstrip('$$$')
videos.append({
'vod_id': data3.get('vod_id'),
'vod_name': data3.get('vod_name'),
'vod_content': data3.get('vod_content'),
'vod_content': data3.get('vod_content', ''),
'vod_remarks': data3.get('vod_remarks'),
'vod_director': data3.get('vod_director'),
'vod_actor': data3.get('vod_actor'),
@@ -85,7 +120,7 @@ class Spider(Spider):
response = self.fetch(url, headers=self.headers).json()
data = response['data']['data']
for i in data:
if i.get('vod_pic').startswith('mac://'):
if i.get('vod_pic', '').startswith('mac://'):
i['vod_pic'] = i['vod_pic'].replace('mac://', 'http://', 1)
return {'list': data, 'page': pg, 'total': response['data']['total']}
@@ -141,17 +176,17 @@ class Spider(Spider):
home_cate = config['get_home_cate']
classes = []
for i in home_cate:
if isinstance(i.get('extend', []),dict):
if isinstance(i.get('extend', []), dict):
classes.append({'type_id': i['cate'], 'type_name': i['title']})
return {'class': classes}
def homeVideoContent(self):
response = self.fetch(f'{self.host}/apptov5/v1/home/data?id=1&mold=1&__platform=android',headers=self.headers).json()
response = self.fetch(f'{self.host}/apptov5/v1/home/data?id=1&mold=1&__platform=android', headers=self.headers).json()
data = response['data']
vod_list = []
for i in data['sections']:
for j in i['items']:
vod_pic = j.get('vod_pic')
vod_pic = j.get('vod_pic', '')
if vod_pic.startswith('mac://'):
vod_pic = vod_pic.replace('mac://', 'http://', 1)
vod_list.append({
@@ -163,7 +198,10 @@ class Spider(Spider):
return {'list': vod_list}
def categoryContent(self, tid, pg, filter, extend):
response = self.fetch(f"{self.host}/apptov5/v1/vod/lists?area={extend.get('area','')}&lang={extend.get('lang','')}&year={extend.get('year','')}&order={extend.get('sort','time')}&type_id={tid}&type_name=&page={pg}&pageSize=21&__platform=android", headers=self.headers).json()
response = self.fetch(
f"{self.host}/apptov5/v1/vod/lists?area={extend.get('area','')}&lang={extend.get('lang','')}&year={extend.get('year','')}&order={extend.get('sort','time')}&type_id={tid}&type_name=&page={pg}&pageSize=21&__platform=android",
headers=self.headers
).json()
data = response['data']
data2 = data['data']
for i in data['data']:
@@ -184,4 +222,4 @@ class Spider(Spider):
pass
def localProxy(self, param):
pass
pass
-289
View File
@@ -1,289 +0,0 @@
# -*- coding: utf-8 -*-
# 基于原作者 @嗷呜 版本修改
# 本资源来源于互联网公开渠道,仅可用于个人学习爬虫技术。
# 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
from base.spider import Spider
from urllib.parse import urlparse, urlencode
import re,sys,time,json,urllib3,hashlib,datetime
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
sys.path.append('..')
class Spider(Spider):
api,apisignkey,datasignkey,detail_type,search_data = '', '' , '','',''
headers = {
'User-Agent': 'okhttp/4.12.0',
}
def init(self, extend=""):
ext = extend.rstrip()
if ext.startswith('http'):
self.api = ext.rstrip('/')
else:
arr = json.loads(ext)
self.api = arr['api'].rstrip('/')
if self.api.endswith('v1.vod'):
self.detail_type = arr.get('detail_type','')
self.apisignkey = arr.get('apisignkey', '')
if self.apisignkey:
self.datasignkey = arr.get('datasignkey', '6QQNUsP3PkD2ajJCPCY8')
def homeContent(self, filter):
if self.api.endswith('v1.vod'):
path = '/types'
if self.apisignkey and self.datasignkey:
path = self.datasign(path)
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
else:
data = self.fetch(f"{self.api}/nav?token=", headers=self.headers, verify=False).json()
keys = ["class", "area", "lang", "year", "letter", "by", "sort"]
filters = {}
classes = []
for item in data.get('list',data.get('data',[])):
has_non_empty_field = False
jsontype_extend = item["type_extend"]
classes.append({"type_name": item["type_name"], "type_id": item["type_id"]})
for key in keys:
if key in jsontype_extend and jsontype_extend[key].strip() != "":
has_non_empty_field = True
break
if has_non_empty_field:
filters[str(item["type_id"])] = []
for dkey in jsontype_extend:
if dkey in keys and jsontype_extend[dkey].strip() != "":
values = jsontype_extend[dkey].split(",")
value_array = [{"n": value.strip(), "v": value.strip()} for value in values if
value.strip() != ""]
filters[str(item["type_id"])].append({"key": dkey, "name": dkey, "value": value_array})
result = {"class": classes, "filters": filters}
return result
def homeVideoContent(self):
if self.api.endswith('v1.vod'):
path = '/vodPhbAll'
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path += self.datasign(f'?apikey={self.apikey()}&keytime={keytime}',keytime)
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
else:
data = self.fetch(f"{self.api}/index_video?token=", headers=self.headers, verify=False).json()
videos = []
if self.api.endswith('v1.vod'):
for item in data['list']: videos.extend(item['vod_list'])
elif 'list' in data:
for item in data['list']: videos.extend(item['vlist'])
elif 'data' in data:
for item in data['data']: videos.extend(item['vlist'])
return {'list': videos}
def categoryContent(self, tid, pg, filter, extend):
if self.api.endswith('v1.vod'):
path = f"?type={tid}&class={extend.get('class', '')}&lang={extend.get('lang', '')}&area={extend.get('area', '')}&year={extend.get('year', '')}&by=&page={pg}&limit=9"
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path = self.datasign(f'{path}&apikey={self.apikey()}&keytime={keytime}' ,keytime)
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
else:
params = {'tid': tid, 'class': extend.get('class', ''), 'area': extend.get('area', ''), 'lang': extend.get('lang', ''), 'year': extend.get('year', ''), 'limit': '18', 'pg': pg}
data = self.fetch(f"{self.api}/video", params=params, headers=self.headers, verify=False).json()
if 'data' in data:
data = {'list':data['data']}
return data
def searchContent(self, key, quick, pg="1"):
if self.api.endswith('v1.vod'):
path = f"?page={pg}&limit=10&wd={key}"
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path = self.datasign(f'{path}&apikey={self.apikey()}&keytime={keytime}',keytime)
else:
path = f"/search?text={key}&pg={pg}"
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
if self.api.endswith('v1.vod') and self.detail_type == 'search':
self.search_data = data
data2 = data.get('list',data.get('data',[]))
if 'type' in data2:
for item in data2:
item.pop('type', None)
if not 'list' in data2:
data2 = {'list': data2, 'page': pg}
return data2
def detailContent(self, ids):
if self.detail_type == 'search':
for i in self.search_data['data']['list']:
if str(i['vod_id']) == str(ids[0]):
data = i
break
else:
if self.api.endswith('v1.vod'):
path = f'/detail?vod_id={ids[0]}&rel_limit=10'
if self.apisignkey and self.datasignkey:
keytime = self.keytime()
path = self.datasign(f'{path}&apikey={self.apikey()}&keytime={keytime}',keytime)
else:
path = f'/video_detail?id={ids[0]}'
data = self.fetch(f"{self.api}{path}", headers=self.headers, verify=False).text
data = json.loads(data[1:] if data.startswith('\ufeff') else data)
data = data['data']
if 'vod_info' in data:
data = data['vod_info']
show = []
vod_play_url = []
if 'vod_url_with_player' in data:
for i in data['vod_url_with_player']:
if i['code'] == i['name']:
show.append(i['name'])
else:
show.append(f"{i['name']}\u2005({i['code']})")
parse_api = i.get('parse_api','')
if parse_api and parse_api.startswith('http'):
url = i.get('url','')
if url:
url2 = '#'.join([i+ '@' + parse_api for i in url.split('#')])
vod_play_url.append(url2)
else:
vod_play_url.append(i.get('url', ''))
data.pop('vod_url_with_player')
if 'vod_play_list' in data:
vod_play_list = data['vod_play_list']
for i in vod_play_list.values() if isinstance(vod_play_list, dict) else vod_play_list:
parses = ''
player_info = i['player_info']
if player_info['show'] == i['from']:
show.append(player_info['show'])
else:
show.append(f"{player_info['show']}\u2005({i['from']})")
parse = player_info.get('parse','').strip()
parse2 = player_info.get('parse2','').strip()
if 'parse' in player_info and parse.startswith('http'):
parses += parse + ','
if 'parse2' in player_info and parse2.startswith('http') and parse2 != parse:
parses += parse2
parses = parses.rstrip(',')
url = ''
urls = i['urls']
for j in urls.values() if isinstance(urls, dict) else urls:
if parse:
url += f"{j['name']}${j['url']}@{parses}#"
else:
url += f"{j['name']}${j['url']}#"
url = url.rstrip('#')
vod_play_url.append(url)
if 'vod_play_list' in data:
data.pop('vod_play_list')
if 'rel_vods' in data:
data.pop('rel_vods')
if 'type' in data:
data.pop('type')
if show:
data['vod_play_from'] = '$$$'.join(show)
if vod_play_url:
data['vod_play_url'] = '$$$'.join(vod_play_url)
return {'list': [data]}
def playerContent(self, flag, id, vipFlags):
video_pattern = re.compile(r'https?:\/\/.*\.(?:m3u8|mp4|flv)')
jx,url,ua = 0,'','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
if '@' in id:
rawurl, jxapi = id.split('@', 1)
jxapis = jxapi.split(',', 1) if ',' in jxapi else [jxapi]
for jxapi_ in jxapis:
try:
res = self.fetch(f"{jxapi_}{rawurl}", headers=self.headers, timeout=10, verify=False).text
res = json.loads(res[1:] if res.startswith('\ufeff') else res)
url = res.get('url', '')
if url.startswith('http'):
jxua = res.get('ua')
if jxua:
ua = jxua
except Exception:
url = ''
continue
if url.startswith('http'):
jx = 0
else:
url = rawurl
jx = 0 if video_pattern.match(rawurl) else 1
else:
url = id
jx = 0 if video_pattern.match(id) else 1
if url.startswith('NBY'):
jx, url = 0,''
return {'jx': jx,'parse': 0,'url': url,'header': {'User-Agent': ua}}
def keytime(self):
return str(int(datetime.datetime.now().timestamp()))
def md5(self, str):
hash_obj = hashlib.md5()
hash_obj.update(str.encode('utf-8'))
return hash_obj.hexdigest()
def apikey(self):
date = datetime.datetime.now()
year = str(date.year)
hour = str(date.hour)
minute = str(date.minute)
if len(hour) < 2:
hour = "0" + hour
if len(minute) < 2:
minute = "0" + minute
str_value = self.apisignkey
sign_str = f"{year}:{hour}:{year}:{minute}:{str_value}"
md5_hash = self.md5(sign_str)
return md5_hash
def datasign(self, url='', timestamp=''):
parsed_url = urlparse(url)
query_params = self._parse_query_params(parsed_url.query)
if not timestamp:
timestamp = str(time.time())
query_params["timestamp"] = timestamp
sorted_params = sorted(query_params.items(), key=lambda x: x[0])
sign = self._generate_signature(sorted_params)
query_params["datasign"] = sign
new_query = urlencode(query_params)
new_url = parsed_url._replace(query=new_query).geturl()
return new_url
def _parse_query_params(self, query_str):
params = {}
if not query_str:
return params
for param in query_str.split('&'):
if '=' not in param:
continue
key, value = param.split('=', 1)
if value:
params[key] = value
return params
def _generate_signature(self, sorted_params):
param_str = '&'.join([f"{k}={v}" for k, v in sorted_params])
raw_sign_str = f"{param_str}{self.datasignkey}"
md5_hash = hashlib.md5(raw_sign_str.encode('utf-8')).hexdigest()
return md5_hash
def localProxy(self, param):
pass
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass