feat: add tengxun category and batch helper
This commit is contained in:
@@ -21,6 +21,13 @@ HOME_HTML = """
|
|||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
CATEGORY_HTML = """
|
||||||
|
<div class="list_item">
|
||||||
|
<a data-float="/x/cover/cid001/vid001.html"><img alt="三体" src="https://img.test/c.jpg"></a>
|
||||||
|
<a>更新至30集</a>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TestTencentSpider(unittest.TestCase):
|
class TestTencentSpider(unittest.TestCase):
|
||||||
def setUp(self):
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
+72
@@ -1,4 +1,5 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -74,3 +75,74 @@ class Spider(BaseSpider):
|
|||||||
|
|
||||||
def playerContent(self, flag, id, vipFlags):
|
def playerContent(self, flag, id, vipFlags):
|
||||||
return {"parse": 1, "jx": 1, "url": id, "header": self._headers()}
|
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,
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user