@cloud411716/fancy-webnovel 0.2.20 → 0.2.22
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.
package/package.json
CHANGED
|
@@ -293,33 +293,28 @@ async function scrapeRank(rankTypeId, channelId) {
|
|
|
293
293
|
"",
|
|
294
294
|
`- 来源:${url}`,
|
|
295
295
|
`- 抓取时间:${now}`,
|
|
296
|
+
`- 条目数:${totalBooks}`,
|
|
296
297
|
"",
|
|
297
298
|
"---",
|
|
298
299
|
"",
|
|
299
300
|
];
|
|
300
301
|
|
|
301
302
|
for (const ch of data.channels) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
lines.push(
|
|
315
|
-
|
|
316
|
-
// 布局B(交替行型):只有书名/作者,其他留空让详情补采
|
|
317
|
-
lines.push(`题材:${ch.name}`);
|
|
303
|
+
for (let i = 0; i < ch.books.length; i++) {
|
|
304
|
+
const b = ch.books[i];
|
|
305
|
+
var genre = '';
|
|
306
|
+
if (data.layout === 'A' && b.type) {
|
|
307
|
+
var parts = b.type.split('-');
|
|
308
|
+
genre = parts[1] || '';
|
|
309
|
+
} else {
|
|
310
|
+
genre = ch.name;
|
|
311
|
+
}
|
|
312
|
+
lines.push(`书名:${b.title || ""}`);
|
|
313
|
+
lines.push(`题材:${genre}`);
|
|
314
|
+
lines.push(`作者:${b.author || ""}`);
|
|
315
|
+
if (b.novelid) lines.push(`作品页:https://www.jjwxc.net/onebook.php?novelid=${b.novelid}`);
|
|
316
|
+
lines.push("");
|
|
318
317
|
}
|
|
319
|
-
if (b.novelid) lines.push(`作品页:https://www.jjwxc.net/onebook.php?novelid=${b.novelid}`);
|
|
320
|
-
lines.push("");
|
|
321
|
-
}
|
|
322
|
-
lines.push("--", "");
|
|
323
318
|
}
|
|
324
319
|
|
|
325
320
|
return {
|
|
@@ -183,6 +183,7 @@ async function scrapeRank(channelId, rankTypeId, periodId) {
|
|
|
183
183
|
if (!b.bookId && !b.novel_id && !b.id) continue;
|
|
184
184
|
const id = String(b.bookId || b.novel_id || b.id);
|
|
185
185
|
xhrBookMap.set(id, {
|
|
186
|
+
id: id,
|
|
186
187
|
title: b.title || b.bookName || b.name || "",
|
|
187
188
|
author: b.author || b.authorName || "",
|
|
188
189
|
});
|
|
@@ -278,6 +279,28 @@ async function scrapeRank(channelId, rankTypeId, periodId) {
|
|
|
278
279
|
rawCount = rawBooks.length;
|
|
279
280
|
books = rawBooks.filter(isUsableBook);
|
|
280
281
|
|
|
282
|
+
// 直接从 DOM 提取 /shuku/{id}/ 链接补到每本书上(不依赖 XHR 匹配)
|
|
283
|
+
const urlMap = {};
|
|
284
|
+
await page.evaluate(() => {
|
|
285
|
+
document.querySelectorAll('a[href*="/shuku/"]').forEach(a => {
|
|
286
|
+
var m = a.href.match(/\/shuku\/(\d+)\//);
|
|
287
|
+
if (!m) return;
|
|
288
|
+
var id = m[1];
|
|
289
|
+
// 取离它最近的文字作为书名
|
|
290
|
+
var title = (a.innerText || '').trim();
|
|
291
|
+
if (title && title.length < 50 && title.length > 0) {
|
|
292
|
+
urlMap[title] = 'https://www.qimao.com/shuku/' + id + '/';
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
const norm = s => (s || '').replace(/\s+/g, '');
|
|
297
|
+
for (const b of books) {
|
|
298
|
+
if (!b.url) {
|
|
299
|
+
var found = Object.entries(urlMap).find(([t]) => norm(t) === norm(b.title));
|
|
300
|
+
if (found) b.url = found[1];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
281
304
|
// 用 XHR 数据覆盖明文书名/作者,绕过字体加密
|
|
282
305
|
if (xhrBookMap.size > 0) {
|
|
283
306
|
let updated = 0;
|