From 651f77d14477127612a6aecb94d0243409379246 Mon Sep 17 00:00:00 2001 From: qist <87984115@qq.com> Date: Thu, 21 May 2026 18:24:36 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=95=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py/czzyv.py | 34 ++-- py/czzyv_v2.py | 396 +++++++++++++++++++++++++++++++++++++++++++++ tools/dianshi.json | 2 +- 3 files changed, 419 insertions(+), 13 deletions(-) create mode 100644 py/czzyv_v2.py diff --git a/py/czzyv.py b/py/czzyv.py index 0c6173b2..f785343f 100644 --- a/py/czzyv.py +++ b/py/czzyv.py @@ -37,16 +37,22 @@ class Spider(Spider): self._ua_fallback = "Dalvik/2.1.0 (Linux; U; Android 10)" self._class_map = [ - ("最新电影", "/zuixindianying"), - ("豆瓣Top250", "/dbtop250"), - ("国产剧", "/gcj"), - ("美剧", "/meijutt"), - ("韩剧", "/hanjutv"), - ("日剧", "/riju"), - ("番剧", "/fanju"), - ("剧场版", "/dongmanjuchangban"), - ("海外剧", "/haiwaijuqita"), + ("最新电影", "zuixindianying", "/zuixindianying"), + ("豆瓣Top250", "dbtop250", "/dbtop250"), + ("国产剧", "gcj", "/gcj"), + ("美剧", "meijutt", "/meijutt"), + ("韩剧", "hanjutv", "/hanjutv"), + ("日剧", "riju", "/riju"), + ("番剧", "fanju", "/fanju"), + ("剧场版", "dongmanjuchangban", "/dongmanjuchangban"), + ("海外剧", "haiwaijuqita", "/haiwaijuqita"), ] + self._tid_map = {} + for n, tid, p in self._class_map: + self._tid_map[tid] = p + self._tid_map[n] = p + self._tid_map[p] = p + self._tid_map[p.lstrip("/")] = p def getName(self): return "厂长资源" @@ -236,8 +242,8 @@ class Spider(Spider): def homeContent(self, filter): result = {"class": [], "list": []} - for name, path in self._class_map: - result["class"].append({"type_name": name, "type_id": path}) + for name, tid, _ in self._class_map: + result["class"].append({"type_name": name, "type_id": tid}) if self._use_api: return result @@ -247,7 +253,10 @@ class Spider(Spider): return result def homeVideoContent(self): - return {} + if self._use_api: + return {} + html = self._fetch_text(self.host + "/") + return {"list": self._parse_vod_list(html)[:24]} def categoryContent(self, tid, pg, filter, extend): pg = int(pg or 1) @@ -256,6 +265,7 @@ class Spider(Spider): if self._use_api: return result + tid = self._tid_map.get(tid, tid) if tid.startswith("http"): base = tid else: diff --git a/py/czzyv_v2.py b/py/czzyv_v2.py new file mode 100644 index 00000000..f785343f --- /dev/null +++ b/py/czzyv_v2.py @@ -0,0 +1,396 @@ +""" +czzyv.com - 厂长资源 +""" + +import re +import json +import time +from urllib.parse import urljoin, quote, unquote, urlparse, parse_qs + +import requests + +from base.spider import Spider + + +class Spider(Spider): + def __init__(self): + self.host = "https://czzyv.com" + self.timeout = 20 + self._hosts = [ + "https://czzyv.com", + "https://www.czzy.site", + "https://www.cz4k.com", + "https://cz01.vip", + "https://cz01.tv", + ] + self.headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36", + "Referer": "https://czzyv.com/", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", + "Accept-Language": "zh-CN,zh;q=0.9", + } + self.session = None + self._text_cache = {} + self._text_cache_ttl = 300 + self._api_base = "" + self._use_api = False + self._ua_fallback = "Dalvik/2.1.0 (Linux; U; Android 10)" + + self._class_map = [ + ("最新电影", "zuixindianying", "/zuixindianying"), + ("豆瓣Top250", "dbtop250", "/dbtop250"), + ("国产剧", "gcj", "/gcj"), + ("美剧", "meijutt", "/meijutt"), + ("韩剧", "hanjutv", "/hanjutv"), + ("日剧", "riju", "/riju"), + ("番剧", "fanju", "/fanju"), + ("剧场版", "dongmanjuchangban", "/dongmanjuchangban"), + ("海外剧", "haiwaijuqita", "/haiwaijuqita"), + ] + self._tid_map = {} + for n, tid, p in self._class_map: + self._tid_map[tid] = p + self._tid_map[n] = p + self._tid_map[p] = p + self._tid_map[p.lstrip("/")] = p + + def getName(self): + return "厂长资源" + + def init(self, extend=""): + if isinstance(extend, dict): + host = (extend.get("host") or extend.get("site") or "").strip() + if host: + self.host = host.rstrip("/") + elif isinstance(extend, str) and extend.strip(): + ext_str = extend.strip() + if (ext_str.startswith("{") and ext_str.endswith("}")) or (ext_str.startswith("[") and ext_str.endswith("]")): + try: + ext_obj = json.loads(ext_str) + if isinstance(ext_obj, dict): + host = (ext_obj.get("host") or ext_obj.get("site") or "").strip() + if host: + self.host = host.rstrip("/") + except Exception: + pass + elif ext_str.startswith("http"): + self.host = ext_str.rstrip("/") + + self.headers["Referer"] = self.host + "/" + self.headers["Origin"] = self.host + self.session = requests.Session() + self.session.headers.update(self.headers) + self._choose_host() + self._detect_api() + self._warmup() + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + def _choose_host(self): + if not self.session: + return + + candidates = [] + if self.host: + candidates.append(self.host.rstrip("/")) + for h in self._hosts: + h = (h or "").rstrip("/") + if h and h not in candidates: + candidates.append(h) + + for h in candidates: + try: + r = self.session.get(h + "/", timeout=self.timeout, allow_redirects=True, verify=False) + if not r or r.status_code != 200: + continue + r.encoding = "utf-8" + text = r.text or "" + if "访问已被拦截" in text or "已被拦截" in text: + continue + self.host = h + self.headers["Referer"] = self.host + "/" + self.headers["Origin"] = self.host + self.session.headers.update(self.headers) + self._text_cache.clear() + return + except Exception: + continue + + def _detect_api(self): + candidates = [ + "/api.php/provide/vod?ac=list", + "/api.php/provide/vod/?ac=list", + "/index.php/api/vod?ac=list", + ] + for p in candidates: + url = urljoin(self.host + "/", p.lstrip("/")) + try: + r = self.session.get(url, timeout=self.timeout, allow_redirects=True, verify=False) + ct = (r.headers.get("Content-Type") or "").lower() + if r.status_code == 200 and (("json" in ct) or ("xml" in ct) or r.text.strip().startswith(("{", "<"))): + self._api_base = url.split("?", 1)[0] + self._use_api = True + return + except Exception: + continue + self._api_base = "" + self._use_api = False + + def _warmup(self): + try: + self.session.get(self.host + "/", timeout=self.timeout, allow_redirects=True, verify=False) + except Exception: + pass + + def _fetch_text(self, url): + now = time.time() + cached = self._text_cache.get(url) + if cached and cached[0] > now: + return cached[1] + + try: + r = None + for _ in range(3): + try: + r = self.session.get(url, timeout=self.timeout, allow_redirects=True, verify=False) + break + except Exception: + time.sleep(1) + if not r or r.status_code != 200: + if r and r.status_code in (403, 406, 412): + self.session.headers["User-Agent"] = self._ua_fallback + try: + r = self.session.get(url, timeout=self.timeout, allow_redirects=True, verify=False) + except Exception: + r = None + if not r or r.status_code != 200: + return "" + r.encoding = "utf-8" + text = r.text or "" + if "访问已被拦截" in text or "已被拦截" in text: + self.session.headers["User-Agent"] = self._ua_fallback + try: + r2 = self.session.get(url, timeout=self.timeout, allow_redirects=True, verify=False) + if r2 and r2.status_code == 200: + r2.encoding = "utf-8" + text = r2.text or "" + except Exception: + pass + if text: + self._text_cache[url] = (now + self._text_cache_ttl, text) + return text + except Exception: + return "" + + def _abs(self, href): + return urljoin(self.host + "/", href or "") + + def _parse_pagecount(self, html, current_pg): + m = re.findall(r"/page/(\d+)", html or "") + nums = [int(x) for x in m if x.isdigit()] + if nums: + return max(max(nums), current_pg) + return 999 + + def _parse_vod_list(self, html): + html = html or "" + vods = [] + + blocks = re.findall(r"(?is)