简介:面向虚假新闻检测研究与社交媒体内容分析的中文多模态数据集包,可供自然语言处理、多模态学习及新闻可信度分析方向的初学者和研究者使用。资源包含数据采集脚本、介绍文档与图片资源,其中两个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/下放的是经过清洗、标注、可以直接读进内存的 CSV;data/下放的是爬虫落盘的原始 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 读取时用engine="python"或quoting=1能规避大部分解析问题。
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 是相对保守的值--output:CSV 落盘路径,脚本会把清洗后的数据结构化写到这里
--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(page=page), timeout=10) # 通常列表页会返回一组帖子 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(id=item["id"]), timeout=10) 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, index=False, encoding="utf-8")逻辑说明:第一段是列表页解析,拿到的是“有哪些帖子”;第二段是详情页解析,补充正文和配图地址;第三段把配图下载到images/目录,并生成相对路径;第四段统一落盘。抓取过程中任何一条帖子请求失败,脚本一般会跳过并记录日志,不会整体中断。
参数说明:timeout=10表示单个请求 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", encoding="utf-8") print(train_df.columns.tolist()) print(train_df.shape) print(Counter(train_df["label"]))逻辑说明:read_csv用encoding="utf-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_transform=None, image_transform=None): self.df = df.reset_index(drop=True) 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.jpg,CSV 里记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 解析器遇到换行会认为一行结束,后续字段全部错位。
解决:读取时显式指定encoding="utf-8",如果乱码再尝试encoding="gbk"。文本换行的问题用quoting=1解决,它告诉解析器把带引号的字段整体读入,即使里面有换行也不拆分。另加engine="python"更稳,虽然慢一点,但能兼容更多边界情况。
6. 用一条校验管线给数据集做体检:统计分布、抽样与可视化
6.1 一次体检脚本:分布、完整性与可解码性
数据拿到手,最好不要直接开训。我习惯用一段脚本做三件事:统计标签分布、核对图片文件状态、抽样打印若干条样本,确认图文对应关系。这段脚本可以复用在你后续抓取的任何新数据上。
import os import pandas as pd from collections import Counter from PIL import Image df = pd.read_csv("../Datasets/train.csv", encoding="utf-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 能正常下降,再去扩充数据。数据质量问题大多在训练前暴露,拖到训练时才暴露,定位成本会成倍增加。希望帮到你。
本文还有配套的精品资源,点击获取