#!/usr/bin/python import sys sys.path.append('..') from base.spider import Spider import json import urllib.parse import re class Spider(Spider): def getName(self): return "Avple.tv" def init(self, extend=""): self.host = "https://avple.tv" self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Referer': self.host } self.log(f"{self.getName()} 爬虫初始化完成,主站: {self.host}") def isVideoFormat(self, url): return False def manualVideoCheck(self): return True def homeContent(self, filter): """获取首页内容和分类""" result = {} classes = self._getCategories() result['class'] = classes try: rsp = self.fetch(self.host, headers=self.headers) html = rsp.text videos = self._getVideos(html) result['list'] = videos except Exception as e: self.log(f"首页获取出错: {str(e)}") result['list'] = [] return result def homeVideoContent(self): """首页视频内容(可留空)""" return {'list': []} def categoryContent(self, tid, pg, filter, extend): """分类内容""" try: pg_int = int(pg) if tid == 'trending': url = f"{self.host}/trending/daily/{pg_int}" elif tid == 'latest': url = f"{self.host}/latest/{pg_int}" else: url = f"{self.host}/tags/{str(tid)}/{pg_int}/date" self.log(f"访问分类URL: {url}") rsp = self.fetch(url, headers=self.headers) html = rsp.text videos = self._getVideos(html) pagecount = 999 limit = 24 total = 999999 if not videos and pg_int == 1: self.log(f"警告: 分类ID {tid}, 页码 {pg} 未找到任何视频。URL: {url}") elif not videos: self.log(f"信息: 分类ID {tid}, 页码 {pg} 没有更多视频。URL: {url}") return { 'list': videos, 'page': pg_int, 'pagecount': pagecount, 'limit': limit, 'total': total } except Exception as e: self.log(f"分类内容获取出错 (tid={tid}, pg={pg}): {str(e)}") return {'list': []} def searchContent(self, key, quick, pg="1"): """搜索功能""" try: search_url = f"{self.host}/search/{urllib.parse.quote(key)}/{pg}" self.log(f"搜索URL: {search_url}") rsp = self.fetch(search_url, headers=self.headers) html = rsp.text videos = self._getVideos(html) return {'list': videos} except Exception as e: self.log(f"搜索出错: {str(e)}") return {'list': []} def detailContent(self, ids): """详情页面""" try: vid = ids[0] detail_url = f"{self.host}/video/{vid}" self.log(f"详情URL: {detail_url}") rsp = self.fetch(detail_url, headers=self.headers) html = rsp.text video_info = self._getDetail(html, vid) return {'list': [video_info]} if video_info else {'list': []} except Exception as e: self.log(f"详情获取出错 (vid: {ids[0]}): {str(e)}") return {'list': []} def playerContent(self, flag, id, vipFlags): """播放链接解析""" try: video_url = f"https://s4.avple.tv/video/{id}" self.log(f"✅ 找到视频直链: {video_url}") return { 'parse': 0, 'playUrl': '', 'url': video_url, 'header': json.dumps(self.headers) } except Exception as e: self.log(f"播放链接获取出错 (id: {id}): {str(e)}") return {'parse': 1, 'playUrl': '', 'url': f"{self.host}/video/{id}"} def _getCategories(self): """从首页提取分类""" categories = [] try: categories.append({'type_id': 'trending', 'type_name': '近期热门'}) categories.append({'type_id': 'latest', 'type_name': '最新影片'}) rsp = self.fetch(self.host, headers=self.headers) html = rsp.text category_pattern = r']*?href="(/tags/(\d+)/1/date)"[^>]*>([^<]+?)' matches = re.findall(category_pattern, html, re.IGNORECASE) for _, cat_id, cat_name in matches: if not any(c['type_id'] == cat_id for c in categories): categories.append({'type_id': cat_id, 'type_name': cat_name.strip()}) if len(categories) < 5: self.log("动态获取分类失败或分类过少,使用硬编码分类。") hardcoded_categories = [ {'type_id': '121', 'type_name': '麻豆傳媒'}, {'type_id': '123', 'type_name': '果凍傳媒'}, {'type_id': '124', 'type_name': '皇家華人'}, {'type_id': '125', 'type_name': '精東影業'}, {'type_id': '126', 'type_name': '天美傳媒'}, {'type_id': '127', 'type_name': '星空無限傳媒'}, {'type_id': '128', 'type_name': '樂播傳媒'}, {'type_id': '129', 'type_name': '蜜桃傳媒'}, {'type_id': '130', 'type_name': '烏鴉傳媒'}, {'type_id': '131', 'type_name': '國產自拍'}, {'type_id': '122', 'type_name': 'SWAG'}, {'type_id': '135', 'type_name': 'FC2PPV'}, {'type_id': '15', 'type_name': '黑絲'}, {'type_id': '113', 'type_name': '旗袍'}, {'type_id': '49', 'type_name': '校服'}, {'type_id': '13', 'type_name': '絲襪'}, {'type_id': '97', 'type_name': '女僕'}, {'type_id': '76', 'type_name': '吊帶襪'}, {'type_id': '87', 'type_name': '兔女郎'}, {'type_id': '1', 'type_name': '巨乳'}, {'type_id': '63', 'type_name': '貧乳'}, {'type_id': '79', 'type_name': '露出'}, {'type_id': '2', 'type_name': '中出'}, {'type_id': '32', 'type_name': '顏射'}, {'type_id': '18', 'type_name': '潮吹'}, {'type_id': '53', 'type_name': '綑綁'}, {'type_id': '33', 'type_name': '多P'} ] for hc in hardcoded_categories: if not any(c['type_id'] == hc['type_id'] for c in categories): categories.append(hc) return categories except Exception as e: self.log(f"获取分类出错: {str(e)}") return [{'type_id': 'trending', 'type_name': '近期热门'}, {'type_id': 'latest', 'type_name': '最新影片'}] def _getVideos(self, html): """从HTML中提取视频列表""" videos = [] try: # 尝试匹配首页的JSON-LD结构(最稳定) json_ld_match = re.search(r'