#!/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'