diff --git a/base/custom.conf b/base/custom.conf index 7e41e60..ce6fb1b 100644 --- a/base/custom.conf +++ b/base/custom.conf @@ -68,12 +68,22 @@ "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" }, { "key":"hipy_t4_两个BT", "name":"两个BT(hipy_t4)", "type":4, - "api":"http://192.168.31.49:5707/api/v1/vod/两个BT", + "api":"http://192.168.31.49:5707/api/v1/vod/两个BT?api_ext={{host}}/txt/hipy/两个BT.json", "searchable":1, "quickSearch":0, "filterable":1, diff --git a/txt/hipy/base_spider.py b/txt/hipy/base_spider.py index e8bcfdf..7d84948 100644 --- a/txt/hipy/base_spider.py +++ b/txt/hipy/base_spider.py @@ -10,7 +10,8 @@ import sys sys.path.append('..') try: - from base.spider import Spider as BaseSpider + # from base.spider import Spider as BaseSpider + from base.spider import BaseSpider except ImportError: from t4.base.spider import BaseSpider import json diff --git a/txt/hipy/cctv_spider.py b/txt/hipy/cctv_spider.py index 4dcac08..95d0d2a 100644 --- a/txt/hipy/cctv_spider.py +++ b/txt/hipy/cctv_spider.py @@ -5,7 +5,8 @@ import sys sys.path.append('..') try: - from base.spider import Spider as BaseSpider + # from base.spider import Spider as BaseSpider + from base.spider import BaseSpider except ImportError: from t4.base.spider import BaseSpider import json @@ -507,7 +508,8 @@ class Spider(BaseSpider): # 元类 默认的元类 type "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"}, + "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"}, @@ -566,7 +568,8 @@ class Spider(BaseSpider): # 元类 默认的元类 type {"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"}, + "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"}, diff --git a/txt/hipy/两个BT.json b/txt/hipy/两个BT.json new file mode 100644 index 0000000..0b5e9c6 --- /dev/null +++ b/txt/hipy/两个BT.json @@ -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"}]}, {"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"}]}]} \ No newline at end of file diff --git a/txt/hipy/两个BT.py b/txt/hipy/两个BT.py new file mode 100644 index 0000000..f507c1b --- /dev/null +++ b/txt/hipy/两个BT.py @@ -0,0 +1,509 @@ +#!/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":"两个BT" +}, +{ + "key": "hipy_t3_两个BT", + "name": "两个BT(hipy_t3)", + "type": 3, + "api": "{{host}}/txt/hipy/两个BT.py", + "searchable": 1, + "quickSearch": 0, + "filterable": 1, + "ext": "{{host}}/txt/hipy/两个BT.json" +}, +""" + + +class Spider(BaseSpider): # 元类 默认的元类 type + api: str = 'https://www.bttwo.net' + api_ext_file: str = api + '/movie_bt/' + + 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.net/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}') + + print(f"============{extend}============") + if isinstance(extend, str): + if extend.startswith('./'): + ext_file = os.path.join(os.path.dirname(__file__), extend) + init_file(ext_file) + elif extend.startswith('http'): + try: + r = self.fetch(extend) + self.config['filter'].update(r.json()) + except Exception as e: + print(f'更新扩展筛选条件发生错误:{e}') + elif extend and not extend.startswith('./') and not extend.startswith('http'): + ext_file = os.path.join(os.path.dirname(__file__), './' + extend + '.json') + init_file(ext_file) + elif 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 = '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 = [] + + 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 + } + + url = f'{self.api}/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: + """ + 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.net/" + } + + def localProxy(self, param): + return [200, "video/MP2T", action, ""] + + # -----------------------------------------------自定义函数----------------------------------------------- + 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__': + spider = Spider() + spider.init() + 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.net/movie/20107.html'])) + # print(spider.playerContent('在线播放', 'https://www.bttwo.net/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)) diff --git a/txt/hipy/樱花动漫.py b/txt/hipy/樱花动漫.py index 84048bd..4c29112 100644 --- a/txt/hipy/樱花动漫.py +++ b/txt/hipy/樱花动漫.py @@ -9,18 +9,10 @@ import sys sys.path.append('..') try: - from base.spider import Spider as BaseSpider + # 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 -import io -import tokenize -from Crypto.Cipher import AES, PKCS1_v1_5 as PKCS1_cipher -from Crypto.Util.Padding import unpad -from Crypto.PublicKey import RSA from cachetools import cached, TTLCache # 可以缓存curd的函数,指定里面的key @@ -109,7 +101,7 @@ class Spider(BaseSpider): # 元类 默认的元类 type 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(json.dumps(ext_file_dict, ensure_ascii=False)) + f.write(self.json2str(ext_file_dict)) def init(self, extend=""): """ @@ -117,11 +109,6 @@ class Spider(BaseSpider): # 元类 默认的元类 type @param extend: @return: """ - # cipher_text = 'qebgGqODxKrWujD2TaoZoyh6aeYntZLsTaZW8MQvVNORiHLLi/o/cEEEXkSs1vfHEuiURxMTr9zL2Kwbffz2pDvLCamrK1tuxIcczsaI7cfsAh5gcgyzOOdZUszoU45ZgS9Bi4HZ3RDHZAWEqP0JyTaH32oDxgO9nx/xhe1xyVRyj9y5gkfPVt2AP94I8/DwDI9CEstcPzGfRNHYxlCuGMtnMvVwzonmkNDyOw+N5xr2ZI3P8O1cj04mCEAE6/d2tGSU/o+EOyWVLWWw0R3hoduHvjAtuMbBt6WNCcsL51zWceiTcsl34acqd9Pya3lA52ZFRojME/R4zdTzMbvwrOEF3Up0eB58//+NM6k4tW+cBNAHDGgO8t7hcfSG/TnJLQ7Gigrn899dGbFeQMCEQf0zxMxfgYBDkADs0aza9N2QIXChgEDVFDvw0vE2m6b+bJfswmbrhNEIM//95XpMv68HEP3jCTwFEehattwHjyYE4fJoH3OtPO++pDiZltaxdizfFD6WmTO+v4qub1QfpUEXWHzG6nqDA9MioigiTRdRAb4PEMioBCJS5/gel+PGRja1cR1NtBjl4RKpSPstwrmczG8qKQrn2Zjwkz8f4OdnEBKRLaWzb/hVDAQgqRfUyohEdnQyocaYsnB5lJoMSp93QCzwAuHYueyMh2fNpcemWGfs58wrZr5VFSBaQDYv/HjsNntoPaCDpoQIV9DJKiqQsiYMblsz/Mdbnlfd1vI5X69TVPvItzh26zXjgpFBKsiFDOtvd3bNe92XnTH3jiDgBQbqoL6jUbvhNQapqRUZxovWk30UQ0YTQnqODkorKdrEB1IeNhKt0epurJG/G7A6DQ6/6lZ7pAwlYVlfXzFgZOGNb1Hs52j2F+sGvgWY00BAH1eHtpHPPdckL25JMKai3QDQUAVBxAI8njCmcNUPfl9kDZe62QMNJwaZKD9eaOjNrdVsKjah9gkiWL8BnDxJeGErZyrvKhyqg0fwNvyn8wEfeUlwRzfDysrvjdBz3' - # ret = self.decode_rsa(cipher_text[1:]) - # print(ret) - # extend内容进行解密后可以获取key,iv,token等参数 - # print('gParam:',gParam) if extend.startswith('http'): self.init_extend(extend) else: @@ -248,7 +235,6 @@ class Spider(BaseSpider): # 元类 默认的元类 type 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'], @@ -346,141 +332,10 @@ class Spider(BaseSpider): # 元类 默认的元类 type # -----------------------------------------------自定义函数----------------------------------------------- def decode(self, text): - return json.loads(self.aes_cbs_decode(text, self.key, self.iv)) + return self.str2json(self.aes_cbc_decode(text, self.key, self.iv)) def decode_rsa(self, text): - return json.loads(self.rsa_private_decode(text, self.private_key)) - - 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 - - @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 rsa_private_decode(ciphertext, private_key): - # 计算需要添加的等号数 - b64_ciphertext = ciphertext - num_padding = 4 - (len(b64_ciphertext) % 4) - if num_padding < 4: - b64_ciphertext += "=" * num_padding - # print(len(ciphertext), ciphertext) - # 将密文转换成byte数组 - # ciphertext = base64.b64decode(b64_ciphertext.encode("utf8")) - ciphertext = base64.b64decode(b64_ciphertext) - # print(len(ciphertext), ciphertext) - # 构建RSA解密器 - private_key = f'-----BEGIN RSA PRIVATE KEY-----\n{private_key}\n-----END RSA PRIVATE KEY-----' - pri_Key = RSA.importKey(private_key) - decrypter = PKCS1_cipher.new(pri_Key) - # 解密 - length = len(ciphertext) - default_length = 256 - # 长度不用分段 - if length < default_length: - plaintext = b''.join(decrypter.decrypt(ciphertext, b' ')) - else: - # 需要分段 - offset = 0 - res = [] - while length - offset > 0: - if length - offset > default_length: - res.append(decrypter.decrypt(ciphertext[offset:offset + default_length], b' ')) - else: - res.append(decrypter.decrypt(ciphertext[offset:], b' ')) - offset += default_length - - plaintext = b''.join(res) - return plaintext.decode('utf-8') - - @staticmethod - def rsa_public_encode(text, public_key): - public_key = "-----BEGIN RSA PRIVATE KEY-----\n" + public_key + "\n-----END RSA PRIVATE KEY-----" - pub_key = RSA.importKey(public_key) - cipher = PKCS1_cipher.new(pub_key) - text = text.encode("utf-8)") - length = len(text) - default_length = 256 - if length < default_length: - rsa_text = base64.b64encode(cipher.encrypt(text)) # 加密并转为b64编码 - else: - # 需要分段 - offset = 0 - res = [] - while length - offset > 0: - if length - offset > default_length: - res.append(cipher.encrypt(text[offset:offset + default_length])) - else: - res.append(cipher.encrypt(text[offset:])) - offset += default_length - byte_data = b''.join(res) - - rsa_text = base64.b64encode(byte_data) - - ciphertext = rsa_text.decode("utf8") - return ciphertext + return self.str2json(self.rsa_private_decode(text, self.private_key)) if __name__ == '__main__':