财经新闻分类预测实战:多源爬虫、文本预处理与SVM/随机森林模型落地

发布时间:2026/10/12 9:43:55
财经新闻分类预测实战:多源爬虫、文本预处理与SVM/随机森林模型落地
简介这份资源是面向金融数据分析初学者与文本挖掘实践者的完整项目源码围绕上市公司新闻的抓取、文本分析与分类预测展开帮助读者理解从数据采集到模型落地的全流程。项目从财经、每经网、金融界、中国证券网、证券时报网等平台爬取历史新闻经分词、停用词过滤与财经词典辅助后提取特征再以SVM、随机森林等分类器完成训练与预测。压缩包共21个文件以17个Python源码为主覆盖爬虫、文本处理、特征提取、模型训练与评估等模块另含财经词典、中文停用词等3个文本文件及1个许可证文件整体约180KB结构清晰便于按模块阅读。目前已有350人学习下载。读者可据此获得一套可运行的财经新闻分类方案掌握爬虫编写、文本预处理与机器学习建模的衔接思路并参考其模块划分方式扩展自己的分析流程。1. 从一份 21 文件的源码包说起财经新闻分类预测到底怎么落地手里这份upload.zip解压后是 21 个文件17 个 Python 源码、2 个词典文本、1 个 LICENSE、1 个 readme。第一次看到目录结构时我愣了一下——它把爬虫和文本分析拆成了两个完全独立的包Crawler/下按来源分文件crawler_cnstock.py、crawler_nbd.py、crawler_stcn.py、crawler_jrj.py、crawler_sina.py、crawler_tushare.py各管一个站点外层再用run_crawler_*.py做入口Text_Analysis/下则是text_processing.py和text_mining.py两个核心模块配一个run_main.py串流程。这种「一源一文件 统一入口」的写法在财经新闻这种来源杂、反爬策略各不相同的场景里比塞进一个大脚本要靠谱得多。它解决的是一个很具体的问题把上市公司相关的历史新闻从多个财经平台抓下来清洗成能喂给模型的文本再用 SVM 和随机森林做分类预测。适合两类人——一类是想跑通「爬虫 → 文本预处理 → 特征提取 → 分类器训练」完整链路的 Python 学习者另一类是手里有财经文本、想找个能改的基线工程的分析岗从业者。下面我按实际拆包和跑通的顺序把这份源码的用法、参数和坑一条条讲清楚。2. 爬虫层拆解六个来源脚本怎么分工、入口怎么调2.1 为什么按来源拆文件而不是写一个通用爬虫财经新闻站点之间的差异比想象中大。中国证券网、证券时报网这类偏公告和快讯列表页结构相对规整金融界、新浪财经的频道多、翻页参数不统一每经网nbd的正文里常带图片说明和编辑署名直接抽文本会混入噪声。把每个来源单独放一个crawler_*.py好处是改一个站点的解析规则不会波及其他站点坏处是公共逻辑请求头、重试、编码处理容易重复。这份源码选择了前者公共部分靠各文件内部自己处理对新手来说反而更好读——你打开crawler_stcn.py就只看证券时报网这一件事。run_crawler_*.py是外层入口命名和crawler_*.py一一对应。这种「入口脚本 实现模块」的分层常见做法是入口里只做参数解析和调用实现模块里放具体抓取逻辑。我一般会先看入口脚本确认它接收哪些参数再进实现模块看解析细节。2.2 跑通一个爬虫入口的最小步骤以证券时报网为例先确认依赖再执行入口。下面是我本地跑通时的命令序列# 建议用虚拟环境避免污染系统 Python python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 常见依赖requests 负责请求bs4/lxml 负责解析 pip install requests beautifulsoup4 lxml pandas# 进入项目根目录后执行单个来源的入口 python run_crawler_stcn.py# run_crawler_stcn.py 里通常是这样组织的示意按实际文件为准 from Crawler.crawler_stcn import StcnCrawler if __name__ __main__: crawler StcnCrawler() # 起始页码和结束页码控制抓取范围 crawler.run(start_page1, end_page5)逻辑说明入口脚本本身几乎不写业务只负责实例化爬虫类并传入页码范围。参数说明start_page和end_page决定抓多少列表页第一次跑一定只开 1 到 2 页确认字段抽对了再放大。crawler_stcn.py内部一般会维护一个base_url和翻页模板改站点就改这里。2.3 抓取结果落到哪里、字段长什么样爬虫跑完通常落成 CSV 或直接进 DataFrame字段至少要有标题、正文、发布时间、来源、股票代码或公司名。这份源码没有用数据库落文件的方式对复现最友好。如果你要接自己的流程建议统一成下面这种列结构后面文本分析模块才好接字段含义注意title新闻标题去空白标题里常含公司简称content正文去掉脚本、样式、编辑署名publish_time发布时间统一成YYYY-MM-DDsource来源站点用脚本名区分即可stock_code关联股票代码抓不到就留空别硬编提示不同来源的时间格式差异很大有的带「来源xx 作者xx」尾巴落盘前统一清洗否则后面按时间切训练集会翻车。3. 文本分析层分词、停用词、财经词典与特征提取3.1 两个词典文件是整个流程的地基Chinese_Stop_Words.txt是通用中文停用词表finance_dict.txt是财经领域词表。分词工具常见是 jieba默认词典对「涨停」「市盈率」「定增」「商誉减值」这类词的切分不一定准把finance_dict.txt加载进去能显著改善术语完整性。停用词表则负责把「的」「是」「以及」这类无统计意义的词滤掉降低特征维度。import jieba # 加载财经自定义词典保证专业术语不被切碎 jieba.load_userdict(finance_dict.txt) # 加载停用词集合 with open(Chinese_Stop_Words.txt, encodingutf-8) as f: stop_words set(line.strip() for line in f if line.strip()) def cut_text(text): # 精确模式分词过滤停用词和单字 words jieba.lcut(text) return [w for w in words if w not in stop_words and len(w) 1]逻辑说明load_userdict要在第一次分词前调用否则不生效。参数说明len(w) 1过滤单字是常见做法但会误伤「涨」「跌」这类有信号的单字如果你的分类标签和涨跌强相关这一条要放宽。停用词表建议自己再补几条财经场景的高频噪声词比如「记者」「报道」「本报讯」。3.2 text_processing 与 text_mining 的职责边界text_processing.py管清洗和分词text_mining.py管特征和模型。这个边界要守住预处理阶段只做文本到词序列的转换不要在这里做任何和标签相关的操作否则训练集和测试集会串。run_main.py是总调度一般按「读数据 → 预处理 → 特征提取 → 划分数据集 → 训练 → 评估」的顺序串起来。# run_main.py 的典型骨架按实际文件调整 from Text_Analysis import text_processing as tp from Text_Analysis import text_mining as tm # 1. 读取爬虫落盘的新闻数据 df tm.load_news(news.csv) # 2. 文本预处理分词 去停用词 df[tokens] df[content].apply(tp.cut_text) # 3. 特征提取TF-IDF 把词序列转成数值矩阵 X, vectorizer tm.build_tfidf(df[tokens]) # 4. 取标签列划分训练测试集 y df[label] X_train, X_test, y_train, y_test tm.split_data(X, y) # 5. 训练并评估 tm.train_and_eval(X_train, X_test, y_train, y_test)逻辑说明build_tfidf内部一般用TfidfVectorizer注意它接收的是分词后的词列表不是原始字符串。参数说明max_features控制特征维度财经新闻语料几万条时设 5000 到 10000 比较稳min_df设 2 到 3 能滤掉只出现一两次的噪声词。划分比例常见 8:2random_state固定住否则每次结果都不一样调参时没法对比。3.3 TF-IDF 与词袋模型在这类任务上的取舍词袋模型只看词频TF-IDF 会压低高频通用词的权重、抬高区分度高的词。财经新闻分类里「涨停」「重组」「减持」这类词的出现本身就带强信号TF-IDF 通常比纯词袋表现好。但要注意TF-IDF 的 IDF 是在训练集上统计的必须用训练集 fit、测试集只 transform否则就是数据泄漏评估分数会虚高。这份源码如果没显式处理这一点你接手时第一件事就是检查fit和transform的调用位置。4. 分类器训练SVM 与随机森林的参数怎么设、结果怎么看4.1 两种模型在这份工程里的定位SVM 在高维稀疏的文本特征上表现稳定尤其是线性核训练快、对小样本友好适合先跑出一个基线。随机森林是集成方法对特征缩放不敏感、能给出特征重要性适合在 SVM 基线之上做对比和解释。这份源码同时放了两种意图很明显让你有个可对比的参照而不是只信一个模型的输出。from sklearn.svm import LinearSVC from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report # 线性 SVM文本分类的稳妥基线 svm LinearSVC(C1.0, max_iter2000) svm.fit(X_train, y_train) print(SVM:) print(classification_report(y_test, svm.predict(X_test))) # 随机森林对比 看特征重要性 rf RandomForestClassifier( n_estimators200, # 树的数量越多越稳但越慢 max_depthNone, # 不限制深度靠 min_samples 控制过拟合 min_samples_leaf2, # 叶子最少样本防止噪声被单独成叶 random_state42, n_jobs-1 # 用满 CPU 核 ) rf.fit(X_train, y_train) print(RandomForest:) print(classification_report(y_test, rf.predict(X_test)))逻辑说明LinearSVC的C是正则强度越大越拟合训练集文本任务上 0.5 到 2 之间调。参数说明随机森林的n_estimators从 100 起200 到 500 通常够用min_samples_leaf是控制过拟合最有效的旋钮设 1 容易记住噪声设 2 到 5 更稳。classification_report要看每一类的 precision 和 recall不要只看 accuracy——类别不平衡时 accuracy 会骗人。4.2 评估指标怎么读才不被误导财经新闻的类别分布往往不均比如「利好」类远多于「风险」类。这时候 accuracy 高不代表模型有用。重点看少数类的 recall如果风险类新闻 recall 只有 0.3意味着七成风险新闻被漏掉这种模型上线就是灾难。常见做法是看 macro-F1 和加权 F1 的差距差距大说明模型偏向多数类需要调class_weightbalanced或做重采样。指标含义什么时候重点看accuracy整体正确率类别均衡时参考precision预测为该类的有多少是对的误报代价高时recall该类真实样本有多少被找出漏报代价高时macro-F1各类 F1 的算术平均关注少数类表现weighted-F1按样本量加权的 F1看整体但不掩盖少数类注意如果classification_report里某个类 support 只有个位数这个类的指标没有统计意义别拿它下结论。4.3 从训练到预测的完整调用链训练完要能对新新闻做预测这一步常被忽略。text_mining.py里应该保留训练好的 vectorizer 和模型预测时用同一个 vectorizer 对新文本做 transform再喂给模型。顺序错了比如重新 fit 一个 vectorizer结果会完全对不上。# 预测新新闻必须复用训练阶段的 vectorizer def predict_news(text, vectorizer, model): tokens tp.cut_text(text) # 同样的分词逻辑 vec vectorizer.transform([ .join(tokens)]) # 只 transform不 fit return model.predict(vec)[0]逻辑说明transform接收的是空格连接的词串和训练时保持一致。参数说明如果训练时用了ngram_range(1,2)预测时 vectorizer 会自动处理不用手动改。这一步最容易翻车的地方是分词逻辑不一致——训练用 jieba 精确模式预测用了搜索引擎模式特征对不上预测结果就是玄学。5. 避坑与排查跑这份源码最容易翻车的五个地方5.1 爬虫跑一半报编码错误现象抓回来的正文里出现乱码或者UnicodeDecodeError直接中断。原因不同站点返回的编码不统一有的声明 GBK 实际是 UTF-8requests自动识别的encoding经常猜错。解决手动指定response.encoding response.apparent_encoding或者按站点硬编response.encoding utf-8落盘时统一用encodingutf-8-sig写 CSVExcel 打开也不乱码。5.2 分词后特征维度爆炸、训练极慢现象TfidfVectorizer跑完内存飙升训练几十分钟不收敛。原因没设max_features几万条新闻的全量词表能到几十万维。解决max_features8000、min_df3、max_df0.8三件套先压下来再逐步放宽。SVM 用LinearSVC而不是SVC后者在几十万维上基本跑不动。5.3 训练集测试集划分导致分数虚高现象评估 F1 到 0.95换一批新数据掉到 0.5。原因划分前做了全局的 TF-IDF fit或者同一篇新闻的重复版本同时进了训练和测试。解决先划分再 fit vectorizer去重按标题或正文哈希做别按行号去重。5.4 停用词表把关键信号词滤掉了现象模型对「不涨」「未涨停」这类否定表达完全无感。原因停用词表里收了「不」「未」这类否定词或者分词把「不」单独切出来后被过滤。解决财经场景下否定词必须保留检查停用词表里有没有误收更稳的做法是把「不涨」「未重组」这类否定搭配加进finance_dict.txt让它们作为一个整体被切出来。5.5 随机森林特征重要性对不上业务直觉现象排在前面的全是「记者」「报道」这类词。原因正文清洗不干净编辑署名和来源说明混进了文本。解决在text_processing.py里加正则把「记者 xxx」「来源xxx」「编辑xxx」这类模板句先删掉再分词。这一步做完特征重要性通常会立刻变得可解释。6. 进阶用法把分类结果接回时间序列做信号验证跑通分类只是第一步这份源码真正的价值在于它能接后续验证。我一般会做一件事把模型预测出的类别按发布时间排序看某类新闻比如风险类出现后对应股票在随后几天的表现有没有统计上的差异。这一步不需要多复杂的工具pandas 按日期分组、算个简单的均值对比就能看出苗头。import pandas as pd # 假设预测结果里有 publish_time、stock_code、pred_label df[publish_time] pd.to_datetime(df[publish_time]) df df.sort_values(publish_time) # 按股票和日期聚合统计风险类新闻的分布 risk df[df[pred_label] risk] daily_risk risk.groupby([stock_code, df[publish_time].dt.date]).size() # 和全量新闻对比看风险类是否集中在特定时间段 print(daily_risk.describe())逻辑说明这一步的目的是验证分类结果有没有超出「文本分类」本身的信息量。参数说明pred_label的取值要和训练时的标签体系一致别一边训练用数字编码一边这里用字符串。如果发现某类新闻在时间上高度聚集先排查是不是爬虫在某个时间段抓多了而不是急着下结论。验证模型稳定性还有个笨办法但很有效把数据按时间切成前后两段前段训练、后段测试对比 F1 的衰减幅度。衰减超过 0.15 就说明模型对时间不鲁棒多半是特征里混了和时间强相关的噪声词。我吃过这个亏——有次 F1 从 0.88 掉到 0.61查了半天才发现是「今日」「本周」这类词在训练集里恰好和标签相关换时间段就失效了。从那以后我每次做完文本分类都强制走一遍时间切分验证再决定要不要把结果拿去做后续分析。希望这份拆解能帮你少走点弯路。本文还有配套的精品资源点击获取