简介面向虚假新闻检测研究与社交媒体内容分析的中文多模态数据集包可供自然语言处理、多模态学习及新闻可信度分析方向的初学者和研究者使用。资源包含数据采集脚本、介绍文档与图片资源其中两个csv文件提供文本-图像联合标注数据可直接用于训练分类模型Python脚本实现新闻内容采集与预处理并可复现数据构建流程便于扩展自有数据集。压缩包共5个文件涵盖csv、py、md、jpg等类型整体约654KB体量轻巧、内容紧凑无需大型环境即可快速下载与二次处理。目前已有75人学习下载尤其适合需要低成本起步的虚假新闻检测项目。借助说明文档与脚本注释使用者能快速理解数据组织方式并基于图像与文本联合信息开展多模态分类实验也可参考爬虫逻辑迁移至其他社交媒体平台提升数据敏感度与模型实用价值。 做虚假新闻检测的同学最难受的往往不是模型选型而是训练数据。公开的纯文本假新闻集不少可落到社交媒体场景一条虚假消息通常是“正文 配图 传播渠道”一起出现文本模型看不到图片里的烟幕弹图片模型又抓不住文本里的时间矛盾。这份面向虚假新闻检测的社交媒体多模态数据集把 NewsCrawler 采集脚本、Datasets 里的 CSV 标注数据和 images 图片目录打成了一个包解压后既能拿现成 CSV 直接训练也能改参数自己扩展采集。适合正在跑基线实验、做毕设、以及想复现多模态检测完整流程的从业者。我的第一判断是它的价值不在“数据量大”而在“图文成对 可再采集”。2. 压缩包目录与字段语义先用几分钟把文件结构对齐2.1 解压后的目录划分拿到这个 zip我习惯第一步不是看代码而是把目录树拉出来看一遍。资源包的目录分层是采集、缓存、训练三件事的物理隔离目录分得越清楚后面写训练脚本时就越少踩路径坑。目录/文件主要内容格式在管线里的角色NewsCrawler/news_crawler.py 采集脚本.py扩展采集、增量更新Datasets/标注好的 CSV 数据集.csv训练/验证主数据data/原始抓取中间结果.jsonl、日志排错、回溯数据来源images/与 CSV 关联的新闻配图jpg/png 为主多模态里的图像模态介绍.md数据说明与字段约定.md动手前先读它把data/和Datasets/分开是我觉得这份资源做得比较顺手的地方。Datasets/下放的是经过清洗、标注、可以直接读进内存的 CSVdata/下放的是爬虫落盘的原始 jsonl没做过字段规整。这样做的意义在于如果 CSV 里某一行数据可疑你能回到data/对应的原始记录里查采集现场而不是对着一个已经被处理过、丢了上下文的表格发呆。images/目录的命名也和 CSV 里的相对路径是对应的常见做法是按images/train/、images/val/划分或者直接按样本 id 建子目录。判断一份多模态数据是否合格我通常只看一条给定 CSV 里的image_path字段能不能不经过任何人工翻译直接用os.path.join(BASE_DIR, image_path)找到文件。这份资源的目录结构是朝这个方向组织的。2.2 CSV 字段的一般约定CSV 是整个数据集的核心。文本字段、图像路径、类别标签三个列必须齐全这是多模态训练的最低要求。以我从压缩包里读到的字段风格来说一般会包含以下几类信息id样本唯一编号同时用于关联 images 目录里的文件名text帖子正文或新闻文本是语言模型那一路的输入image_path相对路径指向 images 目录下的图片文件label类别标签常见取值是real/fake或 0/1 二值publish_time发布时间可以用来做时间维度的特征比如“发布后 1 小时内转发量异常”source来源标识用于区分不同采集频道也是事后分层抽样的依据要注意的是 CSV 里文本字段可能夹带换行符。很多表格处理工具遇到带换行的单元格会直接把列挤歪训练脚本读进去后字段对不上报错又不好查。后面第 5 章我会专门讲这个坑这里先说结论pandas 读取时用enginepython或quoting1能规避大部分解析问题。2.3 多模态体现在哪里所谓多模态站在训练角度就是“一条样本同时有文本和图像两个输入”。虚假新闻的典型特征恰恰是跨模态的文字描述一个时间地点配图却是另一个场合的旧图或者正文引导情绪图片截掉了关键的上下文。只看文本或者只看图都可能漏掉这种不一致。在这份数据里文本和图像通过image_path关联到同一行。构造训练样本时的标准动作是读 CSV 拿到text和label再按image_path去 images 目录加载对应图片三者拼成一个样本 dict。加载图片时建议统一转成 RGB避免灰度图和 RGBA 图混在一起影响后续处理。提示不要用label的取值个数判断数据集规模。二分类问题里真实和虚假两类的分布是否均衡比类别数量更能影响模型收敛。3. NewsCrawler 爬虫脚本参数配置、运行流程与首份 CSV 产出3.1 依赖与前置条件news_crawler.py是资源里唯一一个代码文件定位是数据采集入口。它不是模型训练代码而是帮你把“新的社交媒体帖子”拉下来、转成 CSV 的流水线。脚本依赖不多我用下来的经验是三个库就能跑requests、pandas、pillow。pip install requests pandas pillow建议在虚拟环境里装避免把系统 Python 环境搞乱。装完先跑一次python news_crawler.py --help或python news_crawler.py -h看脚本暴露了哪些参数。这一步花不了 30 秒但能避免后面直接python启动时因为缺参数而翻车。python news_crawler.py --help如果脚本没有提供--help解析直接打开news_crawler.py看文件末尾的if __name__ __main__部分那里通常写了默认参数。我一般会关注这几个参数是否存在目标来源、抓取页数、请求间隔、输出路径。3.2 一条采集命令跑通流程参数与默认值以我常用的方式采集命令长这样python news_crawler.py --source soical_news --pages 3 --delay 1.5 --output ./Datasets/crawl_result.csv参数说明--source指定抓取的目标来源频道对应脚本内部的频道配置--pages抓取多少页列表页页码越多数据量越大但反爬风险也越高--delay两次请求之间的间隔秒数1.5 是相对保守的值--outputCSV 落盘路径脚本会把清洗后的数据结构化写到这里--delay是最值得调的参数。设成 0.1 时抓得快但很容易触发对方的风控设成 2 或 3 更稳代价是采集耗时线性增长。我一般先 1.5 起步被 403 或滑块验证就翻倍。3.3 脚本内部的四段式工作流我没法逐行复制压缩包里的源码但这类爬虫脚本的骨架基本是固定的理解四段式结构后改参数和排错都能有的放矢import time import json import requests import pandas as pd def fetch_list_pages(source, pages, delay): 第一段拉列表页拿到帖子链接集合 raw_items [] for page in range(1, pages 1): resp requests.get(source[list_url].format(pagepage), timeout10) # 通常列表页会返回一组帖子 id 或跳转链接 raw_items.extend(parse_list(resp.text)) time.sleep(delay) # 延时是反爬的第一道防线 return raw_items def fetch_detail(item, source, delay): 第二段逐个帖子拉详情页拿到正文和配图链接 detail_resp requests.get(source[detail_url].format(iditem[id]), timeout10) detail parse_detail(detail_resp.text) time.sleep(delay) return detail def build_dataset(rows, image_base): 第三段拼接文本、图像路径、标签统一成 DataFrame df pd.DataFrame(rows) # 生成相对路径统一用正斜杠避免 Windows 下反斜杠污染 CSV df[image_path] df[image_url].apply(lambda u: download_image(u, image_base)) return df def save_csv(df, output_path): 第四段落盘 CSV保证字段顺序稳定 df.to_csv(output_path, indexFalse, encodingutf-8)逻辑说明第一段是列表页解析拿到的是“有哪些帖子”第二段是详情页解析补充正文和配图地址第三段把配图下载到images/目录并生成相对路径第四段统一落盘。抓取过程中任何一条帖子请求失败脚本一般会跳过并记录日志不会整体中断。参数说明timeout10表示单个请求 10 秒没响应就放弃这个值调大调小都有代价——太小容易误判网络抖动太大遇到连接卡死会拖慢整体进度。延时不应该写死在代码里从命令行参数传入是更灵活的做法。3.4 落盘格式、去重与增量更新脚本产出的 CSV 字段要和 Datasets 里已有文件的字段保持一致这样新抓的数据才能直接 append 进去。我在实际使用中经常遇到的问题是重复采集同一个帖子在列表页出现两次或者增量抓取时把上次已经入库的帖子又拉了一遍。常见做法是在脚本里维护一个去重键一般用帖子的唯一id或 URL 的哈希值。增量更新时先读旧 CSV 里的 id 集合新数据里如果 id 已经在集合内就直接跳过。这个逻辑不复杂但在脚本里缺了它数据规模上去后重复率会悄悄涨到 20% 以上模型在训练时反复见到几乎一样的样本等于变相过拟合。4. 把 CSV 喂给模型标签映射、图文对齐与数据加载4.1 CSV 读入与标签映射拿到 CSV 后第一个动作是读进来然后看一眼字段类型和分布。我习惯先做一个最小化的数据体检确认列名和标注取值符合预期再动模型。import os import pandas as pd from collections import Counter train_df pd.read_csv(../Datasets/train.csv, encodingutf-8) print(train_df.columns.tolist()) print(train_df.shape) print(Counter(train_df[label]))逻辑说明read_csv用encodingutf-8明确指定编码Counter用于快速看标签分布判断真实样本和虚假样本的比例是否悬殊。如果输出里某类样本占比低于 10%训练时就要考虑加权或过采样。标签列如果是字符串需要统一映射成整数。常见的取值组合是real/fake也有数据集直接标 0/1。映射逻辑必须放在最前面且要保证训练、验证、测试三份 CSV 共用同一个映射表。label_map {real: 0, fake: 1} train_df[label_id] train_df[label].map(label_map)参数说明map遇到映射表里不存在的值会生成 NaN。所以映射后立刻检查train_df[label_id].isna().sum()如果有缺失说明 csv 里混进了映射表外的取值比如REAL、unknown之类。这种脏数据宁可早发现早过滤别让它在训练时变成隐性噪声。4.2 图像存在性与可解码过滤图文对齐是多模态训练最容易翻车的环节。CSV 里写了image_path但文件不一定真的存在——可能是采集时下载失败也可能是压缩包解压时目录层级变了。训练脚本一旦遇到读不到图的情况轻则跳过样本重则整个 DataLoader 崩掉。我的做法是构造样本前先做一层存在性过滤BASE_DIR ../ # 压缩包根目录 train_df[abs_path] train_df[image_path].apply(lambda p: os.path.join(BASE_DIR, p)) train_df[img_exists] train_df[abs_path].apply(os.path.exists) filtered_df train_df[train_df[img_exists]].copy() print(f过滤前 {len(train_df)} 条过滤后 {len(filtered_df)} 条)逻辑说明先拼出绝对路径再用os.path.exists做存在性判定。过滤掉缺失图片的样本避免在训练循环里再判断。这一步不会删除磁盘上的文件只影响 DataFrame 里的样本集合。要注意的是os.path.exists只能判断路径是否存在文件损坏了它检测不出来。图片文件虽然存在但只有 0 字节或用错误的格式后缀命名加载时照样报错。所以更稳的做法是在过滤阶段就把图片真正打开一次用 Pillow 验证可解码性。from PIL import Image def verify_image(path): try: with Image.open(path) as im: im.load() im.convert(RGB) return True except Exception: return False filtered_df[img_valid] filtered_df[abs_path].apply(verify_image) filtered_df filtered_df[filtered_df[img_valid]].copy()逻辑说明im.load()触发真实解码能过滤空文件和截断文件convert(RGB)统一颜色通道避免灰度图和 RGBA 图在后续进入模型时 shape 不一致。这一步比较耗时但只在数据集准备阶段跑一次属于值得花的成本。4.3 组一个最简单的多模态样本迭代器过滤干净后就可以把文本、图像、标签组装成训练样本。最省事的方式是直接用 PyTorch 的Dataset封装一个轻量类import torch from PIL import Image from torch.utils.data import Dataset class MultiModalDataset(Dataset): def __init__(self, df, image_root, text_transformNone, image_transformNone): self.df df.reset_index(dropTrue) self.image_root image_root self.text_transform text_transform self.image_transform image_transform def __len__(self): return len(self.df) def __getitem__(self, idx): row self.df.iloc[idx] # 文本模态原样返回或走 tokenizer text str(row[text]) # 图像模态按相对路径拼接完整路径再打开 img_path os.path.join(self.image_root, row[image_path]) image Image.open(img_path).convert(RGB) if self.image_transform is not None: image self.image_transform(image) # 标签 label int(row[label_id]) return {text: text, image: image, label: label}逻辑说明__getitem__返回一个字典一条样本同时携带文本、图像和标签。image_root单独传而不是拼接在 CSV 里是为了让同一份 CSV 在不同机器上都能用只要调整根目录即可。image_transform可以是随机裁剪、水平翻转等增强操作也可以只做 Resize 和归一化。参数说明text的预处理不在这个类里做。实际训练时通常先分词再按 batch 做 padding那部分要在 DataLoader 的collate_fn里处理不然长度不齐的文本根本堆不成 batch。建议先在单条样本上跑通再考虑 batch 化。5. 避坑指南爬虫被封、图文错位与类别不平衡的排查记录5.1 请求 403爬一阵就断不是脚本坏了现象爬虫前几十条正常某次请求后所有请求都返回 403或者主页能打开详情页全部超时。原因请求频率超过了目标站点的风控阈值或者请求头里的 User-Agent 特征太明显。采集脚本默认的 UA 和真实浏览器差异很大连续高频请求很容易被识别为机器行为。解决给脚本加随机延时和随机 UA。延时不用固定在 1 到 3 秒之间随机取比固定 1.5 秒更难被捕捉到规律UA 可以从默认的那一串换成常见浏览器的 UA 列表每次请求随机抽一个。如果加了延时还是被限制再把目标站点的请求频率降到每 5 秒一次配合断点续抓避免从头再来。注意不要试图绕过登录鉴权或破解验证码。做数据采集要控制频率这是底线。5.2 图文错位下载失败的图片让整行数据报废现象模型训练时 loss 正常下降抽看验证集样本发现有些样本的配图和文本内容完全对不上比如体育新闻配了一张财经图片。原因爬虫在下载图片时某个请求超时脚本没有把下载状态和 CSV 行回写导致图片缺失或串位后续拼接时按行号顺序对齐一条下载失败后面的图片就整体向前错一位。解决图像路径必须在下载成功之后才写进 CSV且每条样本的图片路径以实际落盘文件为准而不是按列表顺序猜测。更稳的做法是给每个下载文件命名时带上样本 id比如images/000123.jpgCSV 里记000123.jpg这样无论下载顺序怎么变路径和文本永远通过 id 关联。5.3 类别比例失衡训练时 loss 不降反而震荡现象二分类任务里虚假样本只占 5%训练几个 epoch 后准确率停在 95% 上下不去loss 一直在高位震荡模型输出几乎全部预测为真实类。原因模型发现“全部预测为多数类”就能拿到极高准确率梯度被多数类样本主导少数类的特征根本没学到。解决先看类别分布再用分层采样或加权损失。最直接的是让 DataLoader 里的少数类样本重复出现构造平衡 batch另一条路是给 loss 加权重让虚假类的错误预测付出更高代价。两者可以叠加先用WeightedRandomSampler平衡采样再把pos_weight传给二分类损失函数。5.4 图片文件损坏PIL 一拍就报错现象训练跑到中途突然抛OSError: image file is truncated或者UnidentifiedImageError: cannot identify image file整个训练进程被杀掉。原因采集过程中网络中断落盘的图片文件只有部分字节或者某些图片本身是 WebP、GIF 格式却用了.jpg后缀Pillow 按 JPEG 解码自然失败。解决训练前用 Pillow 统一验证所有图片捕获解码异常并剔除。处理截断图可以在打开时加ImageFile.LOAD_TRUNCATED_IMAGES True能救回一部分尾部缺失的图片救不回来的直接过滤不要试图在训练循环里兜底那样会拖慢数据加载且结果不可控。5.5 编码与多行字段CSV 列被挤歪现象pandas 读 CSV 后print(df.head())显示字段错位本该属于text的内容跑到label列里或中文全部变成乱码。原因两个来源。一是 CSV 文件用 GBK 保存read_csv默认按 UTF-8 读中文直接变乱码二是文本字段里包含换行符标准 CSV 解析器遇到换行会认为一行结束后续字段全部错位。解决读取时显式指定encodingutf-8如果乱码再尝试encodinggbk。文本换行的问题用quoting1解决它告诉解析器把带引号的字段整体读入即使里面有换行也不拆分。另加enginepython更稳虽然慢一点但能兼容更多边界情况。6. 用一条校验管线给数据集做体检统计分布、抽样与可视化6.1 一次体检脚本分布、完整性与可解码性数据拿到手最好不要直接开训。我习惯用一段脚本做三件事统计标签分布、核对图片文件状态、抽样打印若干条样本确认图文对应关系。这段脚本可以复用在你后续抓取的任何新数据上。import os import pandas as pd from collections import Counter from PIL import Image df pd.read_csv(../Datasets/train.csv, encodingutf-8) BASE_DIR ../ print(标签分布:, Counter(df[label])) print(总样本数:, len(df)) # 图片存在性检查 df[abs_path] df[image_path].apply(lambda p: os.path.join(BASE_DIR, p)) df[exists] df[abs_path].apply(os.path.exists) # 图片可解码检查抽样前 500 张即可全量太慢 sample df[df[exists]].head(500).copy() def check_img(path): try: with Image.open(path) as im: im.load() return True except Exception: return False sample[valid] sample[abs_path].apply(check_img) print(f存在性通过率: {df[exists].mean():.2%}) print(f可解码通过率: {sample[valid].mean():.2%}) # 按类别抽样打印 for label in df[label].unique(): one df[df[label] label].iloc[0] print( * 20) print(标签:, label) print(标题/正文:, str(one[text])[:120]) print(图片路径:, one[image_path])运行结果里如果存在性通过率低于 95%要先补图或过滤后再训练可解码通过率低则说明采集阶段图片下载质量差需要回看data/里的原始记录。6.2 抽样人工核对与后续扩展体检脚本只能发现文件层面的问题语义层面的对应关系需要人工介入。我会从每个类别里随机抽 20 条样本打开图片、读文本逐条确认图文是否一致。这听起来原始但跑一次能发现不少自动化检查发现不了的问题比如图片虽然是完整的但配图其实是广告图或者文本本身是一篇转发拼凑的内容没有原始信息源。从那以后我每次拿到外部数据集或者自己抓完一批数据都强制走一遍这套体检流程先看分布再查图片最后抽样人眼核对。三步下来半小时换的是训练阶段不再被脏数据反复折磨。如果你也想用这套资源快速验证模型建议先跑通第 4 章的最小迭代器用体检通过的数据跑一个小模型确认 loss 能正常下降再去扩充数据。数据质量问题大多在训练前暴露拖到训练时才暴露定位成本会成倍增加。希望帮到你。本文还有配套的精品资源点击获取