Files
2026-08-01 11:47:20 +00:00

421 lines
15 KiB
Python

# -*- coding: utf-8 -*-
import sys
import re
import base64
import requests
from urllib.parse import quote, unquote
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend=""):
self.host = "https://gzbk.didi51-tedb0997.cc"
self.hosts = [self.host, "https://www.gg51.com", "https://gg51.com"]
self.img_host = "https://oytsuig.kwvqaj.cn"
self.valid_hosts = []
self.headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 12; SM-G9750 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.72 MQQBrowser/6.2 TBS/046279 Mobile Safari/537.36",
"Referer": self.host + "/",
"Origin": self.host,
"Accept": "application/json,text/plain,*/*"
}
self.session = requests.Session()
self.session.headers.update(self.headers)
def getName(self):
return "GG51"
def isVideoFormat(self, url):
return bool(re.search(r"\.(m3u8|mp4|flv|avi|mkv|mov|ts)(\?|$)", url or "", re.I))
def manualVideoCheck(self):
return False
def homeContent(self, filter):
classes = [
{"type_id": "7", "type_name": "大厂原创"},
{"type_id": "8", "type_name": "重磅泄密"},
{"type_id": "5", "type_name": "自拍偷拍"},
{"type_id": "6", "type_name": "绿帽偷情"},
{"type_id": "11", "type_name": "中文字幕"},
{"type_id": "14", "type_name": "强奸迷奸"},
{"type_id": "12", "type_name": "高清无码"},
{"type_id": "13", "type_name": "熟女人妻"},
{"type_id": "15", "type_name": "剧情大片"},
{"type_id": "16", "type_name": "黑白配"},
{"type_id": "18", "type_name": "美颜巨乳"},
{"type_id": "48", "type_name": "欧美少妇"},
{"type_id": "19", "type_name": "动漫3D"},
{"type_id": "21", "type_name": "网红主播"},
{"type_id": "22", "type_name": "AI换脸"}
]
return {"class": classes, "filters": {}}
def homeVideoContent(self):
return {"list": []}
def categoryContent(self, tid, pg, filter, extend):
data = self.apiList(str(tid), str(pg))
if str(tid) == "21" and not data:
data = self.mergeCategory(["23", "24", "25", "27"], pg)
return {
"page": int(pg),
"pagecount": 999 if data else int(pg),
"limit": 10,
"total": 999999 if data else 0,
"list": data
}
def detailContent(self, ids):
sid = ids[0] if ids else ""
ps = sid.split("@@@")
vid = ps[0] if len(ps) > 0 else sid
play = ps[1] if len(ps) > 1 else ""
name = unquote(ps[2]) if len(ps) > 2 else vid
pic = unquote(ps[3]) if len(ps) > 3 else ""
if play:
return {"list": [{
"vod_id": sid,
"vod_name": name,
"vod_pic": pic,
"type_name": "",
"vod_year": "",
"vod_area": "",
"vod_remarks": "",
"vod_actor": "",
"vod_director": "",
"vod_content": name,
"vod_play_from": "GG51",
"vod_play_url": name + "$" + play
}]}
html = self.get(self.host + "/view/" + vid)
title = self.match(html, r"<title>(.*?)</title>")
title = self.clean(title).split("-")[0].strip() if title else vid
pic = self.match(html, r'(?:poster|data-original|src)=["\']([^"\']+\.(?:jpg|jpeg|png|webp)[^"\']*)')
pic = self.fix(pic)
play = self.extractPlay(html)
return {"list": [{
"vod_id": sid,
"vod_name": title,
"vod_pic": pic,
"type_name": "",
"vod_year": "",
"vod_area": "",
"vod_remarks": "",
"vod_actor": "",
"vod_director": "",
"vod_content": title,
"vod_play_from": "GG51",
"vod_play_url": title + "$" + (play or sid)
}]}
def searchContent(self, key, quick, pg="1"):
wd = quote(key)
urls = [
self.host + "/search/" + wd,
self.host + "/search/" + wd + "/" + str(pg),
self.host + "/search/" + wd + "/page/" + str(pg),
self.host + "/search?keyword=" + wd + "&page=" + str(pg),
self.host + "/search/?keyword=" + wd + "&page=" + str(pg)
]
for url in urls:
html = self.get(url)
data = self.parseList(html)
if data:
return {"list": data}
return {"list": []}
def playerContent(self, flag, id, vipFlags):
sid = id or ""
ps = sid.split("@@@")
url = ps[1] if len(ps) > 1 else sid
if self.isVideoFormat(url):
return {"parse": 0, "url": url, "header": self.headers}
html = self.get(self.host + "/view/" + ps[0])
play = self.extractPlay(html)
if play:
return {"parse": 0, "url": play, "header": self.headers}
return {"parse": 1, "url": url, "header": self.headers}
def localProxy(self, param):
return [200, "video/MP2T", {}, ""]
def destroy(self):
return "success"
def apiList(self, tid, pg):
try:
real_pg = str(int(pg) + 1)
except Exception:
real_pg = "2"
hosts = self.fastHosts()
for h in hosts:
data = self.postList(h, tid, real_pg)
if data:
return data
for h in self.findHosts():
if h in hosts:
continue
data = self.postList(h, tid, real_pg)
if data:
return data
return []
def fastHosts(self):
res = []
for h in [self.host] + self.valid_hosts:
h = str(h).rstrip("/")
if h and h not in res:
res.append(h)
return res
def postList(self, h, tid, real_pg):
try:
h = h.rstrip("/")
url = h + "/data/getlistbyid"
hs = dict(self.headers)
hs["Referer"] = h + "/category/" + str(tid)
hs["Origin"] = h
hs["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
r = self.session.post(url, headers=hs, data="id=" + str(tid) + "&page=" + str(real_pg), timeout=5, verify=False)
js = r.json()
arr = js.get("listData") or js.get("data") or js.get("list") or []
if not arr:
return []
self.host = h
self.headers["Referer"] = self.host + "/"
self.headers["Origin"] = self.host
if self.host in self.valid_hosts:
self.valid_hosts.remove(self.host)
self.valid_hosts.insert(0, self.host)
return self.parseApiList(arr)
except Exception:
return []
def mergeCategory(self, tids, pg):
res = []
for tid in tids:
arr = self.apiList(str(tid), str(pg))
for item in arr:
res.append(item)
if len(res) >= 10:
return self.uniqueList(res)
return self.uniqueList(res)
def uniqueList(self, arr):
res = []
seen = set()
for item in arr or []:
vid = item.get("vod_id", "")
key = vid.split("@@@")[0] if "@@@" in vid else vid
if not key or key in seen:
continue
seen.add(key)
res.append(item)
return res
def parseApiList(self, arr):
res = []
for item in arr or []:
vid = str(item.get("view_key") or item.get("id") or item.get("vod_id") or "")
name = self.clean(str(item.get("title") or item.get("name") or item.get("vod_name") or ""))
pic = self.fix(str(item.get("poster") or item.get("pic") or item.get("vod_pic") or ""))
play = str(item.get("play_url") or item.get("url") or item.get("vod_play_url") or "")
remark = str(item.get("duration") or item.get("vod_remarks") or item.get("display_heat") or item.get("hits") or "")
if not vid and play:
vid = play
if not vid or not name:
continue
sid = vid + "@@@" + play + "@@@" + quote(name) + "@@@" + quote(pic)
res.append({
"vod_id": sid,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remark
})
return res
def findHosts(self):
if self.valid_hosts:
return self.valid_hosts
res = []
base = [self.host] + [h for h in self.hosts if h != self.host]
for h in base:
try:
h = h.rstrip("/")
url = h + "/data/domains"
r = self.session.get(url, headers=self.headers, timeout=4, verify=False)
js = r.json()
ds = js.get("landingdomains") or js.get("domains") or []
for x in ds:
x = str(x).rstrip("/")
if x and x not in res:
res.append(x)
if res:
break
except Exception:
pass
for h in base:
h = h.rstrip("/")
if h not in res:
res.append(h)
self.valid_hosts = res
return res
def get(self, url):
try:
r = self.session.get(url, headers=self.headers, timeout=8, verify=False)
html = r.text
if "/_guard/auto.js" in html or len(html.strip()) < 80:
ck = r.cookies.get("guard") or self.session.cookies.get("guard")
if ck:
self.session.cookies.set("guardret", self.guardCookie(ck))
r = self.session.get(url, headers=self.headers, timeout=8, verify=False)
html = r.text
return self.decodeShell(html)
except Exception:
return ""
def guardCookie(self, guard):
try:
key = guard[:8]
tail = re.sub(r"\D", "", guard[12:])
val = str(int(tail) * 2 + 16)
return base64.b64encode(self.rc4(val, key)).decode()
except Exception:
return ""
def rc4(self, data, key):
s = list(range(256))
j = 0
out = []
key = key.encode()
data = data.encode()
for i in range(256):
j = (j + s[i] + key[i % len(key)]) % 256
s[i], s[j] = s[j], s[i]
i = j = 0
for c in data:
i = (i + 1) % 256
j = (j + s[i]) % 256
s[i], s[j] = s[j], s[i]
out.append(c ^ s[(s[i] + s[j]) % 256])
return bytes(out)
def decodeShell(self, html):
old = ""
cur = html or ""
for i in range(3):
if cur == old:
break
old = cur
cur = self.decodeShellOnce(cur)
return cur
def decodeShellOnce(self, html):
if not html:
return ""
for p in [
r"atob\([\"']([A-Za-z0-9+/=]+)[\"']\)",
r"window\.atob\([\"']([A-Za-z0-9+/=]+)[\"']\)",
r"Base64\.decode\([\"']([A-Za-z0-9+/=]+)[\"']\)"
]:
m = re.search(p, html)
if m:
s = self.b64(m.group(1))
if len(s) > 100:
return s
m = re.search(r"Uint8Array\(\s*\[([0-9,\s]+)\]", html)
if m:
try:
s = "".join([chr(int(x.strip())) for x in m.group(1).split(",") if x.strip()])
if len(s) > 100:
return s
except Exception:
pass
m = re.search(r"['\"]((?:%[0-9A-Fa-f]{2}){20,})['\"]", html)
if m:
s = self.decodeUri(m.group(1))
if len(s) > 100:
return s
m = re.search(r"['\"]([0-9a-fA-F]{80,})['\"]", html)
if m:
s = self.decodeHex(m.group(1))
if len(s) > 100:
return s
return html
def b64(self, s):
try:
return base64.b64decode(s + "=" * (-len(s) % 4)).decode("utf-8", "ignore")
except Exception:
return ""
def decodeHex(self, s):
try:
return bytes.fromhex(s).decode("utf-8", "ignore")
except Exception:
return ""
def decodeUri(self, s):
try:
return unquote(s)
except Exception:
return ""
def extractPlay(self, html):
if not html:
return ""
ps = [
r'initPlayer\([\"\']([^\"\']+)[\"\']',
r'["\']url["\']\s*:\s*["\']([^"\']+)["\']',
r'["\']play_url["\']\s*:\s*["\']([^"\']+)["\']',
r'(https?://[^"\']+\.(?:m3u8|mp4)(?:\?[^"\']*)?)'
]
for p in ps:
m = re.search(p, html, re.I)
if m:
u = m.group(1).replace("\\/", "/")
if self.isVideoFormat(u):
return u
return ""
def parseList(self, html):
res = []
if not html:
return res
cards = re.findall(r'<a[^>]+href=["\']([^"\']*/view/([^"\']+))["\'][\s\S]{0,800}?</a>', html, re.I)
for href, vid in cards:
block = self.match(html, r'<a[^>]+href=["\'][^"\']*/view/' + re.escape(vid) + r'["\'][\s\S]{0,800}?</a>')
name = self.match(block, r'alt=["\']([^"\']+)') or self.match(block, r'title=["\']([^"\']+)') or self.clean(re.sub(r"<[^>]+>", " ", block))
pic = self.match(block, r'(?:data-original|data-src|src)=["\']([^"\']+\.(?:jpg|jpeg|png|webp)[^"\']*)')
remark = self.match(block, r'<span[^>]*>(.*?)</span>')
if vid and name:
res.append({
"vod_id": vid,
"vod_name": self.clean(name),
"vod_pic": self.fix(pic),
"vod_remarks": self.clean(remark)
})
return self.uniqueList(res)
def match(self, text, pat):
m = re.search(pat, text or "", re.I)
return m.group(1) if m else ""
def clean(self, text):
text = re.sub(r"<[^>]+>", " ", text or "")
text = re.sub(r"\s+", " ", text)
return text.strip()
def fix(self, url):
url = (url or "").strip().replace("\\/", "/")
if not url:
return ""
if url.startswith("//"):
return "https:" + url
if url.startswith("http"):
return url
if url.startswith("/"):
return self.img_host.rstrip("/") + url
return self.img_host.rstrip("/") + "/" + url