From c87f96d417203943a4738e03dcb4ee6a249c9065 Mon Sep 17 00:00:00 2001
From: Harold <8866033@gmail.com>
Date: Thu, 23 Apr 2026 21:08:58 +0800
Subject: [PATCH] feat: add tengxun category and batch helper
---
py/tests/test_腾讯视频.py | 49 ++++++++++++++++++++++++++
py/腾讯视频.py | 72 +++++++++++++++++++++++++++++++++++++++
2 files changed, 121 insertions(+)
diff --git a/py/tests/test_腾讯视频.py b/py/tests/test_腾讯视频.py
index bbc7a2e..9d9b85c 100644
--- a/py/tests/test_腾讯视频.py
+++ b/py/tests/test_腾讯视频.py
@@ -21,6 +21,13 @@ HOME_HTML = """
"""
+CATEGORY_HTML = """
+
+"""
+
class TestTencentSpider(unittest.TestCase):
def setUp(self):
@@ -71,6 +78,48 @@ class TestTencentSpider(unittest.TestCase):
},
)
+ @patch.object(Spider, "fetch")
+ def test_category_content_maps_filters_and_prefixes_channel(self, mock_fetch):
+ mock_fetch.return_value = SimpleNamespace(text=CATEGORY_HTML)
+ result = self.spider.categoryContent(
+ "tv",
+ "2",
+ False,
+ {
+ "sort": "18",
+ "iyear": "2024",
+ "year": "2024",
+ "type": "17",
+ "feature": "4",
+ "area": "2",
+ "itrailer": "1",
+ "sex": "1",
+ },
+ )
+ request_url = mock_fetch.call_args.args[0]
+ self.assertIn("channel=tv", request_url)
+ self.assertIn("offset=21", request_url)
+ self.assertIn("sort=18", request_url)
+ self.assertIn("iyear=2024", request_url)
+ self.assertIn("year=2024", request_url)
+ self.assertIn("itype=17", request_url)
+ self.assertIn("ifeature=4", request_url)
+ self.assertIn("iarea=2", request_url)
+ self.assertIn("itrailer=1", request_url)
+ self.assertIn("gender=1", request_url)
+ self.assertEqual(result["list"][0]["vod_id"], "tv$/x/cover/cid001/vid001.html")
+ self.assertEqual(result["page"], 2)
+ self.assertEqual(result["limit"], 21)
+ self.assertEqual(result["pagecount"], 9999)
+
+ @patch.object(Spider, "fetch")
+ def test_get_batch_video_info_parses_qzoutputjson_payload(self, mock_fetch):
+ mock_fetch.return_value = SimpleNamespace(
+ text='QZOutputJson={"results":[{"fields":{"vid":"vid001","title":"第1集","category_map":["0","正片"]}}]};'
+ )
+ result = self.spider._get_batch_video_info(["vid001"])
+ self.assertEqual(result, [{"vid": "vid001", "title": "第1集", "type": "正片"}])
+
if __name__ == "__main__":
unittest.main()
diff --git a/py/腾讯视频.py b/py/腾讯视频.py
index b6b56d2..b0a981d 100644
--- a/py/腾讯视频.py
+++ b/py/腾讯视频.py
@@ -1,4 +1,5 @@
# coding=utf-8
+import json
import re
import sys
@@ -74,3 +75,74 @@ class Spider(BaseSpider):
def playerContent(self, flag, id, vipFlags):
return {"parse": 1, "jx": 1, "url": id, "header": self._headers()}
+
+ def _safe_json(self, text, strip_prefix="", strip_suffix=""):
+ raw = str(text or "")
+ if strip_prefix and raw.startswith(strip_prefix):
+ raw = raw[len(strip_prefix):]
+ if strip_suffix and raw.endswith(strip_suffix):
+ raw = raw[: -len(strip_suffix)]
+ return json.loads(raw) if raw else {}
+
+ def _get_batch_video_info(self, vids):
+ results = []
+ for start in range(0, len(vids or []), 30):
+ batch = vids[start:start + 30]
+ if not batch:
+ continue
+ url = (
+ "https://union.video.qq.com/fcgi-bin/data?otype=json&tid=1804&appid=20001238"
+ "&appkey=6c03bbe9658448a4&union_platform=1&idlist=" + ",".join(batch)
+ )
+ response = self.fetch(url, headers=self._headers())
+ payload = self._safe_json(getattr(response, "text", ""), strip_prefix="QZOutputJson=", strip_suffix=";")
+ for item in payload.get("results", []):
+ fields = item.get("fields", {})
+ category_map = fields.get("category_map", [])
+ results.append(
+ {
+ "vid": fields.get("vid", ""),
+ "title": fields.get("title", ""),
+ "type": category_map[1] if len(category_map) > 1 else "",
+ }
+ )
+ return results
+
+ def categoryContent(self, tid, pg, filter, extend):
+ page = int(pg)
+ offset = (page - 1) * 21
+ params = [
+ "_all=1",
+ "append=1",
+ f"channel={tid}",
+ "listpage=1",
+ f"offset={offset}",
+ "pagesize=21",
+ "iarea=-1",
+ ]
+ options = dict(extend or {})
+ if options.get("sort"):
+ params.append(f"sort={options['sort']}")
+ if options.get("iyear"):
+ params.append(f"iyear={options['iyear']}")
+ if options.get("year"):
+ params.append(f"year={options['year']}")
+ if options.get("type"):
+ params.append(f"itype={options['type']}")
+ if options.get("feature"):
+ params.append(f"ifeature={options['feature']}")
+ if options.get("area"):
+ params.append(f"iarea={options['area']}")
+ if options.get("itrailer"):
+ params.append(f"itrailer={options['itrailer']}")
+ if options.get("sex"):
+ params.append(f"gender={options['sex']}")
+ url = f"{self.base_host}/x/bu/pagesheet/list?" + "&".join(params)
+ response = self.fetch(url, headers=self._headers())
+ return {
+ "list": self._parse_list_items(getattr(response, "text", ""), with_channel=True, channel_id=str(tid)),
+ "page": page,
+ "pagecount": 9999,
+ "limit": 21,
+ "total": 999999,
+ }