From 6de6d19da1f899e5f2921d18350ebe19d020c748 Mon Sep 17 00:00:00 2001 From: Harold <8866033@gmail.com> Date: Mon, 20 Apr 2026 13:08:02 +0800 Subject: [PATCH] feat: add searchContent with OCR verification and filtering Co-Authored-By: Claude Opus 4.7 --- py/tests/test_鲸鱼APP.py | 18 +++++++++++++ py/鲸鱼APP.py | 58 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/py/tests/test_鲸鱼APP.py b/py/tests/test_鲸鱼APP.py index 64a200e..3916b74 100644 --- a/py/tests/test_鲸鱼APP.py +++ b/py/tests/test_鲸鱼APP.py @@ -193,3 +193,21 @@ class TestJingyuSpider(unittest.TestCase): vod = result["list"][0] self.assertIn("1线", vod["vod_play_from"]) self.assertIn("正式线路", vod["vod_play_from"]) + + def test_search_content_filters_and_maps_results(self): + search_data = { + "search_list": [ + {"vod_id": "1", "vod_name": "繁花", "vod_pic": "http://p1.jpg", + "vod_remarks": "更新中", "vod_class": "都市", "vod_year": "2024"}, + {"vod_id": "2", "vod_name": "花繁", "vod_pic": "http://p2.jpg", + "vod_remarks": "", "vod_class": "屏蔽预留", "vod_year": ""}, + ] + } + + self.spider._api_post = lambda ep, payload=None: search_data + self.spider.host = "http://test.com" + self.spider.init_data = {} + result = self.spider.searchContent("繁花", False, "1") + self.assertEqual(len(result["list"]), 1) + self.assertEqual(result["list"][0]["vod_name"], "繁花") + self.assertNotIn("pagecount", result) diff --git a/py/鲸鱼APP.py b/py/鲸鱼APP.py index b3e39bc..d22ea2f 100644 --- a/py/鲸鱼APP.py +++ b/py/鲸鱼APP.py @@ -294,3 +294,61 @@ class Spider(BaseSpider): "vod_play_url": "$$$".join(l["urls"] for l in lines), }] } + + def _get_verify_code(self): + import uuid + try: + uid = str(uuid.uuid4()) + verify_url = f"{self.host}{self.api_path}/verify/create?key={uid}" + rsp = self.fetch(verify_url, headers={"User-Agent": self.ua}, + timeout=10, verify=False) + b64_img = base64.b64encode(rsp.content).decode("utf-8") + ocr_url = "http://154.222.22.188:9898/ocr/b64/text" + ocr_rsp = self.post(ocr_url, data=b64_img, + headers={"User-Agent": self.ua, "Content-Type": "text/plain"}, + timeout=10, verify=False) + code = (ocr_rsp.text or "").strip() + if not code: + return None + replacements = { + "y": "9", "口": "0", "q": "0", "u": "0", "o": "0", + ">": "1", "d": "0", "b": "8", "已": "2", "D": "0", "五": "5", + } + code = "".join(replacements.get(c, c) for c in code) + if not re.match(r"^\d{4}$", code): + return None + return {"uuid": uid, "code": code} + except Exception as e: + self.log(f"验证码获取失败: {e}") + return None + + def searchContent(self, key, quick, pg="1"): + self.init() + payload = {"keywords": key, "type_id": "0", "page": str(pg)} + if self.search_verify: + verify = self._get_verify_code() + if verify: + payload["code"] = verify["code"] + payload["key"] = verify["uuid"] + res = self._api_post(self.search_endpoint, payload) + if not res: + return {"list": [], "page": int(pg), "limit": 90, "total": 999999} + raw = res.get("search_list", []) + filtered = [i for i in raw if "屏蔽预留" not in (i.get("vod_class") or "").lower()] + kw = (key or "").strip().lower() + if kw: + filtered = [ + i for i in filtered + if kw in " ".join([ + i.get("vod_name", ""), + i.get("vod_remarks", ""), + i.get("vod_class", ""), + ]).lower() + ] + items = [{ + "vod_id": str(i.get("vod_id", "")), + "vod_name": i.get("vod_name", ""), + "vod_pic": i.get("vod_pic", ""), + "vod_remarks": f"{i.get('vod_year', '')} {i.get('vod_class', '')}".strip(), + } for i in filtered] + return {"list": items, "page": int(pg), "limit": 90, "total": 999999}