# -*- coding: utf-8 -*- # 本资源来源于互联网公开渠道,仅可用于个人学习爬虫技术。 # 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。 from Crypto.Cipher import AES from base.spider import Spider from Crypto.Util.Padding import unpad import re,sys,time,json,base64,hashlib,urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) sys.path.append('..') class Spider(Spider): headers,player,host,name,data_key,data_iv,cms_host,jx_api = { 'User-Agent': 'Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 MUOUAPP/10.8.4506.400', 'Accept-Encoding': 'gzip', 'brand-model': 'xiaomi', 'app-device': 'nodata', 'app-time': '', 'sys-version': '12', 'device': '811397239bddf2e7', 'os': 'Android', 'app-version': '' },{},'', '', '', '', '', '' def init(self, extend=''): try: try: config = json.loads(extend) except (json.JSONDecodeError, TypeError): config = {} name = config.get('name', 'muou') self.headers['app-version'] = config.get('version', '4.2.0') self.host = config['host'] if not re.match(r'^https?:\/\/[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(:\d+)?(\/)?$', self.host): self.host = self.fetch(self.host, headers=self.headers, timeout=10, verify=False).text.rstrip('/') timestamp = int(time.time()) self.headers['app-time'] = str(timestamp) api = config.get('api', '/app_info.php') salt = '' if api.endswith('app_info.php'): salt = 'muouapp' inner_sha1 = hashlib.sha1(f"{timestamp}{name}".encode('utf-8')).hexdigest() outer_sha1 = hashlib.sha1(f"{timestamp}{inner_sha1}{salt}".encode('utf-8')).hexdigest() payload = {'t': timestamp, 'n': inner_sha1, 'm': outer_sha1} res = self.post(f'{self.host}{api}', data=payload, headers=self.headers, verify=False).json() data = res['data'] a,e,s = res.get('a'),res.get('e'),res.get('s') if a and e and s: data = self.t(data, s, e) else: a = str(res['time']) key = hashlib.md5(a.encode('utf-8')).hexdigest()[:16] iv = hashlib.md5(outer_sha1.encode('utf-8')).hexdigest()[:16] dat3 = json.loads(self.decrypt(data, key, iv)) self.data_key = hashlib.md5(dat3['key'].encode('utf-8')).hexdigest()[:16] self.data_iv = hashlib.md5(dat3['iv'].encode('utf-8')).hexdigest()[:16] self.cms_host = dat3['HBqq'].rstrip('/') jx_api = dat3.get('HBrjjg', '') if jx_api.startswith('http'): self.jx_api = jx_api except Exception: self.cms_host = '' def homeContent(self, filter): if not self.cms_host: return {'list': []} self.headers['app-time'] = str(int(time.time())) try: response = self.fetch(f'{self.cms_host}/api.php/v1.vod/types', headers=self.headers, verify=False).text except Exception as e: return {"class": [], "filters": {}} try: data = json.loads(response) or {} except json.JSONDecodeError: try: data = json.loads(self.decrypt(response)) except (json.JSONDecodeError, TypeError): return {"class": [], "filters": {}} filter_keys = {"class", "area", "lang", "year", "letter", "by", "sort"} filters, classes = {}, [] typelist = data.get('data', {}).get('typelist', []) for item in typelist: type_id = str(item["type_id"]) classes.append({"type_name": item["type_name"], "type_id": type_id}) extend = item.get("type_extend", {}) type_filters = [] for key, value_str in extend.items(): if key not in filter_keys: continue stripped = value_str.strip() if not stripped: continue values = [v.strip() for v in stripped.split(",") if v.strip()] if not values: continue type_filters.append({"key": key, "name": key, "value": [{"n": v, "v": v} for v in values] }) if type_filters: filters[type_id] = type_filters return {"class": classes, "filters": filters} def homeVideoContent(self): if not self.cms_host: return {'list': []} self.headers['app-time'] = str(int(time.time())) response = self.fetch(f'{self.cms_host}/api.php/v1.vod/HomeIndex?page=&limit=6', headers=self.headers, verify=False).text try: data = json.loads(response) except (json.JSONDecodeError, TypeError): data_ = self.decrypt(response) data = json.loads(data_) videos = [] for i in data['data']: if i.get('vod_list'): vod_list = i['vod_list'] for j in vod_list: pic = j.get('vod_pic') if pic: if not pic.startswith('http'): j['vod_pic'] = self.cms_host + pic videos.extend(vod_list) return {'list': videos} def categoryContent(self, tid, pg, filter, extend): if not self.cms_host: return {'list': []} self.headers['app-time'] = str(int(time.time())) response = self.fetch(f"{self.cms_host}/api.php/v1.vod?type={tid}&class={extend.get('class', '')}&area={extend.get('area', '')}&year={extend.get('year', '')}&by=time&page={pg}&limit=18", headers=self.headers, verify=False).text try: data = json.loads(response) except (json.JSONDecodeError, TypeError): data_ = self.decrypt(response) data = json.loads(data_) videos = data['data']['list'] for item in data['data']['list']: pic = item.get('vod_pic', '') if pic: if not pic.startswith('http'): item['vod_pic'] = self.cms_host + pic item.pop('type', None) return {'list': videos, 'page': pg} def searchContent(self, key, quick, pg="1"): if not self.cms_host: return {'list': []} self.headers['app-time'] = str(int(time.time())) response = self.fetch(f'{self.cms_host}/api.php/v1.vod?wd={key}&limit=18&page={pg}', headers=self.headers, verify=False).text try: data = json.loads(response) except (json.JSONDecodeError, TypeError): data_ = self.decrypt(response) data = json.loads(data_) videos = data['data']['list'] for item in data['data']['list']: item.pop('type', None) return {'list': videos, 'page': pg} def detailContent(self, ids): self.headers['app-time'] = str(int(time.time())) response = self.fetch(f'{self.cms_host}/api.php/v1.vod/detail?vod_id={ids[0]}', headers=self.headers, verify=False).text try: data = json.loads(response) except (json.JSONDecodeError, TypeError): data_ = self.decrypt(response) data = json.loads(data_) data = data['data'] if data == '': return {'list': []} show, vod_play_url = [], [] for i, j in data['vod_play_list'].items(): show.append(j['player_info']['show']) urls = j.get('urls', {}) play_url = [] if isinstance(urls, dict): for i2, j2 in urls.items(): play_url.append(f"{j2['name']}${j2['from']}@{j2['url']}") vod_play_url.append('#'.join(play_url)) data['vod_play_from'] = '$$$'.join(show) data['vod_play_url'] = '$$$'.join(vod_play_url) data.pop('vod_play_list') data.pop('type') return {'list': [data]} def playerContent(self, flag, vid, vip_flags): play_from, raw_url = vid.split('@') jx,url,playurl,play_headers = 1,raw_url,'',{'User-Agent': self.headers['User-Agent']} try: if not self.player: res = self.fetch(f'{self.host}/api.php?action=playerinfo',headers=self.headers, verify=False).text data = self.decrypt(res) self.player = json.loads(data).get('data',{}) playerinfo = self.player.get('playerinfo', []) for i in playerinfo: play_jx = i.get('playerjiekou','') if i.get('playername') == play_from and play_jx.startswith('http'): response = self.fetch(f'{play_jx}{raw_url}&playerkey={play_from}',headers=self.headers,verify=False).text try: data = json.loads(response) except (json.JSONDecodeError, TypeError): data_ = self.decrypt(response) data = json.loads(data_) if str(data.get('code','')) == '403': playurl = '' else: playurl = data['url'] jx = 0 playeruas = self.player.get('playerua', []) for j in playeruas: try: if j['player'] == play_from and re.search(j['matching'], playurl): play_headers.update(self.playerua_to_dict(j['playerua'])) break except Exception: continue except Exception: playurl = '' if playurl.startswith('http'): url = playurl else: if re.search(r'^https?[^\s]*\.(m3u8|mp4|flv)', raw_url, re.I): url = raw_url jx = 0 else: try: response = self.fetch(self.jx_api + raw_url,headers=self.headers,verify=False).text try: data = json.loads(response) except (json.JSONDecodeError, TypeError): data_ = self.decrypt(response) data = json.loads(data_) playurl = data.get('url','') if playurl.startswith('http'): jx,url = 0,playurl else: jx,url = 1,raw_url except Exception as e: jx,url = 1,raw_url if url.startswith('NBY-'): jx.url = 0,'' return {'jx': jx,'parse': 0, 'url': url,'header': play_headers} def playerua_to_dict(self, playerua_str): headers = {} lines = playerua_str.replace("\r\n", "\n").split("\n") for line in lines: if not line.strip(): continue if ':' in line: key, value = line.split(":", 1) key = key.strip().lower() value = value.strip() headers[key] = value return headers def decrypt(self,data, key='', iv=''): if not(key or iv): key = self.data_key iv = self.data_iv key_bytes = key.encode('utf-8') iv_bytes = iv.encode('utf-8') encrypted_data = base64.b64decode(data) cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes) decrypted_padded = cipher.decrypt(encrypted_data) decrypted = unpad(decrypted_padded, AES.block_size) return decrypted.decode('utf-8') def t(self, s, v, v1): if s is not None and s != '': n = len(s) if v < 0 or v1 < 0: raise ValueError("参数不能为负数") if v + v1 <= n: return s[v:n - v1] else: return '' return s def getName(self): pass def isVideoFormat(self, url): pass def manualVideoCheck(self): pass def destroy(self): pass def localProxy(self, param): pass