187 lines
6.7 KiB
Python
187 lines
6.7 KiB
Python
import sys
|
|
import re
|
|
import requests
|
|
from bs4 import BeautifulSoup
|
|
from base.spider import Spider
|
|
from urllib.parse import urljoin, quote,unquote
|
|
|
|
class Spider(Spider):
|
|
def getName(self):
|
|
return "WhosTV"
|
|
|
|
def init(self, extend=""):
|
|
self.host = "https://whos.tv"
|
|
self.header = {
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
|
"Referer": self.host
|
|
}
|
|
|
|
def _decode_cover(self, encoded):
|
|
"""解密 data-cover-src(还原前端 coolDecrypt 函数)"""
|
|
if not encoded:
|
|
return ""
|
|
try:
|
|
key_hex = encoded[-2:]
|
|
key = int(key_hex, 16)
|
|
data_hex = encoded[:-2]
|
|
chars = []
|
|
for i in range(0, len(data_hex), 2):
|
|
byte_val = int(data_hex[i:i+2], 16)
|
|
chars.append(chr(byte_val ^ key))
|
|
return ''.join(chars)
|
|
except:
|
|
return ""
|
|
|
|
|
|
def homeContent(self, filter):
|
|
result = {}
|
|
result['class'] = [
|
|
{'type_name': '影片库', 'type_id': '/videos'},
|
|
{'type_name': '女优库', 'type_id': '/actresses'}
|
|
]
|
|
return result
|
|
|
|
def categoryContent(self, tid, pg, filter, extend):
|
|
result = {}
|
|
url = f"{self.host}{tid}"
|
|
if int(pg) > 1:
|
|
url += f"/page-{pg}"
|
|
|
|
rsp = self.fetch(url, headers=self.header)
|
|
soup = BeautifulSoup(rsp.text, 'html.parser')
|
|
videos = []
|
|
|
|
# 女优名录页
|
|
if tid == "/actresses":
|
|
items = soup.find_all('a', href=re.compile(r'^/actresses/.'))
|
|
for item in items:
|
|
img = item.find('img')
|
|
if not img:
|
|
continue
|
|
name = img.get('alt', '').strip()
|
|
href = item.get('href')
|
|
if not name or href == "/actresses" or "page-" in href:
|
|
continue
|
|
pic_url = img.get('src', '')
|
|
count_text = ""
|
|
icon_span = item.find('span', class_=re.compile(r'icon-\[lucide--film\]'))
|
|
if icon_span:
|
|
parent_flex = icon_span.find_parent('span', class_='flex')
|
|
if parent_flex:
|
|
count_text = parent_flex.get_text(strip=True) + "部作品"
|
|
videos.append({
|
|
"vod_id": href,
|
|
"vod_name": name,
|
|
"vod_pic": pic_url,
|
|
"vod_remarks": count_text if count_text else "作品集",
|
|
"vod_tag": "folder"
|
|
})
|
|
# 影片网格页(包括女优个人页的作品列表)
|
|
else:
|
|
items = soup.find_all('a', href=re.compile(r'^/videos/.'))
|
|
for item in items:
|
|
h3 = item.find('h3')
|
|
v_name = h3.get_text(strip=True) if h3 else item.get('alt', '')
|
|
if not v_name:
|
|
continue
|
|
div_cover = item.find('div', attrs={'data-cover-src': True})
|
|
if div_cover:
|
|
encoded = div_cover.get('data-cover-src')
|
|
real_pic = self._decode_cover(encoded)
|
|
else:
|
|
real_pic = ""
|
|
real_pic = real_pic
|
|
remarks = self.regStr(v_name, r'([A-Z0-9]+-[0-9]+)')
|
|
videos.append({
|
|
"vod_id": item.get('href'),
|
|
"vod_name": v_name,
|
|
"vod_pic": real_pic,
|
|
"vod_remarks": remarks if remarks else ""
|
|
})
|
|
|
|
result['list'] = videos
|
|
result['page'] = pg
|
|
result['pagecount'] = 999
|
|
result['limit'] = len(videos)
|
|
result['total'] = 9999
|
|
|
|
if tid.startswith("/actresses/"):
|
|
h1_tag = soup.find('h1')
|
|
if h1_tag:
|
|
result['type_name'] = h1_tag.get_text(strip=True)
|
|
return result
|
|
|
|
def detailContent(self, ids):
|
|
vodId = ids[0]
|
|
if vodId.startswith("/actresses/"):
|
|
return self.categoryContent(vodId, "1", None, None)
|
|
|
|
url = self.host + vodId
|
|
rsp = self.fetch(url, headers=self.header)
|
|
soup = BeautifulSoup(rsp.text, 'html.parser')
|
|
|
|
title_meta = soup.find('meta', property="og:title")
|
|
title = title_meta.get('content') if title_meta else ""
|
|
|
|
pic_meta = soup.find('meta', property="og:image")
|
|
pic = pic_meta.get('content') if pic_meta else ""
|
|
pic = pic
|
|
|
|
source = soup.find('source', type="application/x-mpegURL")
|
|
play_url = source.get('src') if source else ""
|
|
|
|
actor_tags = soup.select('a[href^="/actresses/"]')
|
|
actors = ",".join([a.get_text(strip=True) for a in actor_tags if a.get_text(strip=True)])
|
|
|
|
tag_tags = soup.select('a[href^="/tags/"] span.truncate')
|
|
tags = ",".join([t.get_text(strip=True) for t in tag_tags])
|
|
|
|
vod = {
|
|
"vod_id": vodId,
|
|
"vod_name": title,
|
|
"vod_pic": pic,
|
|
"type_name": tags,
|
|
"vod_actor": actors,
|
|
"vod_content": title,
|
|
"vod_play_from": "WhosTV",
|
|
"vod_play_url": "全高清$" + play_url if play_url else ""
|
|
}
|
|
return {'list': [vod]}
|
|
|
|
def playerContent(self, flag, id, vipFlags):
|
|
return {
|
|
"parse": 0,
|
|
"url": id,
|
|
"header": {
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
|
"Referer": "https://whos.tv/",
|
|
"Origin": "https://whos.tv"
|
|
}
|
|
}
|
|
|
|
def searchContent(self, key, quick):
|
|
url = f"{self.host}/result?serach={key}"
|
|
rsp = self.fetch(url, headers=self.header)
|
|
soup = BeautifulSoup(rsp.text, 'html.parser')
|
|
videos = []
|
|
|
|
items = soup.find_all('a', href=re.compile(r'^/videos/.'))
|
|
for item in items:
|
|
h3 = item.find('h3')
|
|
if h3:
|
|
v_name = h3.get_text(strip=True)
|
|
div_cover = item.find('div', attrs={'data-cover-src': True})
|
|
if div_cover:
|
|
encoded = div_cover.get('data-cover-src')
|
|
real_pic = self._decode_cover(encoded)
|
|
else:
|
|
real_pic = ""
|
|
real_pic = real_pic
|
|
remarks = self.regStr(v_name, r'([A-Z0-9]+-[0-9]+)')
|
|
videos.append({
|
|
"vod_id": item.get('href'),
|
|
"vod_name": v_name,
|
|
"vod_pic": real_pic,
|
|
"vod_remarks": remarks if remarks else ""
|
|
})
|
|
return {"list": videos} |