From a0eccf7370b64934b7feb71186cd83f82392cb10 Mon Sep 17 00:00:00 2001 From: YuBai Date: Mon, 2 Feb 2026 11:24:07 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20handle()=20=E6=96=B9=E6=B3=95=20catc?= =?UTF-8?q?h=20=E5=9D=97=E8=BF=94=E5=9B=9E=20undefined=20=E8=80=8C?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=E9=94=99=E8=AF=AF=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当 HTTP 请求失败时,返回错误对象会导致上层代码尝试访问错误对象的属性, 造成 TypeError。改为返回 undefined 让上层代码正确处理空值情况。 --- .../douban/data/handler/DoubanAbstractLoadHandler.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts index 10e27a4..a4ac748 100644 --- a/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts @@ -142,7 +142,7 @@ export default abstract class DoubanAbstractLoadHandler }else { context.syncStatusHolder?context.syncStatusHolder.syncStatus.handled(1):null; } - return e; + return undefined; }); @@ -608,12 +608,20 @@ export default abstract class DoubanAbstractLoadHandler handlePersonNameByMeta(html: CheerioAPI, movie: DoubanSubject, context: HandleContext, metaProperty:string, objectProperty:string) { + if (!movie) { + return; + } const metaProperties: string[] = html(`head > meta[property='${metaProperty}']`).get() .map((e) => { return html(e).attr('content'); }); // @ts-ignore - movie[objectProperty] + const currentArray = movie[objectProperty]; + if (!Array.isArray(currentArray)) { + return; + } + // @ts-ignore + currentArray // @ts-ignore .filter((p:Person) => p.name) // @ts-ignore From 297ccd33cfe4c71aca96e62bec23b22527b6f8a2 Mon Sep 17 00:00:00 2001 From: YuBai Date: Mon, 2 Feb 2026 11:25:56 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E5=BD=93=20JSON-LD=20=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=A4=B1=E8=B4=A5=E6=97=B6=E6=B7=BB=E5=8A=A0=E5=9B=9E?= =?UTF-8?q?=E9=80=80=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 豆瓣现在对未登录请求返回反爬虫验证页面,导致 JSON-LD 解析返回 undefined。 添加从 OG meta 标签提取基本信息的回退机制,防止代码崩溃。 同时给所有数组字段添加默认值 || []。 --- .../data/handler/DoubanMovieLoadHandler.ts | 82 ++++++++++++++----- .../data/handler/DoubanTeleplayLoadHandler.ts | 58 +++++++++++-- 2 files changed, 111 insertions(+), 29 deletions(-) diff --git a/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts index 1ff1ea0..908ca42 100644 --- a/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts @@ -35,24 +35,24 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler c) + (extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ @@ -98,7 +98,7 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler "application/ld+json" == html(scd).attr("type")) .map(i => { @@ -108,8 +108,8 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler meta[property='og:title']").get(0)).attr("content") || ''; + const image = html(html("head > meta[property='og:image']").get(0)).attr("content") || ''; + const urlMeta = html(html("head > meta[property='og:url']").get(0)).attr("content") || ''; + const desc = html(html("head > meta[property='og:description']").get(0)).attr("content") || ''; + + // Extract ID from URL + const idPattern = /(\d){5,10}/g; + const idMatch = idPattern.exec(urlMeta); + const id = idMatch ? idMatch[0] : ''; + + // Extract score from HTML + const scoreText = html("#interest_sectl strong[property='v:average']").text(); + const score = scoreText ? parseFloat(scoreText) : undefined; + + movie = { + id, + title, + type: this.getSupportType(), + score, + originalTitle: title, + desc, + url: urlMeta || (id ? `https://movie.douban.com/subject/${id}/` : ''), + director: [], + author: [], + actor: [], + aggregateRating: undefined, + datePublished: undefined, + image, + imageUrl: image, + genre: [], + publisher: '', + aliases: [], + language: [], + country: [], + time: null, + IMDb: null, + }; + } + + this.handlePersonNameByMeta(html, movie, context, 'video:actor', 'actor'); + this.handlePersonNameByMeta(html, movie, context, 'video:director', 'director'); + + const desc: string = html("span[property='v:summary']").text(); if (desc) { movie.desc = desc; } @@ -156,7 +198,7 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler v.trim()); - } else if(key.indexOf('片长') >= 0) { + } else if (key.indexOf('片长') >= 0) { value = html(info.next.next).text().trim() } else { value = html(info.next).text().trim(); @@ -164,11 +206,11 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler, extract: DoubanTeleplaySubject, context: HandleContext): void { - variableMap.set("director", new DataField("director", DataValueType.array, extract.director,extract.director.map(SchemaOrg.getPersonName).filter(c => c))); + variableMap.set("director", new DataField("director", DataValueType.array, extract.director,(extract.director || []).map(SchemaOrg.getPersonName).filter(c => c))); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ @@ -84,7 +84,7 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler "application/ld+json" == html(scd).attr("type")) .map(i => { @@ -104,14 +104,14 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler meta[property='og:title']").get(0)).attr("content") || ''; + const image = html(html("head > meta[property='og:image']").get(0)).attr("content") || ''; + const urlMeta = html(html("head > meta[property='og:url']").get(0)).attr("content") || ''; + const desc = html(html("head > meta[property='og:description']").get(0)).attr("content") || ''; + + const idPattern = /(\d){5,10}/g; + const idMatch = idPattern.exec(urlMeta); + const id = idMatch ? idMatch[0] : ''; + + const scoreText = html("#interest_sectl strong[property='v:average']").text(); + const score = scoreText ? parseFloat(scoreText) : undefined; + + teleplay = { + id, + title, + type: this.getSupportType(), + score, + originalTitle: title, + desc, + url: urlMeta || (id ? `https://movie.douban.com/subject/${id}/` : ''), + director: [], + author: [], + actor: [], + aggregateRating: undefined, + datePublished: undefined, + image, + imageUrl: image, + genre: [], + publisher: '', + aliases: [], + language: [], + country: [], + episode: null, + time: null, + IMDb: null, + }; + } + this.handlePersonNameByMeta(html, teleplay, context, 'video:actor', 'actor'); this.handlePersonNameByMeta(html, teleplay, context, 'video:director', 'director'); const desc:string = html("span[property='v:summary']").text(); From 99d417062691e49a86b756e87334d0d70e73cacf Mon Sep 17 00:00:00 2001 From: YuBai Date: Mon, 2 Feb 2026 11:26:47 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E4=B8=BA=E5=85=B6=E4=BB=96=20handle?= =?UTF-8?q?r=20=E6=B7=BB=E5=8A=A0=E6=95=B0=E7=BB=84=E7=A9=BA=E5=80=BC?= =?UTF-8?q?=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 parseVariable 方法中为所有数组字段的 .map() 调用添加 (field || []) 保护, 防止当字段为 undefined 时调用 .map() 报错。 --- .../wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts | 8 ++++---- .../wanxp/douban/data/handler/DoubanBookLoadHandler.ts | 4 ++-- .../wanxp/douban/data/handler/DoubanGameLoadHandler.ts | 2 +- .../wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts b/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts index 3d04057..64f4283 100644 --- a/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts +++ b/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts @@ -33,24 +33,24 @@ export default class DoubanTheaterAiLoadHandler extends DoubanAbstractLoadHandle "director", DataValueType.array, extract.director, - extract.director.map(SchemaOrg.getPersonName).filter(c => c) + (extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ diff --git a/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts index 899ba04..64e2a36 100644 --- a/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts @@ -30,9 +30,9 @@ export default class DoubanBookLoadHandler extends DoubanAbstractLoadHandler, extract: DoubanBookSubject, context: HandleContext): void { variableMap.set(DoubanBookParameter.author, new DataField(DoubanBookParameter.author, - DataValueType.array, extract.author, extract.author.map(this.handleSpecialAuthorName))); + DataValueType.array, extract.author, (extract.author || []).map(this.handleSpecialAuthorName))); variableMap.set(DoubanBookParameter.translator, new DataField(DoubanBookParameter.translator, - DataValueType.array, extract.translator, extract.translator.map(this.handleSpecialAuthorName))); + DataValueType.array, extract.translator, (extract.translator || []).map(this.handleSpecialAuthorName))); } support(extract: DoubanSubject): boolean { diff --git a/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts index ca40aed..036d334 100644 --- a/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts @@ -31,7 +31,7 @@ export default class DoubanGameLoadHandler extends DoubanAbstractLoadHandler, extract: DoubanGameSubject, context: HandleContext): void { // super.parseAliases(beforeContent, variableMap, extract, context); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ diff --git a/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts index a27f8bb..2f3a25c 100644 --- a/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts @@ -33,28 +33,28 @@ export default class DoubanTheaterLoadHandler extends DoubanAbstractLoadHandler< "director", DataValueType.array, extract.director, - extract.director.map(SchemaOrg.getPersonName).filter(c => c) + (extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField( "aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a => a + (extract.aliases || []).map(a => a .trim() .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') //replace multiple _ to single _