Sync all projects
This commit is contained in:
+54
-1
@@ -1,20 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re, urllib.parse
|
||||
import json
|
||||
import time
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
from base.spider import Spider as BaseSpider
|
||||
|
||||
|
||||
class Spider(BaseSpider):
|
||||
# 缓存变量,避免频繁请求发布页
|
||||
_cache_host = ""
|
||||
_cache_time = 0
|
||||
CACHE_DURATION = 300 # 缓存5分钟
|
||||
|
||||
def init(self, extend=""):
|
||||
self.host = "https://www.ht10010.com"
|
||||
self.headers = {
|
||||
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
}
|
||||
|
||||
# 优先读取缓存,没有或过期则重新获取
|
||||
now = time.time()
|
||||
if self._cache_host and now - self._cache_time < self.CACHE_DURATION:
|
||||
self.host = self._cache_host
|
||||
else:
|
||||
publish_url = "https://www.vip1949.com/"
|
||||
self.host = self.get_online_host(publish_url)
|
||||
self._cache_host = self.host
|
||||
self._cache_time = now
|
||||
|
||||
def get_online_host(self, publish_url):
|
||||
"""从发布页解析JS域名列表,返回第一个在线可用主站"""
|
||||
try:
|
||||
resp = requests.get(publish_url, headers=self.headers, timeout=15)
|
||||
resp.raise_for_status()
|
||||
html = resp.text
|
||||
|
||||
# 正则提取JS中的domains数组
|
||||
pattern = r'const domains = (\[.*?\]);'
|
||||
match = re.search(pattern, html, re.S)
|
||||
if not match:
|
||||
return "https://www.cd-zj.com"
|
||||
|
||||
js_str = match.group(1)
|
||||
# 修复JS对象格式为标准JSON
|
||||
js_str = re.sub(r'(\w+)\s*:', r'"\1":', js_str)
|
||||
js_str = re.sub(r'"+', '"', js_str)
|
||||
domains = json.loads(js_str)
|
||||
|
||||
# 逐个检测连通性,返回第一个可用
|
||||
for item in domains:
|
||||
url = item["url"]
|
||||
if self.check_url_online(url):
|
||||
return url.rstrip("/")
|
||||
|
||||
return "https://www.cd-zj.com"
|
||||
except Exception as e:
|
||||
print(f"获取发布页异常: {e}")
|
||||
return "https://www.cd-zj.com"
|
||||
|
||||
def check_url_online(self, url, timeout=3):
|
||||
"""检测网址是否可正常访问"""
|
||||
try:
|
||||
r = requests.head(url, headers=self.headers, timeout=timeout, allow_redirects=True)
|
||||
return 200 <= r.status_code < 400
|
||||
except:
|
||||
return False
|
||||
|
||||
def getName(self):
|
||||
return '枫叶影院'
|
||||
|
||||
|
||||
+247
-103
@@ -1,17 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# by @嗷呜
|
||||
import re
|
||||
import sys
|
||||
from pyquery import PyQuery as pq
|
||||
sys.path.append('..')
|
||||
from base.spider import Spider
|
||||
import requests
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
host = "https://mov.cenguigui.cn"
|
||||
base_url = host + "/duanju/api.php"
|
||||
quality_host = "https://mov.cenguigui.cn"
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36'
|
||||
}
|
||||
timeout = 10
|
||||
|
||||
class Spider(Spider):
|
||||
|
||||
def init(self, extend=""):
|
||||
pass
|
||||
|
||||
def getName(self):
|
||||
return "小心儿悠悠"
|
||||
|
||||
def init(self, extend):
|
||||
pass
|
||||
|
||||
def isVideoFormat(self, url):
|
||||
@@ -20,108 +24,248 @@ class Spider(Spider):
|
||||
def manualVideoCheck(self):
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
pass
|
||||
|
||||
host='https://www.hongguodj.cc'
|
||||
|
||||
headers = {
|
||||
'Accept': '*/*',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'DNT': '1',
|
||||
'Origin': host,
|
||||
'Pragma': 'no-cache',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
|
||||
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="134", "Google Chrome";v="134"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"macOS"',
|
||||
}
|
||||
|
||||
def homeContent(self, filter):
|
||||
result = {}
|
||||
class_names = "推荐榜&热播榜&新剧榜&漫剧榜大唐&大秦&大明&擦边&逆袭&霸总&豪门恩怨&神豪&都市日常&大女主&都市修仙&强者回归&重生&闪婚&赘婿逆袭&追妻&萌宝&奇幻脑洞&传承觉醒&奇幻爱情&乡村&历史古代&王妃&娱乐圈&暗恋成真&系统&真假千金&穿书&女帝&团宠&年代爱情&玄幻仙侠&皇后&逆袭&霸总&现代言情&打脸虐渣&豪门恩怨&神豪&马甲&都市日常&战神归来&小人物&女性成长&大女主&穿越&都市修仙&强者回归&亲情&古装&重生&闪婚&赘婿逆袭&虐恋&追妻&天下无敌&家庭伦理&萌宝&古风权谋&职场&奇幻脑洞&异能&无敌神医&古风言情&传承觉醒&现言甜宠&奇幻爱情&乡村&历史古代&王妃&高手下山&娱乐圈&强强联合&破镜重圆&暗恋成真&民国&欢喜冤家&系统&真假千金&龙王&校园&穿书&女帝&团宠&年代爱情&玄幻仙侠&青梅竹马&悬疑推理&皇后&替身&大叔&喜剧&剧情"
|
||||
class_list = class_names.split('&')
|
||||
|
||||
classes = []
|
||||
vlist = []
|
||||
data = pq(self.fetch(self.host, headers=self.headers).text)
|
||||
for i in list(data('.slip li').items())[1:]:
|
||||
for class_name in class_list:
|
||||
classes.append({
|
||||
'type_name': i.text(),
|
||||
'type_id': re.findall(r'\d+', i('a').attr('href'))[0]
|
||||
"type_id": class_name,
|
||||
"type_name": class_name
|
||||
})
|
||||
for i in data('.wrap .rows').items():
|
||||
vlist.extend(self.getlist(i('li')))
|
||||
result['class'] = classes
|
||||
result['list'] = vlist
|
||||
return result
|
||||
|
||||
return {"class": classes}
|
||||
|
||||
def homeVideoContent(self):
|
||||
pass
|
||||
return {'list': []}
|
||||
|
||||
def categoryContent(self, tid, pg, filter, extend):
|
||||
data=pq(self.fetch(f'{self.host}/type/{tid}-{pg}.html', headers=self.headers).text)
|
||||
result = {}
|
||||
result['list'] = self.getlist(data('.list ul li'))
|
||||
result['page'] = pg
|
||||
result['pagecount'] = 9999
|
||||
result['limit'] = 90
|
||||
result['total'] = 999999
|
||||
return result
|
||||
def categoryContent(self, cid, pg, filter, ext):
|
||||
videos = []
|
||||
page = int(pg) if pg else 1
|
||||
|
||||
params = f"page={page}&name={urllib.parse.quote(cid)}"
|
||||
|
||||
tab_type = "19"
|
||||
if ext and 'tab_type' in ext:
|
||||
tab_type = ext['tab_type']
|
||||
params += f"&tab_type={tab_type}"
|
||||
|
||||
url = f"{base_url}?{params}"
|
||||
|
||||
try:
|
||||
response = requests.get(url=url, headers=headers, timeout=timeout)
|
||||
if response.status_code != 200:
|
||||
return {'list': []}
|
||||
|
||||
response.encoding = "utf-8"
|
||||
data = response.json()
|
||||
|
||||
if data.get('code') == 200 and data.get('data'):
|
||||
for vod in data['data']:
|
||||
vod_id = f"book_id={vod.get('book_id', '')}&actor={vod.get('author', '')}&type={vod.get('type', '')}"
|
||||
videos.append({
|
||||
"vod_id": vod_id,
|
||||
"vod_name": vod.get('title', ''),
|
||||
"vod_pic": vod.get('cover', ''),
|
||||
"vod_remarks": vod.get('type', ''),
|
||||
"vod_content": vod.get('intro', '')
|
||||
})
|
||||
except Exception:
|
||||
return {'list': []}
|
||||
|
||||
return {
|
||||
'list': videos,
|
||||
'page': pg,
|
||||
'pagecount': 9999,
|
||||
'limit': 20,
|
||||
'total': 999999
|
||||
}
|
||||
|
||||
def detailContent(self, ids):
|
||||
data=pq(self.fetch(f'{self.host}{ids[0]}', headers=self.headers).text)
|
||||
v=data('.info')
|
||||
p=v('p')
|
||||
vod = {
|
||||
'vod_name': v('h1').text(),
|
||||
'type_name': p.eq(2).text(),
|
||||
'vod_year': p.eq(3).text(),
|
||||
'vod_area': p.eq(4).text(),
|
||||
'vod_remarks': v('em').text(),
|
||||
'vod_actor': p.eq(0).text(),
|
||||
'vod_director': p.eq(1).text(),
|
||||
'vod_content': data('#desc .text').text(),
|
||||
'vod_play_from': '',
|
||||
'vod_play_url': ''
|
||||
}
|
||||
names = [i.text() for i in data('.title.slip a').items()]
|
||||
plist=[]
|
||||
for i in data('.play-list ul').items():
|
||||
plist.append('#'.join([f'{j("a").text()}${j("a").attr("href")}' for j in i('li').items()]))
|
||||
vod['vod_play_from'] = '$$$'.join(names)
|
||||
vod['vod_play_url'] = '$$$'.join(plist)
|
||||
return {'list': [vod]}
|
||||
|
||||
def searchContent(self, key, quick, pg="1"):
|
||||
data=pq(self.fetch(f'{self.host}/search/{key}----------{pg}---.html', headers=self.headers).text)
|
||||
return {'list': self.getlist(data('.show.rows li')),'page':pg}
|
||||
did = ids[0]
|
||||
|
||||
params = {}
|
||||
queryString = did.split('?')[1] if '?' in did else did
|
||||
pairs = queryString.split('&')
|
||||
for i in range(len(pairs)):
|
||||
pair = pairs[i].split('=')
|
||||
if len(pair) == 2:
|
||||
params[pair[0]] = pair[1]
|
||||
|
||||
book_id = params.get('book_id', '')
|
||||
actor = params.get('actor', '')
|
||||
fullType = params.get('type', '')
|
||||
|
||||
if not book_id:
|
||||
match = re.search(r'book_id=([^&]*)', did)
|
||||
if match and match[1]:
|
||||
book_id = match[1]
|
||||
|
||||
if not book_id:
|
||||
return {'list': []}
|
||||
|
||||
apiUrl = f"{base_url}?book_id={book_id}"
|
||||
try:
|
||||
response = requests.get(url=apiUrl, headers=headers, timeout=timeout)
|
||||
if response.status_code != 200:
|
||||
return {'list': []}
|
||||
|
||||
data = response.json()
|
||||
|
||||
if data.get('code') == 200 and data.get('data'):
|
||||
vod_list = data['data']
|
||||
|
||||
play_from = []
|
||||
play_url = []
|
||||
|
||||
quality_options = [
|
||||
("超清", "2160p"),
|
||||
("高清", "1080p"),
|
||||
("标清", "720p"),
|
||||
("低清", "480p"),
|
||||
("流畅", "360p")
|
||||
]
|
||||
|
||||
for quality_name, quality_value in quality_options:
|
||||
urls = []
|
||||
|
||||
for item in vod_list:
|
||||
chapterName = item.get('title', '')
|
||||
videoId = item.get('video_id', '')
|
||||
playUrl = f"{quality_host}/duanju/api.php?video_id={videoId}&type=json&level={quality_value}"
|
||||
urls.append(f"{chapterName}${playUrl}")
|
||||
|
||||
play_from.append(quality_name)
|
||||
play_url.append("#".join(urls))
|
||||
|
||||
actors = []
|
||||
try:
|
||||
actor_api_url = f"{base_url}?series_id={book_id}&showRawParams=false"
|
||||
actor_response = requests.get(url=actor_api_url, headers=headers, timeout=timeout)
|
||||
if actor_response.status_code == 200:
|
||||
actor_data = actor_response.json()
|
||||
if actor_data.get('code') == 200 and 'celebrities' in actor_data:
|
||||
celebrities = actor_data['celebrities']
|
||||
if isinstance(celebrities, list):
|
||||
for celeb in celebrities:
|
||||
actor_name = celeb.get('user_name') or celeb.get('name') or celeb.get('actor_name') or ''
|
||||
if actor_name and actor_name.strip():
|
||||
if actor_name not in actors:
|
||||
actors.append(actor_name)
|
||||
except Exception as e:
|
||||
print(f"获取演员信息失败: {e}")
|
||||
if actor:
|
||||
actors = [actor]
|
||||
|
||||
actor_str = ", ".join(actors) if actors else (actor or "")
|
||||
|
||||
categories = []
|
||||
if 'category_names' in data and isinstance(data['category_names'], list):
|
||||
categories = data['category_names'][:3] # 只取前3个
|
||||
elif 'category' in data:
|
||||
categories = [data['category']][:1]
|
||||
|
||||
type_str = ""
|
||||
if categories:
|
||||
type_str = ", ".join(categories)
|
||||
|
||||
remarks_str = f"共{len(vod_list)}集"
|
||||
|
||||
content_str = data.get('desc', '')
|
||||
|
||||
VOD = {
|
||||
"vod_id": did,
|
||||
"vod_name": data.get('book_name', ''),
|
||||
"vod_pic": data.get('book_pic', ''),
|
||||
"vod_actor": actor_str,
|
||||
"type_name": type_str or fullType,
|
||||
"vod_remarks": remarks_str,
|
||||
"vod_content": content_str,
|
||||
"vod_play_from": "$$$".join(play_from),
|
||||
"vod_play_url": "$$$".join(play_url)
|
||||
}
|
||||
|
||||
return {'list': [VOD]}
|
||||
else:
|
||||
return {'list': []}
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取详情失败: {e}")
|
||||
return {'list': []}
|
||||
|
||||
def playerContent(self, flag, id, vipFlags):
|
||||
p=0
|
||||
uid=f'{self.host}{id}'
|
||||
data=pq(self.fetch(uid, headers=self.headers).text)
|
||||
url=data('.video.ratio').attr('data-play')
|
||||
if not url:
|
||||
url = uid
|
||||
p = 1
|
||||
return {'parse': p, 'url': url, 'header': self.headers}
|
||||
|
||||
def localProxy(self, param):
|
||||
pass
|
||||
|
||||
def getlist(self,data):
|
||||
vlist = []
|
||||
for j in data.items():
|
||||
vlist.append({
|
||||
'vod_id': j('a').attr('href'),
|
||||
'vod_name': j('img').attr('alt'),
|
||||
'vod_pic': self.host + j('img').attr('data-src'),
|
||||
'vod_year': j('.bg').text(),
|
||||
'vod_remarks': j('p').text()
|
||||
})
|
||||
return vlist
|
||||
|
||||
max_retries = 3
|
||||
|
||||
for i in range(max_retries):
|
||||
try:
|
||||
response = requests.get(url=id, headers=headers, timeout=timeout)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
|
||||
if data.get('code') == 200 and data.get('data'):
|
||||
play_url = data['data'].get('url', '')
|
||||
|
||||
if play_url:
|
||||
return {
|
||||
"parse": 0,
|
||||
"playUrl": '',
|
||||
"url": play_url,
|
||||
"header": headers
|
||||
}
|
||||
break
|
||||
except:
|
||||
if i < max_retries - 1:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
return {
|
||||
"parse": 0,
|
||||
"playUrl": '',
|
||||
"url": 'about:blank',
|
||||
"header": headers
|
||||
}
|
||||
|
||||
def searchContent(self, key, quick, pg=1):
|
||||
try:
|
||||
page = int(pg)
|
||||
except:
|
||||
page = 1
|
||||
|
||||
params = f"page={page}&name={urllib.parse.quote(key)}&tab_type=19"
|
||||
search_url = f"{base_url}?{params}"
|
||||
|
||||
try:
|
||||
response = requests.get(search_url, headers=headers, timeout=timeout)
|
||||
|
||||
if response.status_code != 200:
|
||||
return {'list': []}
|
||||
|
||||
response.encoding = "utf-8"
|
||||
data = response.json()
|
||||
|
||||
if data.get('code') != 200 or not data.get('data'):
|
||||
return {'list': []}
|
||||
|
||||
videos = []
|
||||
for vod in data['data']:
|
||||
vod_id = f"book_id={vod.get('book_id', '')}&actor={vod.get('author', '')}&type={vod.get('type', '')}"
|
||||
videos.append({
|
||||
"vod_id": vod_id,
|
||||
"vod_name": vod.get('title', ''),
|
||||
"vod_pic": vod.get('cover', ''),
|
||||
"vod_remarks": vod.get('type', ''),
|
||||
"vod_content": vod.get('intro', '')
|
||||
})
|
||||
|
||||
return {
|
||||
'list': videos,
|
||||
'page': page,
|
||||
'pagecount': 9999,
|
||||
'limit': len(videos),
|
||||
'total': 999999
|
||||
}
|
||||
|
||||
except Exception:
|
||||
return {'list': []}
|
||||
Reference in New Issue
Block a user