Python实战:编写小说解析器自动提取素材供水墨江南模型再创作

最近在玩一些AI绘画和故事生成模型,比如水墨江南这种能生成极简中式风格的模型,感觉特别有意思。但有时候,直接给一个简单的描述,比如“一个侠客在竹林里”,生成的内容虽然好看,但总觉得少了点故事感和深度。

我就想,能不能先让AI“读懂”一个完整的故事,比如一篇网络小说,然后让它基于这个故事里的人物和场景,去创作一些新的画面或者番外故事呢?这样一来,生成的图像或文字就不再是孤立的场景,而是有背景、有情感、有前因后果的“故事新编”。

这个想法听起来复杂,其实拆解开来,核心就是两步:第一步,用Python写个工具,能自动从小说文本里把人物、地点、关键情节这些“素材”给提取出来;第二步,把这些结构化的素材,整理成清晰的指令,喂给像水墨江南这样的AI模型,引导它进行二次创作。

今天,我就来分享一下怎么动手实现这个“小说解析器”,并把它和水墨江南模型的创作流程结合起来。整个过程不需要高深的算法知识,用一些现成的Python库就能搞定,特别适合喜欢折腾AI应用的朋友。

1. 为什么需要小说解析器?

你可能觉得,直接把小说全文复制粘贴给AI不就行了吗?理论上可以,但实际效果往往不尽如人意。

现在的AI大模型,尤其是专注于图像生成或风格化创作的模型,它们的“注意力”是有限的。你给它一整部上百万字的小说,它很难精准地抓住你真正想让它描绘的那个核心人物、那个特定场景,或者那段关键情节。结果可能就是生成的内容过于泛泛,或者偏离主题。

小说解析器的作用,就是充当一个“信息提炼师”。它像是一个细心的读者,帮你快速浏览完整个故事,然后交给你一份清晰的“读书笔记”:

  • 人物档案:故事里有哪些主要角色?他们的名字、身份、关系是什么?(例如:主角-李逍遥,身份-客栈店小二,与赵灵儿是情侣关系)
  • 场景地图:故事发生在哪些典型的地点?(例如:余杭镇、仙灵岛、锁妖塔)
  • 情节梗概:整个故事的主线是什么?某个章节的核心冲突又是什么?(例如:第一章:李逍遥在客栈邂逅神秘少女赵灵儿,为其前往仙灵岛求药。)

有了这份结构化的“素材库”,你再给AI模型下指令时,就可以非常精确了。比如,你可以对水墨江南模型说:“请根据‘李逍遥’和‘仙灵岛’这两个要素,生成一幅充满邂逅与神秘感的极简中式场景图。” 这样的指令,比单纯说“画一个岛和一个人”要有指向性得多,AI生成的内容也会更贴合原作的意境。

简单来说,解析器把“大海”般的小说文本,过滤成了AI模型更容易吸收和发挥的“纯净水”,让后续的创作事半功倍。

2. 构建小说解析器的核心思路

写这个解析器,我们不追求像专业NLP论文里那样做到百分之百的准确,而是追求“可用”和“有趣”。我们的目标是提取出足够多、足够有代表性的素材,来激发AI的创作灵感。

整个流程可以分成几个关键步骤,我们用一张图来直观感受一下:

flowchart TD
    A[输入: 原始小说文本] --> B(文本预处理<br>清洗与分段)
    B --> C{核心信息提取}
    C --> D[人物识别]
    C --> E[场景/地点识别]
    C --> F[关键情节摘要]
    
    D --> G[结构化整理]
    E --> G
    F --> G
    
    G --> H[输出: JSON结构化素材库]
    H --> I[引导AI模型<br>(如“水墨江南”)]
    I --> J[生成新的图像或故事]

下面,我们来拆解每一步具体怎么做。

2.1 第一步:获取与预处理文本

首先,你得有小说文本。可以从某些提供文本下载的网站获取(请注意版权),或者手动复制粘贴。假设我们已经有了一个 novel.txt 文件。

预处理的目标是把乱七八糟的文本整理干净。常见的“噪音”包括网页残留的HTML标签、多余的空格和换行符、无关的广告文字等。

import re

def preprocess_text(text):
    """
    清洗小说文本。
    """
    # 移除常见的HTML标签(如果有的话)
    text = re.sub(r‘<[^>]+>‘, ‘ ‘, text)
    # 将多个连续的空格、换行、制表符替换为单个空格
    text = re.sub(r‘\s+‘, ‘ ‘, text)
    # 这里可以添加更多针对特定来源的清洗规则
    # 例如,移除“本章说”、“求订阅”等特定平台水印
    text = re.sub(r‘本章说.*?‘, ‘ ‘, text)
    return text.strip()

# 读取小说文件
with open(‘novel.txt‘, ‘r‘, encoding=‘utf-8‘) as f:
    raw_text = f.read()

cleaned_text = preprocess_text(raw_text)
print(f"文本清理完成,长度从 {len(raw_text)} 字符减少到 {len(cleaned_text)} 字符。")

预处理后,我们得到了干净的、连续的文本。接下来,为了后续分析章节,我们可以用一个简单的方法进行初步分章——寻找类似“第一章”、“第1章”或“##”这样的模式。

def split_into_chapters(text):
    """
    一个简单的基于正则表达式的分章函数。
    实际小说格式复杂,这里只是一个基础示例。
    """
    # 匹配“第X章”或“第XX章”等模式
    chapter_pattern = r‘(第[零一二三四五六七八九十百千万\d]+章[^\n]*)‘
    chapters = re.split(chapter_pattern, text)
    # re.split会保留分隔符,所以奇数索引是章节标题,偶数索引是章节内容
    chapter_list = []
    for i in range(1, len(chapters), 2):
        if i+1 < len(chapters):
            chapter_list.append({
                ‘title‘: chapters[i],
                ‘content‘: chapters[i+1][:2000]  # 只取前2000字符作为示例,避免太长
            })
    return chapter_list

chapters = split_into_chapters(cleaned_text)
print(f"识别到 {len(chapters)} 个章节。")
for i, chap in enumerate(chapters[:3]):  # 打印前三个章节看看
    print(f"章节 {i+1}: {chap[‘title‘]}")
    print(f"内容预览: {chap[‘content‘][:100]}...\n")

2.2 第二步:提取核心信息

这是解析器最核心也最有挑战的部分。我们将使用一些轻量级的自然语言处理库来帮忙。

人物识别:一个简单有效的方法是寻找文本中高频出现的、且符合中国人名特征的词汇。我们可以使用 jieba 库进行分词和词性标注,然后筛选出人名。

import jieba
import jieba.posseg as pseg
from collections import Counter

def extract_characters(text, top_k=10):
    """
    从文本中提取可能的人物名称。
    策略:找出高频的、被jieba标注为‘nr‘(人名)的词汇。
    """
    words = pseg.cut(text)
    name_candidates = []
    for word, flag in words:
        if flag == ‘nr‘ and len(word) >= 2:  # 人名且长度至少为2
            name_candidates.append(word)
    
    # 统计出现频率
    name_counter = Counter(name_candidates)
    # 返回出现频率最高的前top_k个作为主要人物候选
    most_common_names = name_counter.most_common(top_k)
    
    # 简单过滤掉一些常见的非人名高频词(如地名、通用名词)
    common_non_names = {‘中国‘, ‘美国‘, ‘公司‘, ‘先生‘, ‘小姐‘}
    characters = [name for name, count in most_common_names if name not in common_non_names]
    
    return characters[:5]  # 返回最有可能的前5个

# 对整部小说或第一章进行人物提取
sample_text = chapters[0][‘content‘] if chapters else cleaned_text[:5000]
potential_characters = extract_characters(sample_text)
print(f"识别到的主要人物候选: {potential_characters}")

场景/地点识别:方法与人物识别类似,我们关注标注为‘ns‘(地名)的词汇。

def extract_locations(text, top_k=10):
    """
    从文本中提取可能的地点名称。
    策略:找出高频的、被jieba标注为‘ns‘(地名)的词汇。
    """
    words = pseg.cut(text)
    location_candidates = []
    for word, flag in words:
        if flag == ‘ns‘ and len(word) >= 2:
            location_candidates.append(word)
    
    location_counter = Counter(location_candidates)
    most_common_locations = location_counter.most_common(top_k)
    
    common_non_locations = {‘今天‘, ‘明天‘, ‘时候‘}  # 误判过滤
    locations = [loc for loc, count in most_common_locations if loc not in common_non_locations]
    
    return locations[:5]

potential_locations = extract_locations(sample_text)
print(f"识别到的主要地点候选: {potential_locations}")

关键情节摘要:为每个章节生成一个简短的摘要,帮助我们抓住故事脉络。这里我们可以用一个非常简单的“抽取式摘要”方法:找出章节中最重要的句子(通常通过词频或TF-IDF判断)。

from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np

def generate_summary(text, num_sentences=2):
    """
    生成抽取式摘要(非常基础的版本)。
    将文本分成句子,用TF-IDF找出最具代表性的句子。
    """
    # 简单分句(按句号、问号、感叹号分割)
    sentences = re.split(r‘[。!?]‘, text)
    sentences = [s.strip() for s in sentences if len(s.strip()) > 5]  # 过滤掉过短的句子
    
    if len(sentences) <= num_sentences:
        return ‘。‘.join(sentences) + ‘。‘
    
    # 计算TF-IDF
    vectorizer = TfidfVectorizer(tokenizer=jieba.cut, stop_words=[‘的‘, ‘了‘, ‘在‘, ‘是‘, ‘我‘])
    try:
        tfidf_matrix = vectorizer.fit_transform(sentences)
    except ValueError:  # 可能所有句子都被过滤掉了
        return text[:100] + ‘...‘
    
    # 计算每个句子的重要性得分(TF-IDF向量之和)
    sentence_scores = np.array(tfidf_matrix.sum(axis=1)).flatten()
    
    # 选取得分最高的句子
    top_sentence_indices = sentence_scores.argsort()[-num_sentences:][::-1]
    top_sentence_indices.sort()  # 按原文顺序排列
    summary = ‘。‘.join([sentences[i] for i in top_sentence_indices]) + ‘。‘
    
    return summary

# 为第一章生成摘要
if chapters:
    first_chapter_summary = generate_summary(chapters[0][‘content‘])
    print(f"第一章情节摘要: {first_chapter_summary}")

2.3 第三步:结构化整理与输出

信息提取出来后,我们需要把它们整理成一种清晰、易读的格式,方便后续手动筛选或直接提供给AI。JSON格式是个不错的选择。

import json

def create_story_material(chapters, overall_characters, overall_locations):
    """
    将解析出的信息结构化为一个字典。
    """
    story_material = {
        "metadata": {
            "total_chapters": len(chapters),
            "extracted_time": "2023-10-27"  # 可以替换为动态时间
        },
        "main_characters": overall_characters,
        "main_locations": overall_locations,
        "chapter_details": []
    }
    
    # 为每个章节添加详细信息(这里只处理前几个章节作为示例)
    for i, chap in enumerate(chapters[:3]):  # 限制前3章避免太长
        chapter_characters = extract_characters(chap[‘content‘], top_k=3)
        chapter_locations = extract_locations(chap[‘content‘], top_k=3)
        chapter_summary = generate_summary(chap[‘content‘])
        
        story_material["chapter_details"].append({
            "chapter_index": i + 1,
            "chapter_title": chap[‘title‘],
            "characters_involved": chapter_characters,
            "locations_involved": chapter_locations,
            "plot_summary": chapter_summary
        })
    
    return story_material

# 整合信息(这里用整部小说文本进行整体人物地点提取,用分章内容做详情)
overall_chars = extract_characters(cleaned_text[:20000], top_k=8)  # 用前2万字分析整体人物
overall_locs = extract_locations(cleaned_text[:20000], top_k=8)    # 用前2万字分析整体地点

material = create_story_material(chapters, overall_chars, overall_locs)

# 将结构化数据保存为JSON文件
with open(‘story_material.json‘, ‘w‘, encoding=‘utf-8‘) as f:
    json.dump(material, f, ensure_ascii=False, indent=2)

print("小说素材已成功提取并保存至 ‘story_material.json‘ 文件。")
print("="*50)
print("解析结果预览:")
print(f"主要人物: {material[‘main_characters‘]}")
print(f"主要地点: {material[‘main_locations‘]}")
if material[‘chapter_details‘]:
    first_chap = material[‘chapter_details‘][0]
    print(f"\n第一章《{first_chap[‘chapter_title‘]}》概览:")
    print(f"  涉及人物: {first_chap[‘characters_involved‘]}")
    print(f"  涉及地点: {first_chap[‘locations_involved‘]}")
    print(f"  情节摘要: {first_chap[‘plot_summary‘]}")

运行完上面的代码,你就会得到一个 story_material.json 文件,里面整齐地存放着从小说中提取出来的“食材”。

3. 连接AI模型:从素材到创作

素材准备好了,下一步就是“烹饪”——用水墨江南这样的AI模型进行再创作。这里的关键在于“提示词工程”。我们不能直接把JSON文件扔给AI,而是要把结构化的信息,转化成模型能理解的、富有画面感和故事性的语言描述。

假设我们使用一个支持中文、擅长极简中式风格的文生图或故事生成模型(例如,通过其API或Web界面)。我们的任务就是构建优质的提示词。

基础提示词模板:

请基于以下故事要素,创作一幅极简中式风格的图像/一个简短故事片段:

**核心人物**:[这里填入人物名,例如:李逍遥]
**人物特征**:[从原文推断或总结,例如:少年侠客,机灵聪慧,背负长剑]
**核心场景**:[这里填入地点名,例如:仙灵岛湖畔]
**场景氛围**:[描述氛围,例如:雾气缭绕,荷花盛开,静谧神秘]
**情节启发**:[基于章节摘要,例如:初次登岛,寻找灵药,心中既有期待又怀警惕]
**风格要求**:水墨画风格,留白艺术,色彩淡雅,强调意境而非写实。

具体操作示例:

  1. 读取素材:从我们生成的JSON文件中加载数据。
  2. 组合提示词:编写一个函数,随机或指定地选取人物、地点和情节,填充到上述模板中。
  3. 调用AI模型:将生成的提示词发送给AI模型的API。这里以伪代码示意,因为不同模型的调用方式各异。
import random

def generate_prompt_for_ai(story_material, chapter_index=0):
    """
    根据解析出的小说素材,生成用于AI创作的提示词。
    """
    details = story_material[‘chapter_details‘][chapter_index]
    
    # 从该章节涉及的元素中选取(如果列表为空,则从全局选取)
    character = random.choice(details[‘characters_involved‘] or story_material[‘main_characters‘])
    location = random.choice(details[‘locations_involved‘] or story_material[‘main_locations‘])
    
    # 这里可以更智能地匹配人物特征和场景氛围,例如建立一个小型知识库或使用更高级的NLP模型。
    # 为简化,我们使用固定描述或根据名称简单推断。
    character_traits = {
        ‘李逍遥‘: ‘少年侠客,机灵聪慧,眼神不羁‘,
        ‘赵灵儿‘: ‘少女,清丽脱俗,身世神秘,善良温柔‘,
        # ... 可以预先定义或通过其他方式推断
    }
    location_atmosphere = {
        ‘仙灵岛‘: ‘雾气缭绕,灵气充沛,宛如世外桃源‘,
        ‘余杭镇‘: ‘江南水乡,热闹市井,客栈酒旗飘扬‘,
        # ...
    }
    
    trait = character_traits.get(character, ‘一位故事人物‘)
    atmosphere = location_atmosphere.get(location, ‘一个故事中的地点‘)
    
    prompt_template = f“““
请基于以下故事要素,创作一幅极简中式风格的图像:

**核心人物**:{character}
**人物特征**:{trait}
**核心场景**:{location}
**场景氛围**:{atmosphere}
**情节启发**:{details[‘plot_summary‘]}
**风格要求**:水墨画风格,留白艺术,色彩淡雅(以墨色、赭石、花青为主),强调意境与氛围,人物勾勒写意。
    “““
    return prompt_template

# 生成提示词
ai_prompt = generate_prompt_for_ai(material, chapter_index=0)
print("生成的AI提示词:")
print(ai_prompt)
print("\n" + "="*50)
print("你可以将此提示词复制到水墨江南等AI绘画或故事生成模型中,引导其创作。")

通过这种方式,你就能将千变万化的小说内容,转化为成百上千个不同的、高质量的AI创作指令。你可以为同一个人物在不同场景下生成画像,也可以基于某个关键情节生成一系列故事漫画。

4. 实践建议与扩展方向

上面介绍的解析器是一个基础版本,它能跑通流程,提取出一些有用的信息。但如果你想让它更强大、更精准,可以考虑下面几个扩展方向:

  • 提升识别准确率:目前基于词性标注的方法误判率不低。可以尝试:
    • 构建自定义词典:将小说中特有的人物名、地名提前加入 jieba 词典。
    • 使用预训练模型:利用像 LTPHanLPspaCy(配合中文模型)这类更强大的NLP工具包,它们有更准确的分词、命名实体识别功能。
    • 规则后处理:比如,连续出现的两个单字人名很可能是一个完整人名(如“李 逍遥”应合并)。
  • 挖掘人物关系:这是让“故事新编”更有深度的关键。可以分析人物共现的句子,或者使用关系抽取模型,来构建简单的人物关系图(如:李逍遥-爱慕->赵灵儿,李逍遥-师徒->酒剑仙)。
  • 情感分析与情节曲线:对每个章节进行情感分析(积极/消极),可以绘制出故事的情感起伏曲线。你可以选择情感最强烈的章节(如高潮、转折点)作为AI创作的重点题材。
  • 构建可视化素材库:将提取出的所有人物、地点、情节摘要做成一个简单的网页应用,用图表和关系网可视化展示,点击某个元素就能一键生成对应的AI创作提示词。
  • 全自动化管道:将整个流程脚本化,从监控小说更新、自动抓取新章节、解析、到定时提交任务给AI模型生成作品,实现“小说追更-AI同人创作”的自动化流水线。

5. 总结

把Python脚本和AI模型结合起来,自己动手打造一个从小说到创意作品的转换器,这个过程本身就充满了乐趣。我们做的这个解析器,虽然简单,但它成功地在浩如烟海的文字和AI的生成能力之间,架起了一座结构化的桥梁。

它不再需要你手动去总结人物和情节,而是自动地、批量地完成这些基础工作,让你能把更多精力放在创意引导上——比如,如何设计更巧妙的提示词,如何将不同小说的元素进行“跨界”融合。当你看到水墨江南模型根据你提供的“李逍遥在锁妖塔”的素材,生成出一幅你从未想象过的、充满悲壮与孤独感的极简水墨画时,那种成就感是非常独特的。

这个项目就像一个起点,你可以用它来解构任何你喜欢的文本,然后用不同的AI模型去尝试再创作。技术在这里不是目的,而是帮你释放创意、探索故事无限可能性的工具。希望这个分享能给你带来一些灵感,不妨找一本你喜欢的小说,从运行第一行预处理代码开始,试试看吧。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

更多推荐