lib.itmens/catalog/sites/douban_drama.py

268 lines
9.7 KiB
Python
Raw Permalink Normal View History

2023-07-19 11:12:58 -04:00
import re
from django.core.cache import cache
from lxml import html
from catalog.common import *
2022-12-08 16:59:03 +00:00
from catalog.models import *
2024-07-13 00:16:47 -04:00
from common.models.lang import detect_language
2023-07-19 11:12:58 -04:00
from .douban import DoubanDownloader
2023-06-05 02:04:52 -04:00
def _cache_key(url):
return f"$:{url}"
@SiteManager.register
class DoubanDramaVersion(AbstractSite):
"""
Parse Douban Drama Version section in Douban Drama page
It's the same page as the drama page, each version resides in a <div id="1234" />
since they all get parsed about the same time, page content will be cached to avoid duplicate fetch
"""
SITE_NAME = SiteName.Douban
ID_TYPE = IdType.DoubanDramaVersion
2024-10-13 17:35:36 -04:00
URL_PATTERNS = [
r"\w+://www.douban.com/location/drama/(\d+)/#(\d+)$",
]
2023-06-05 03:39:37 -04:00
2023-06-05 02:04:52 -04:00
WIKI_PROPERTY_ID = "?"
DEFAULT_MODEL = PerformanceProduction
2023-06-05 03:39:37 -04:00
@classmethod
def url_to_id(cls, url: str):
m = re.match(cls.URL_PATTERNS[0], url)
if not m:
return None
return m.group(1) + "-" + m.group(2)
2023-06-05 02:04:52 -04:00
@classmethod
def id_to_url(cls, id_value):
ids = id_value.split("-")
return f"https://www.douban.com/location/drama/{ids[0]}/#{ids[1]}"
def scrape(self):
2025-01-04 11:23:07 -05:00
if not self.id_value or not self.url:
raise ParseError(self, "id_value or url")
2023-06-05 02:04:52 -04:00
show_url = self.url.split("#")[0]
show_id = self.id_value.split("-")[0]
version_id = self.id_value.split("-")[1]
key = _cache_key(show_url)
r = cache.get(key, None)
if r is None:
r = DoubanDownloader(show_url).download().content.decode("utf-8")
cache.set(key, r, 3600)
h = html.fromstring(r)
p = "//div[@id='" + version_id + "']"
q = p + "//dt[text()='{}']/following-sibling::dd[1]/a/span/text()"
q2 = p + "//dt[text()='{}']/following-sibling::dd[1]/text()"
2025-01-04 11:23:07 -05:00
title = " ".join(self.query_list(h, p + "//h3/text()")).strip()
2023-06-05 03:39:37 -04:00
if not title:
raise ParseError(self, "title")
2023-06-05 02:04:52 -04:00
data = {
2023-06-05 03:39:37 -04:00
"title": title,
2024-07-13 00:16:47 -04:00
"localized_title": [{"lang": "zh-cn", "text": title}],
2025-01-04 11:23:07 -05:00
"director": [x.strip() for x in self.query_list(h, q.format("导演"))],
"playwright": [x.strip() for x in self.query_list(h, q.format("编剧"))],
# "actor": [x.strip() for x in self.query_list(h, q.format("主演"))],
"composer": [x.strip() for x in self.query_list(h, q.format("作曲"))],
"language": [x.strip() for x in self.query_list(h, q2.format("语言"))],
"opening_date": " ".join(self.query_list(h, q2.format("演出日期"))).strip(),
"troupe": [x.strip() for x in self.query_list(h, q.format("演出团体"))],
"location": [x.strip() for x in self.query_list(h, q.format("演出剧院"))],
2023-06-05 02:04:52 -04:00
}
2023-06-05 11:45:57 -04:00
if data["opening_date"]:
d = data["opening_date"].split("-")
2024-07-27 03:22:27 -04:00
dl = len(d) if len(d) < 6 else 6
if dl > 3:
2023-06-05 11:45:57 -04:00
data["opening_date"] = "-".join(d[:3])
2024-07-27 03:22:27 -04:00
data["closing_date"] = "-".join(d[0 : 6 - dl] + d[3:dl])
2025-01-04 11:23:07 -05:00
actor_elem = self.query_list(
h, p + "//dt[text()='主演:']/following-sibling::dd[1]/a"
)
2023-06-05 17:22:34 -04:00
data["actor"] = []
for e in actor_elem:
n = "".join(e.xpath("span/text()")).strip()
t = "".join(e.xpath("following-sibling::text()[1]")).strip()
t = re.sub(r"^[\s\(饰]*(.+)\)[\s\/]*$", r"\1", t).strip()
t = t if t != "/" else ""
data["actor"].append({"name": n, "role": t})
2025-01-04 11:23:07 -05:00
img_url_elem = self.query_list(h, "//img[@itemprop='image']/@src")
2023-06-05 03:39:37 -04:00
data["cover_image_url"] = img_url_elem[0].strip() if img_url_elem else None
2023-06-05 02:04:52 -04:00
pd = ResourceContent(metadata=data)
pd.metadata["required_resources"] = [
{
"model": "Performance",
"id_type": IdType.DoubanDrama,
"id_value": show_id,
"title": f"Douban Drama {show_id}",
"url": show_url,
}
]
return pd
2022-12-15 17:29:35 -05:00
@SiteManager.register
class DoubanDrama(AbstractSite):
2022-12-16 01:08:10 -05:00
SITE_NAME = SiteName.Douban
ID_TYPE = IdType.DoubanDrama
2023-07-19 11:12:58 -04:00
URL_PATTERNS = [
r"\w+://www.douban.com/location/drama/(\d+)/[^#]*$",
r"\w+://www.douban.com/doubanapp/dispatch\?uri=/drama/(\d+)/",
2024-11-22 17:38:21 -05:00
r"\w+://www.douban.com/doubanapp/dispatch/drama/(\d+)",
2023-07-19 11:12:58 -04:00
]
2022-12-29 23:57:02 -05:00
WIKI_PROPERTY_ID = "P6443"
DEFAULT_MODEL = Performance
@classmethod
2023-02-15 23:45:12 -05:00
def id_to_url(cls, id_value):
return "https://www.douban.com/location/drama/" + id_value + "/"
def scrape(self):
2023-06-05 02:04:52 -04:00
key = _cache_key(self.url)
r = cache.get(key, None)
if r is None:
r = DoubanDownloader(self.url).download().content.decode("utf-8")
cache.set(key, r, 3600)
h = html.fromstring(r)
data = {}
2025-01-04 11:23:07 -05:00
title_elem = self.query_list(h, "/html/body//h1/span/text()")
if title_elem:
data["title"] = title_elem[0].strip()
2023-06-05 02:04:52 -04:00
data["orig_title"] = title_elem[1] if len(title_elem) > 1 else None
else:
raise ParseError(self, "title")
2025-01-04 11:23:07 -05:00
other_title_elem = self.query_list(
h, "//dl//dt[text()='又名:']/following::dd[@itemprop='name']/text()"
2022-12-29 23:57:02 -05:00
)
2023-06-05 02:04:52 -04:00
data["other_title"] = other_title_elem
2025-01-04 11:23:07 -05:00
plot_elem = self.query_list(
h, "//div[@class='pure-text']/div[@class='full']/text()"
)
if len(plot_elem) == 0:
2025-01-04 11:23:07 -05:00
plot_elem = self.query_list(
h, "//div[@class='pure-text']/div[@class='abstract']/text()"
2023-06-05 13:30:40 -04:00
)
if len(plot_elem) == 0:
2025-01-04 11:23:07 -05:00
plot_elem = self.query_list(h, "//div[@class='pure-text']/text()")
2023-06-05 13:30:40 -04:00
data["brief"] = "\n".join(plot_elem)
2023-02-15 23:45:12 -05:00
data["genre"] = [
2022-12-29 23:57:02 -05:00
s.strip()
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']//dl//dt[text()='类型:']/following-sibling::dd[@itemprop='genre']/text()",
2022-12-29 23:57:02 -05:00
)
]
2023-06-05 02:04:52 -04:00
# data["version"] = [
# s.strip()
2025-01-04 11:23:07 -05:00
# for s in self.query_list(h,
2023-06-05 02:04:52 -04:00
# "//dl//dt[text()='版本:']/following-sibling::dd[@class='titles']/a//text()"
# )
# ]
2023-02-15 23:45:12 -05:00
data["director"] = [
2022-12-29 23:57:02 -05:00
s.strip()
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']/dl//dt[text()='导演:']/following-sibling::dd/a[@itemprop='director']//text()",
2022-12-29 23:57:02 -05:00
)
]
2023-02-15 23:45:12 -05:00
data["composer"] = [
s.strip()
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']/dl//dt[text()='作曲:']/following-sibling::dd/a[@itemprop='musicBy']//text()",
2023-02-15 23:45:12 -05:00
)
]
data["choreographer"] = [
s.strip()
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']/dl//dt[text()='编舞:']/following-sibling::dd/a[@itemprop='choreographer']//text()",
2023-02-15 23:45:12 -05:00
)
]
data["troupe"] = [
s.strip()
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']/dl//dt[text()='演出团体:']/following-sibling::dd/a[@itemprop='performer']//text()",
2023-02-15 23:45:12 -05:00
)
]
data["playwright"] = [
2022-12-29 23:57:02 -05:00
s.strip()
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']/dl//dt[text()='编剧:']/following-sibling::dd/a[@itemprop='author']//text()",
2022-12-29 23:57:02 -05:00
)
]
2023-06-05 17:22:34 -04:00
data["actor"] = [
{"name": s.strip(), "role": ""}
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']/dl//dt[text()='主演:']/following-sibling::dd/a[@itemprop='actor']//text()",
2022-12-29 23:57:02 -05:00
)
]
2025-01-04 11:23:07 -05:00
date_elem = self.query_list(
h, "//div[@class='meta']//dl//dt[text()='演出日期:']/following::dd/text()"
2023-06-05 11:45:57 -04:00
)
2023-02-15 23:45:12 -05:00
data["opening_date"] = date_elem[0] if date_elem else None
2023-06-05 11:45:57 -04:00
if data["opening_date"]:
d = data["opening_date"].split("-")
2024-07-27 03:22:27 -04:00
dl = len(d) if len(d) < 6 else 6
if dl > 3:
2023-06-05 11:45:57 -04:00
data["opening_date"] = "-".join(d[:3])
2024-07-27 03:22:27 -04:00
data["closing_date"] = "-".join(d[0 : 6 - dl] + d[3:dl])
2023-02-15 23:45:12 -05:00
2023-06-05 02:04:52 -04:00
data["location"] = [
2023-02-15 23:45:12 -05:00
s.strip()
2025-01-04 11:23:07 -05:00
for s in self.query_list(
h,
"//div[@class='meta']/dl//dt[text()='演出剧院:']/following-sibling::dd/a[@itemprop='location']//text()",
2023-02-15 23:45:12 -05:00
)
]
2025-01-04 11:23:07 -05:00
versions = self.query_list(
h, "//div[@id='versions']/div[@class='fluid-mods']/div/@id"
)
2023-06-05 02:04:52 -04:00
data["related_resources"] = list(
map(
lambda v: {
"model": "PerformanceProduction",
"id_type": IdType.DoubanDramaVersion,
"id_value": f"{self.id_value}-{v}",
"title": f"{data['title']} - {v}",
"url": f"{self.url}#{v}",
},
versions,
)
)
2025-01-04 11:23:07 -05:00
img_url_elem = self.query_list(h, "//img[@itemprop='image']/@src")
2022-12-29 23:57:02 -05:00
data["cover_image_url"] = img_url_elem[0].strip() if img_url_elem else None
2024-07-13 00:16:47 -04:00
data["localized_title"] = (
[{"lang": "zh-cn", "text": data["title"]}]
+ (
[
{
"lang": detect_language(data["orig_title"]),
"text": data["orig_title"],
}
]
if data["orig_title"]
else []
)
+ [{"lang": detect_language(t), "text": t} for t in data["other_title"]]
)
data["localized_description"] = [{"lang": "zh-cn", "text": data["brief"]}]
2022-12-08 16:08:59 +00:00
pd = ResourceContent(metadata=data)
return pd