novel-downloader 1.4.3__py3-none-any.whl → 1.4.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- novel_downloader/__init__.py +1 -1
- novel_downloader/core/fetchers/qidian/browser.py +2 -2
- novel_downloader/core/fetchers/qidian/session.py +2 -1
- novel_downloader/core/parsers/qidian/book_info_parser.py +31 -54
- novel_downloader/tui/screens/login.py +1 -1
- novel_downloader/utils/time_utils/datetime_utils.py +1 -1
- {novel_downloader-1.4.3.dist-info → novel_downloader-1.4.4.dist-info}/METADATA +1 -1
- {novel_downloader-1.4.3.dist-info → novel_downloader-1.4.4.dist-info}/RECORD +12 -12
- {novel_downloader-1.4.3.dist-info → novel_downloader-1.4.4.dist-info}/WHEEL +0 -0
- {novel_downloader-1.4.3.dist-info → novel_downloader-1.4.4.dist-info}/entry_points.txt +0 -0
- {novel_downloader-1.4.3.dist-info → novel_downloader-1.4.4.dist-info}/licenses/LICENSE +0 -0
- {novel_downloader-1.4.3.dist-info → novel_downloader-1.4.4.dist-info}/top_level.txt +0 -0
novel_downloader/__init__.py
CHANGED
@@ -22,8 +22,8 @@ class QidianBrowser(BaseBrowser):
|
|
22
22
|
|
23
23
|
HOMEPAGE_URL = "https://www.qidian.com/"
|
24
24
|
BOOKCASE_URL = "https://my.qidian.com/bookcase/"
|
25
|
-
BOOK_INFO_URL = "https://book.qidian.com/info/{book_id}/"
|
26
|
-
|
25
|
+
# BOOK_INFO_URL = "https://book.qidian.com/info/{book_id}/"
|
26
|
+
BOOK_INFO_URL = "https://www.qidian.com/book/{book_id}/"
|
27
27
|
CHAPTER_URL = "https://www.qidian.com/chapter/{book_id}/{chapter_id}/"
|
28
28
|
|
29
29
|
LOGIN_URL = "https://passport.qidian.com/"
|
@@ -27,7 +27,8 @@ class QidianSession(BaseSession):
|
|
27
27
|
|
28
28
|
HOMEPAGE_URL = "https://www.qidian.com/"
|
29
29
|
BOOKCASE_URL = "https://my.qidian.com/bookcase/"
|
30
|
-
BOOK_INFO_URL = "https://book.qidian.com/info/{book_id}/"
|
30
|
+
# BOOK_INFO_URL = "https://book.qidian.com/info/{book_id}/"
|
31
|
+
BOOK_INFO_URL = "https://www.qidian.com/book/{book_id}/"
|
31
32
|
CHAPTER_URL = "https://www.qidian.com/chapter/{book_id}/{chapter_id}/"
|
32
33
|
|
33
34
|
LOGIN_URL = "https://passport.qidian.com/"
|
@@ -10,36 +10,19 @@ time, status, word count, summary, and volume-chapter structure.
|
|
10
10
|
"""
|
11
11
|
|
12
12
|
import logging
|
13
|
+
import re
|
14
|
+
from datetime import datetime
|
13
15
|
from typing import Any
|
14
16
|
|
15
17
|
from lxml import html
|
16
18
|
|
17
19
|
logger = logging.getLogger(__name__)
|
18
20
|
|
19
|
-
_AUTHOR_XPATH = (
|
20
|
-
'string(//div[contains(@class, "book-info")]//a[contains(@class, "writer")])'
|
21
|
-
)
|
22
|
-
|
23
21
|
|
24
22
|
def _chapter_url_to_id(url: str) -> str:
|
25
23
|
return url.rstrip("/").split("/")[-1]
|
26
24
|
|
27
25
|
|
28
|
-
def _get_volume_name(
|
29
|
-
vol_elem: html.HtmlElement,
|
30
|
-
) -> str:
|
31
|
-
"""
|
32
|
-
Extracts the volume title from a <div class="volume"> element using lxml.
|
33
|
-
Ignores <a> tags, and extracts text from other elements.
|
34
|
-
"""
|
35
|
-
h3_candidates = vol_elem.xpath(".//h3")
|
36
|
-
if not h3_candidates:
|
37
|
-
return ""
|
38
|
-
texts = vol_elem.xpath(".//h3//text()[not(ancestor::a)]")
|
39
|
-
full_text = "".join(texts).strip()
|
40
|
-
return full_text.split(chr(183))[0].strip()
|
41
|
-
|
42
|
-
|
43
26
|
def parse_book_info(html_str: str) -> dict[str, Any]:
|
44
27
|
"""
|
45
28
|
Extract metadata: title, author, cover_url, update_time, status,
|
@@ -52,59 +35,53 @@ def parse_book_info(html_str: str) -> dict[str, Any]:
|
|
52
35
|
try:
|
53
36
|
doc = html.fromstring(html_str)
|
54
37
|
|
55
|
-
book_name = doc.xpath('string(//h1
|
56
|
-
info["book_name"] = book_name
|
38
|
+
info["book_name"] = doc.xpath('string(//h1[@id="bookName"])').strip()
|
57
39
|
|
58
|
-
author = doc.xpath(
|
59
|
-
info["author"] = author
|
40
|
+
info["author"] = doc.xpath('string(//a[@class="writer-name"])').strip()
|
60
41
|
|
61
|
-
|
62
|
-
info[
|
42
|
+
book_id = doc.xpath('//a[@id="bookImg"]/@data-bid')[0]
|
43
|
+
info[
|
44
|
+
"cover_url"
|
45
|
+
] = f"https://bookcover.yuewen.com/qdbimg/349573/{book_id}/600.webp"
|
63
46
|
|
64
|
-
|
65
|
-
doc.xpath('string(//span[
|
66
|
-
.replace("
|
47
|
+
ut = (
|
48
|
+
doc.xpath('string(//span[@class="update-time"])')
|
49
|
+
.replace("更新时间:", "")
|
67
50
|
.strip()
|
68
51
|
)
|
69
|
-
|
52
|
+
if re.match(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$", ut):
|
53
|
+
info["update_time"] = ut
|
54
|
+
else:
|
55
|
+
info["update_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
70
56
|
|
71
|
-
|
72
|
-
|
57
|
+
info["serial_status"] = doc.xpath(
|
58
|
+
'string(//p[@class="book-attribute"]/span[1])'
|
59
|
+
).strip()
|
73
60
|
|
74
|
-
tags = doc.xpath('//p[@class
|
61
|
+
tags = doc.xpath('//p[contains(@class,"all-label")]//a/text()')
|
75
62
|
info["tags"] = [t.strip() for t in tags if t.strip()]
|
76
63
|
|
77
|
-
|
78
|
-
wc_unit = doc.xpath("string(//p[em and cite][1]/cite[1])").strip()
|
79
|
-
info["word_count"] = (
|
80
|
-
(wc_number + wc_unit) if wc_number and wc_unit else "Unknown"
|
81
|
-
)
|
64
|
+
info["word_count"] = doc.xpath('string(//p[@class="count"]/em[1])').strip()
|
82
65
|
|
83
66
|
summary = doc.xpath('string(//p[@class="intro"])').strip()
|
84
67
|
info["summary_brief"] = summary
|
85
68
|
|
86
|
-
|
87
|
-
|
88
|
-
info["summary"] = detail_intro
|
69
|
+
raw = doc.xpath('//p[@id="book-intro-detail"]//text()')
|
70
|
+
info["summary"] = "\n".join(line.strip() for line in raw if line.strip())
|
89
71
|
|
90
72
|
volumes = []
|
91
|
-
for
|
92
|
-
|
73
|
+
for vol in doc.xpath('//div[@id="allCatalog"]//div[@class="catalog-volume"]'):
|
74
|
+
vol_name = vol.xpath('string(.//h3[@class="volume-name"])').strip()
|
75
|
+
vol_name = vol_name.split(chr(183))[0].strip()
|
93
76
|
chapters = []
|
94
|
-
for li in
|
95
|
-
a = li.xpath(
|
96
|
-
|
97
|
-
|
98
|
-
href = a.attrib["href"].strip()
|
99
|
-
title = "".join(a.itertext()).strip()
|
77
|
+
for li in vol.xpath('.//ul[contains(@class,"volume-chapters")]/li'):
|
78
|
+
a = li.xpath('.//a[@class="chapter-name"]')[0]
|
79
|
+
title = a.text.strip()
|
80
|
+
url = a.get("href")
|
100
81
|
chapters.append(
|
101
|
-
{
|
102
|
-
"title": title,
|
103
|
-
"url": href,
|
104
|
-
"chapterId": _chapter_url_to_id(href),
|
105
|
-
}
|
82
|
+
{"title": title, "url": url, "chapterId": _chapter_url_to_id(url)}
|
106
83
|
)
|
107
|
-
volumes.append({"volume_name":
|
84
|
+
volumes.append({"volume_name": vol_name, "chapters": chapters})
|
108
85
|
info["volumes"] = volumes
|
109
86
|
|
110
87
|
except Exception as e:
|
@@ -1,4 +1,4 @@
|
|
1
|
-
novel_downloader/__init__.py,sha256=
|
1
|
+
novel_downloader/__init__.py,sha256=dWsU4eiEbY9H2_GSR5Y5XdVhEH4CyEbDyc7rzVr79qI,218
|
2
2
|
novel_downloader/cli/__init__.py,sha256=-2HAut_U1e67MZGdvbpEJ1n5J-bRchzto6L4c-nWeXY,174
|
3
3
|
novel_downloader/cli/clean.py,sha256=hOk8SJQwBCw2oOObTdEI79wpnmZ25uB1s9LQK1-4LNU,4487
|
4
4
|
novel_downloader/cli/config.py,sha256=C6QLfegZLp4legmu8KenqyYKNdrk47bH0z86ujLP0pY,6509
|
@@ -63,8 +63,8 @@ novel_downloader/core/fetchers/qianbi/__init__.py,sha256=h4Rve7fO1GcSJ-DlNC5zw7f
|
|
63
63
|
novel_downloader/core/fetchers/qianbi/browser.py,sha256=1EmrSwpqSYhEO_ID3RJbUaAOhcqvVnMnch6iOafXbTA,3162
|
64
64
|
novel_downloader/core/fetchers/qianbi/session.py,sha256=c3pJcgi9C1x9QYTBihvazHcgT7XTp2HBYfStTn6gSEg,3141
|
65
65
|
novel_downloader/core/fetchers/qidian/__init__.py,sha256=2LshlX82lFpWZMV6yujHsfue9KM0-F1O3HvMCopIv9M,236
|
66
|
-
novel_downloader/core/fetchers/qidian/browser.py,sha256=
|
67
|
-
novel_downloader/core/fetchers/qidian/session.py,sha256=
|
66
|
+
novel_downloader/core/fetchers/qidian/browser.py,sha256=0L2iUyOlFNmgKtSb89VMrF7L-MjiJy4E6SSKWxzeSC8,10448
|
67
|
+
novel_downloader/core/fetchers/qidian/session.py,sha256=dsawkalQGjbS-E9lcOZMTr9qZz6w__gnmAg-uRUtDKI,9809
|
68
68
|
novel_downloader/core/fetchers/sfacg/__init__.py,sha256=bQAIwERsX9XOKrP2LteFKX8Jlhw4oeUNwpZTHXn5RRg,230
|
69
69
|
novel_downloader/core/fetchers/sfacg/browser.py,sha256=15PVS75PxEKR5W7mQbqVxoN0d4V1XVYVF0l1yy_sv_Y,5681
|
70
70
|
novel_downloader/core/fetchers/sfacg/session.py,sha256=9K4emQCRq45vzYn-ZDX549tK2F92x2CBMp4ODohNOjc,5085
|
@@ -90,7 +90,7 @@ novel_downloader/core/parsers/linovelib/main_parser.py,sha256=LqC6W-Lk7sdbiqhYvq
|
|
90
90
|
novel_downloader/core/parsers/qianbi/__init__.py,sha256=CNmoER8U2u4-ix5S0DDq-pHTtkLR0IZf2SLaTTYXee4,173
|
91
91
|
novel_downloader/core/parsers/qianbi/main_parser.py,sha256=kMjGew_dmqjI9oIThyHgxThZZgS3IaESB_tNf1nkCKk,5069
|
92
92
|
novel_downloader/core/parsers/qidian/__init__.py,sha256=fWWFeyythX0gpDCJ-2AslrRl2hq4vW2bx9hHh1W7mAw,173
|
93
|
-
novel_downloader/core/parsers/qidian/book_info_parser.py,sha256=
|
93
|
+
novel_downloader/core/parsers/qidian/book_info_parser.py,sha256=TbP8nghoxzBi435cSJKAzWttBRUq49MHw6kn0klfZrU,3056
|
94
94
|
novel_downloader/core/parsers/qidian/chapter_encrypted.py,sha256=uIVmRH96O_G0hcvg2nufUQDrZzq_XRhfvB_8YRW22g8,18532
|
95
95
|
novel_downloader/core/parsers/qidian/chapter_normal.py,sha256=CClt6zVph7qjZ0HaiYzMPYlcFgnPIY783-SLVIXNL7E,4738
|
96
96
|
novel_downloader/core/parsers/qidian/chapter_router.py,sha256=foVMlWtE-qUOvJD_4EDiuAVaNkFdeV_ZTCvS5IL7Orc,1957
|
@@ -126,7 +126,7 @@ novel_downloader/tui/app.py,sha256=ytV1u15nGCRj_ff_GeAL3W1XlU6r5Lh_k3HBcAjPRx0,7
|
|
126
126
|
novel_downloader/tui/main.py,sha256=MBP8SrwEYTpGQm-V9W_4rKnTeslneORfkzFsU3Xj2yA,256
|
127
127
|
novel_downloader/tui/screens/__init__.py,sha256=QsUM5cUEKm7nluQh9acEt37xRWbkZU3vqIpBduepDCU,203
|
128
128
|
novel_downloader/tui/screens/home.py,sha256=nH3pRuCOFcSvNV4AYC6RRkWpphmRgY1bNK36OkSVfCg,6994
|
129
|
-
novel_downloader/tui/screens/login.py,sha256=
|
129
|
+
novel_downloader/tui/screens/login.py,sha256=GK8dXCtH5lGu3ilT1Cv2VmFCwHMspDNpzw0logj2lz0,2225
|
130
130
|
novel_downloader/tui/styles/home_layout.tcss,sha256=VNJs339qiwNUuqwwdK6VkYThCdsqlw-2mEoZVCspdrw,974
|
131
131
|
novel_downloader/tui/widgets/richlog_handler.py,sha256=bhFb0E7Z7-dRS07y_vMjxjPeCic9AD59MFPLhef_R5g,572
|
132
132
|
novel_downloader/utils/__init__.py,sha256=4iUXNUzxeAnGmpGWsB4K_jckUYEW0u_LqWp_OM7mtK8,78
|
@@ -155,11 +155,11 @@ novel_downloader/utils/text_utils/diff_display.py,sha256=aUvjMcYO-1_P8ZYiYbmYbJO
|
|
155
155
|
novel_downloader/utils/text_utils/font_mapping.py,sha256=Aos5skBhowDdPgnYmK0bpLtNm2hZg3RolNlTkxC9kO8,865
|
156
156
|
novel_downloader/utils/text_utils/text_cleaning.py,sha256=zufXt0pc0vnUwgSHD_8LAef3ffqmX9rJ11YzW4rgwLA,2940
|
157
157
|
novel_downloader/utils/time_utils/__init__.py,sha256=725vY2PvqFhjbAz0hCOuIuhSCK8HrEqQ_k3YwvubmXo,624
|
158
|
-
novel_downloader/utils/time_utils/datetime_utils.py,sha256=
|
158
|
+
novel_downloader/utils/time_utils/datetime_utils.py,sha256=3sPdUjDC7Y7dcX6kaQSBu49BU1tnpERNKq0v9lnD170,4932
|
159
159
|
novel_downloader/utils/time_utils/sleep_utils.py,sha256=C4XYeAtxoVZC9Ju6vhhP9sbOrSpdZG2Nm-x1IYO_OFA,3233
|
160
|
-
novel_downloader-1.4.
|
161
|
-
novel_downloader-1.4.
|
162
|
-
novel_downloader-1.4.
|
163
|
-
novel_downloader-1.4.
|
164
|
-
novel_downloader-1.4.
|
165
|
-
novel_downloader-1.4.
|
160
|
+
novel_downloader-1.4.4.dist-info/licenses/LICENSE,sha256=XgmnH0mBf-qEiizoVAfJQAKzPB9y3rBa-ni7M0Aqv4A,1066
|
161
|
+
novel_downloader-1.4.4.dist-info/METADATA,sha256=JrEV-At77txIIajUrfOix5WYcMVvRkhjf5h601ozbVg,7253
|
162
|
+
novel_downloader-1.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
163
|
+
novel_downloader-1.4.4.dist-info/entry_points.txt,sha256=u1Ns5xI_QJyL4HAFCgJvJdib9ugu7M9I2tnQwZjJxrk,112
|
164
|
+
novel_downloader-1.4.4.dist-info/top_level.txt,sha256=hP4jYWM2LTm1jxsW4hqEB8N0dsRvldO2QdhggJT917I,17
|
165
|
+
novel_downloader-1.4.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|