diff --git a/txt/hipy/base_spider.py b/txt/hipy/base_spider.py index 030595d..e8bcfdf 100644 --- a/txt/hipy/base_spider.py +++ b/txt/hipy/base_spider.py @@ -205,7 +205,7 @@ class Spider(BaseSpider): # 元类 默认的元类 type } return result - def searchContent(self, wd, quick=False): + def searchContent(self, wd, quick=False, pg=1): """ 返回搜索列表 @param wd: 搜索关键词 diff --git a/txt/hipy/cctv_spider.py b/txt/hipy/cctv_spider.py index bc6be6b..4dcac08 100644 --- a/txt/hipy/cctv_spider.py +++ b/txt/hipy/cctv_spider.py @@ -77,8 +77,6 @@ class Spider(BaseSpider): # 元类 默认的元类 type # ==================== 获取特别节目的筛选条件 ====================== r = self.fetch('https://tv.cctv.com/yxg/tbjm/index.shtml') html = r.text - # html = self.webReadFile(urlStr='https://tv.cctv.com/yxg/tbjm/index.shtml', header=self.header) - # print(html) html = self.html(html) filter_tbjm = [] @@ -336,7 +334,7 @@ class Spider(BaseSpider): # 元类 默认的元类 type url = 'https://tv.cctv.com/epg/index.shtml' videos = [] - htmlText = self.webReadFile(urlStr=url, header=self.header) + htmlText = self.fetch(url).text if tid == '栏目大全': index = htmlText.rfind(');') if index > -1: @@ -368,11 +366,13 @@ class Spider(BaseSpider): # 元类 默认的元类 type 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.webReadFile(urlStr=lastUrl, header=self.header) + htmlTxt = self.fetch(lastUrl).text topicId = json.loads(htmlTxt)['ctid'] Url = "https://api.cntv.cn/NewVideo/getVideoListByColumn?id={0}&d=&p=1&n=100&sort=desc&mode=0&serviceId=tvcctv&t=json".format( topicId) - htmlTxt = self.webReadFile(urlStr=Url, header=self.header) + # htmlTxt = self.webReadFile(urlStr=Url, header=self.header) + htmlTxt = self.fetch(Url).text else: Url = 'https://api.cntv.cn/NewVideo/getVideoListByAlbumIdNew?id={0}&serviceId=tvcctv&p=1&n=100&mode=0&pub=1'.format( id) @@ -383,13 +383,15 @@ class Spider(BaseSpider): # 元类 默认的元类 type fromId = '中央台' videoList = [title + "$" + lastVideo] else: - htmlTxt = self.webReadFile(urlStr=Url, header=self.header) + # 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.webReadFile(urlStr=lastVideo, header=self.header) + htmlTxt = self.fetch(lastVideo).text if tid == "电视剧" or tid == "纪录片": patternTxt = r"'title':\s*'(?P.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'url':\s*'(?P<url>.+?)'" elif tid == "特别节目": @@ -444,11 +446,12 @@ class Spider(BaseSpider): # 元类 默认的元类 type returnTxt.append(value) return returnTxt - def searchContent(self, key, quick): + 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.webReadFile(urlStr=Url, header=self.header) + htmlTxt = self.fetch(Url).text videos = self.get_list_search(html=htmlTxt, tid='搜索') result = { 'list': videos @@ -466,8 +469,9 @@ class Spider(BaseSpider): # 元类 默认的元类 type url = self.get_m3u8(urlTxt=id) else: try: - html = self.webReadFile(urlStr=id, header=self.header) - guid = self.get_RegexGetText(Text=html, RegexText=r'var\sguid\s*=\s*"(.+?)";', Index=1) + # 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 @@ -503,7 +507,7 @@ 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": "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"}, @@ -562,7 +566,7 @@ class Spider(BaseSpider): # 元类 默认的元类 type {"n": "军事", "v": "军事"}, {"n": "探索", "v": "探索"}, {"n": "社会", "v": "社会"}, {"n": "时政", "v": "时政"}, {"n": "经济", "v": "经济"}, {"n": "科技", "v": "科技"}]}, {"key": "datanf-year", "name": "年份", - "value": [{"n": "全部", "v": ""}, {"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"}, @@ -640,29 +644,7 @@ class Spider(BaseSpider): # 元类 默认的元类 type {"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": "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": "month", "name": "月份", - "value": [{"n": "全部", "v": ""}, - {"n": "12", "v": "12"}, - {"n": "11", "v": "11"}, - {"n": "10", "v": "10"}, - {"n": "09", "v": "09"}, - {"n": "08", "v": "08"}, - {"n": "07", "v": "07"}, - {"n": "06", "v": "06"}, - {"n": "05", "v": "05"}, - {"n": "04", "v": "04"}, - {"n": "03", "v": "03"}, - {"n": "02", "v": "02"}, - {"n": "01", "v": "01"}]}] + ] } } header = { @@ -679,7 +661,7 @@ class Spider(BaseSpider): # 元类 默认的元类 type def webReadFile(self, urlStr, header): html = '' req = urllib.request.Request(url=urlStr) # ,headers=header - with urllib.request.urlopen(req) as response: + with urllib.request.urlopen(req) as response: html = response.read().decode('utf-8') return html @@ -754,8 +736,9 @@ class Spider(BaseSpider): # 元类 默认的元类 type # 取m3u8 def get_m3u8(self, urlTxt): url = "https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid={0}".format(urlTxt) - html = self.webReadFile(urlStr=url, header=self.header) - jo = json.loads(html) + # htmlTxt = self.webReadFile(urlStr=url, header=self.header) + htmlTxt = self.fetch(url).text + jo = json.loads(htmlTxt) link = jo['hls_url'].strip() html = self.webReadFile(urlStr=link, header=self.header) content = html.strip() @@ -874,7 +857,10 @@ if __name__ == '__main__': spider = Spider() spider.init() 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'}, {}) diff --git a/txt/hipy/樱花动漫.json b/txt/hipy/樱花动漫.json new file mode 100644 index 0000000..058d5ce --- /dev/null +++ b/txt/hipy/樱花动漫.json @@ -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": ""}]}]} \ No newline at end of file diff --git a/txt/hipy/樱花动漫.py b/txt/hipy/樱花动漫.py new file mode 100644 index 0000000..84048bd --- /dev/null +++ b/txt/hipy/樱花动漫.py @@ -0,0 +1,501 @@ +#!/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 +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 + +""" +配置示例: +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":1, + "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(json.dumps(ext_file_dict, ensure_ascii=False)) + + def init(self, extend=""): + """ + 初始化加载extend,一般与py文件名同名的json文件作为扩展筛选 + @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: + self.init_extend(self.api_qj) + + 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, param): + return [200, "video/MP2T", action, ""] + + # -----------------------------------------------自定义函数----------------------------------------------- + def decode(self, text): + return json.loads(self.aes_cbs_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 + + +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 + spider = Spider() + # spider.init('https://jihulab.com/qiaoji/open/-/raw/main/yinghua') + spider.init() + # 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('斗罗大陆')