From 63eebfaf2cd8052027078db389bb15144e9c8235 Mon Sep 17 00:00:00 2001 From: XC-Pro <19840+xc-pro@noreply.example.org> Date: Fri, 14 Aug 2026 13:24:31 +0200 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=20py/=E5=8D=B3=E7=9C=8B.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py/即看.py | 245 ----------------------------------------------------- 1 file changed, 245 deletions(-) delete mode 100644 py/即看.py diff --git a/py/即看.py b/py/即看.py deleted file mode 100644 index 1ae14b8..0000000 --- a/py/即看.py +++ /dev/null @@ -1,245 +0,0 @@ -# -*- coding: utf-8 -*- -import re, json, base64 -import urllib.parse -from base.spider import Spider -from bs4 import BeautifulSoup - - -class Spider(Spider): - headers = { - "User-Agent": "Mozilla/5.0 (Linux; Android 15; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Mobile Safari/537.36", - "Referer": "https://www.jkan.app/" - } - - def init(self, extend=""): - self.host = "https://www.jkan.app" - self._timeout = 15 - - def _soup(self, html): - return BeautifulSoup(html, "html.parser") if html else None - - def _fetch(self, url): - return self.fetch(url, headers=self.headers, timeout=self._timeout, verify=False) - - def _fix(self, u): - if not u: return "" - if u.startswith("//"): return "https:" + u - if u.startswith("/"): return self.host + u - return u - - def _parse_list(self, html): - videos, seen = [], set() - soup = self._soup(html) - if soup is None: - return videos - for a in soup.select('a[href*="/video/"]'): - href = a.get("href", "") - if not href or href in seen: - continue - if a.get("class") and "text_muted" in a.get("class"): - continue - seen.add(href) - title = a.get("title", "") or a.get_text(strip=True)[:100] - pic = a.get("data-original", "") or a.get("data-background-image", "") - if not pic: - bg = re.search(r"background(?:-image)?\s*:\s*url\([\"']?([^\"')]+)[\"']?\)", a.get("style", "") or "") - if bg: - pic = bg.group(1) - remark = "" - rt = a.select_one("span.xszxj, span.pic_text") - if rt: - remark = rt.get_text(strip=True) - videos.append({"vod_id": href, "vod_name": title, "vod_pic": self._fix(pic), "vod_remarks": remark}) - if len(videos) >= 50: - break - return videos - - def _get_filters(self, tid): - return [ - {"key": "class", "name": "类型", "value": [ - {"n": "全部", "v": ""}, {"n": "喜剧", "v": "喜剧"}, {"n": "爱情", "v": "爱情"}, - {"n": "恐怖", "v": "恐怖"}, {"n": "动作", "v": "动作"}, {"n": "科幻", "v": "科幻"}, - {"n": "剧情", "v": "剧情"}, {"n": "战争", "v": "战争"}, {"n": "悬疑", "v": "悬疑"}, - {"n": "惊悚", "v": "惊悚"}, {"n": "冒险", "v": "冒险"}, {"n": "犯罪", "v": "犯罪"}, - {"n": "动画", "v": "动画"}, {"n": "奇幻", "v": "奇幻"}, {"n": "武侠", "v": "武侠"}, - {"n": "古装", "v": "古装"}, {"n": "历史", "v": "历史"}, {"n": "记录", "v": "记录"}, - ]}, - {"key": "area", "name": "地区", "value": [ - {"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "美国", "v": "美国"}, - {"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "香港", "v": "香港"}, - {"n": "台湾", "v": "台湾"}, {"n": "泰国", "v": "泰国"}, {"n": "英国", "v": "英国"}, - {"n": "法国", "v": "法国"}, {"n": "德国", "v": "德国"}, {"n": "印度", "v": "印度"}, - ]}, - {"key": "year", "name": "年份", "value": - [{"n": "全部", "v": ""}] + [{"n": str(y), "v": str(y)} for y in range(2026, 1999, -1)]}, - {"key": "sort", "name": "排序", "value": [ - {"n": "按时间", "v": "time"}, {"n": "按人气", "v": "hits"}, {"n": "按评分", "v": "score"}, - ]}, - ] - - def homeContent(self, filter): - return { - "class": [ - {"type_id": "1", "type_name": "电影"}, - {"type_id": "2", "type_name": "电视剧"}, - {"type_id": "3", "type_name": "动漫"}, - {"type_id": "4", "type_name": "综艺"}, - ], - "filters": {tid: self._get_filters(tid) for tid in ["1", "2", "3", "4"]} - } - - def homeVideoContent(self): - try: - rsp = self._fetch(self.host) - return {"list": self._parse_list(rsp.text)} - except: - return {"list": []} - - def _build_vodshow_url(self, tid, pg, extend): - segs = [""] * 12 - segs[0] = tid - if extend.get("area"): segs[1] = extend["area"] - if extend.get("sort"): segs[2] = extend["sort"] - if extend.get("class"): segs[3] = extend["class"] - if extend.get("year"): segs[11] = extend["year"] - url = f"{self.host}/show/{'-'.join(segs)}.html" - if pg != "1": - url = f"{self.host}/show/{'-'.join(segs)}-{pg}.html" - return url - - def categoryContent(self, tid, pg, filter, extend): - try: - if filter and any(v for v in extend.values()): - url = self._build_vodshow_url(tid, pg, extend) - else: - url = f"{self.host}/show/{tid}-----------{pg}.html" - rsp = self._fetch(url) - return {"list": self._parse_list(rsp.text), "page": str(pg), "pagecount": 999} - except: - return {"list": [], "page": str(pg), "pagecount": 0} - - def _get_detail(self, vid): - if vid.startswith("/"): - url = self._fix(vid) - elif vid.startswith("http"): - url = vid - else: - url = f"{self.host}/video/{vid}.html" - try: - rsp = self._fetch(url) - except: - return {"vod_id": vid} - html = rsp.text - if re.search(r"(模板文件不存在|系统安全验证)", html, re.I): - return {"vod_id": "0", "vod_name": ""} - vod = {"vod_id": vid} - soup = self._soup(html) - if soup is None: - return vod - vod["vod_name"] = self._og(soup, "og:title") - vod["vod_pic"] = self._og(soup, "og:image") - vod["vod_content"] = self._og(soup, "og:description") - vod["vod_year"] = self._og(soup, "og:video:release_date") - vod["vod_area"] = self._og(soup, "og:video:area") - vod["vod_director"] = self._og(soup, "og:video:director") - vod["vod_actor"] = self._og(soup, "og:video:actor") - vod["type_name"] = self._og(soup, "og:video:class") - score = self._og(soup, "og:video:score") - if score: vod["vod_score"] = score - # extract play sources and episodes - src_tabs = soup.select_one(".play_source_tab") - src_names = [a.get("alt", "").strip() or a.get_text(strip=True) for a in src_tabs.select("a")] if src_tabs else [] - for i, ul in enumerate(soup.select("ul.content_playlist, ul.playlist")): - source = src_names[i] if i < len(src_names) else "默认" - episodes = [] - seen_urls = set() - for a in ul.select("a[href]"): - ep_url = a.get("href", "") - if ep_url in seen_urls: - continue - seen_urls.add(ep_url) - ep_name = a.get_text(strip=True) or "播放" - episodes.append(f"{ep_name}${self._fix(ep_url)}") - if episodes: - vod.setdefault("vod_play_from", []).append(source) - vod.setdefault("vod_play_url", []).append("#".join(episodes)) - if "vod_play_from" in vod: - vod["vod_play_from"] = "$$$".join(vod.pop("vod_play_from")) - vod["vod_play_url"] = "$$$".join(vod.pop("vod_play_url")) - else: - vod["vod_play_from"] = "默认线路" - vod["vod_play_url"] = "" - return vod - - def _og(self, soup, prop): - m = soup.select_one(f'meta[property="{prop}"]') - if m: return m.get("content", "") - m = soup.select_one(f'meta[name="{prop}"]') - if m: return m.get("content", "") - return "" - - def detailContent(self, ids): - return {"list": [self._get_detail(ids[0])]} - - def playerContent(self, flag, id, vipFlags): - play_url = self._fix(id) - if self.isVideoFormat(play_url): - return {"parse": 0, "url": play_url, "header": self.headers} - try: - rsp = self._fetch(play_url) - return self._parse_player(rsp.text, play_url) - except: - return {"parse": 0, "url": play_url, "header": self.headers} - - def _parse_player(self, html, play_url): - match = re.search(r'player_aaaa\s*=\s*(\{[\s\S]*?\});?\s*', html, re.I) - if not match: - return {"parse": 1, "url": play_url, "header": self.headers} - try: - data = json.loads(match.group(1)) - url_enc = data.get("url", "") - enc_type = str(data.get("encrypt", "0")) - if enc_type == "1": - url_enc = urllib.parse.unquote(url_enc) - elif enc_type == "2": - url_enc = urllib.parse.unquote(base64.b64decode(url_enc).decode()) - real_url = self._unescape(url_enc) - return {"parse": 0, "url": real_url or play_url, "header": self.headers} - except: - return {"parse": 0, "url": play_url, "header": self.headers} - - def _unescape(self, s): - result, i, n = [], 0, len(s) - while i < n: - if s[i] == '%' and i + 5 < n and s[i + 1] == 'u': - result.append(chr(int(s[i + 2:i + 6], 16))) - i += 6 - elif s[i] == '%' and i + 2 < n: - result.append(chr(int(s[i + 1:i + 3], 16))) - i += 3 - else: - result.append(s[i]) - i += 1 - return ''.join(result) - - def searchContent(self, key, quick, pg="1"): - # 搜索翻页格式: ---------{N}--- (page1: 13 dashes, pageN: 10 dashes + N + 3 dashes) - try: - kw = urllib.parse.quote(key) - if pg == "1": - url = f"{self.host}/search/{kw}-------------.html" - else: - url = f"{self.host}/search/{kw}----------{pg}---.html" - rsp = self._fetch(url) - videos = self._parse_list(rsp.text) - return {"list": videos, "page": str(pg), "pagecount": 99} - except: - return {"list": [], "pagecount": 0} - - def isVideoFormat(self, url): - return bool(re.search(r'\.(mp4|m3u8|flv|avi|mkv|ts|webm|mpg|mpeg)\b', url, re.I)) - - def destroy(self): - pass - -