用 Scrapling 抓新闻文章:从列表页到正文提取的实战教程
想用 Python 抓新闻文章,却总是抓到一堆导航、广告和相关推荐?这篇 Scrapling 新闻抓取实战教程,带你从列表页拿链接,到详情页提取标题、时间、作者和正文,再到动态页面与反爬处理,一步步写出真正能用的文章采集脚本。

用 Scrapling 抓新闻文章:别再把广告和“相关推荐”当正文了 📰
抓新闻文章这件事,看起来很简单。
你以为流程是:
打开网页 → 抓正文 → 下班。
现实流程通常是:
打开网页 → 抓到导航栏 → 抓到广告位 → 抓到“相关推荐” → 抓到作者头像旁边的迷之文本 → 怀疑人生。
所以这篇文章不讲虚的。
我们就专门聊一件事:
怎么用 Scrapling 抓新闻文章,并尽量把真正的正文抓干净。

如果你正在找:
- 新闻抓取教程
- 文章正文提取方法
- Python 爬虫抓新闻页
- Scrapling 实战案例
那这篇就是为你准备的。
为什么新闻站比普通页面更容易把人气笑?
因为新闻页最烦的地方,不是“没有内容”,而是“内容太多,而且很多不是你想要的”。
常见噪音包括:
- 顶部导航
- 热门推荐
- 广告位
- 分享按钮
- 图片说明
- 责任编辑信息
- 上一篇 / 下一篇
- 自动插入的关联阅读
于是很多人第一次抓新闻页时,会得到一个看起来像正文、其实像拼盘的结果。
所以真正正确的思路是:
- 先抓文章详情页链接
- 再进入详情页
- 单独提取标题、时间、作者、正文
- 尽量把推荐阅读和广告排除掉
换句话说:
新闻抓取不是“抓页面”,而是“拆页面”。
先把目标字段定清楚
别一上来就“先抓抓看”。
那种写法最后通常会变成:
final_final_real_final_data_v2.json
建议一开始就定好结构:
{
"url": "",
"title": "",
"published_at": "",
"author": "",
"summary": "",
"content": "",
}
这样后面无论你要:
- 导出 JSON
- 存数据库
- 做向量检索
- 喂给 AI
都比较舒服。
第一步:先别批量,先把一篇文章抓通
这是很多新手最容易跳过的一步。
结果就是:
- 批量链接抓到了 300 条
- 每条正文都是空的
- 你又得回头重写
我们先看单篇文章。
from scrapling.fetchers import Fetcher
url = "https://example.com/news/123"
page = Fetcher.fetch(url)
print(page.status)
print(page.title)
print(page.css("h1::text").get())
这一步只做三件事:
- 看页面能不能正常请求
- 看标题能不能拿到
- 看这个站是不是静态页面
先把这三件事搞清楚,后面就好办很多。
第二步:提取标题、时间、作者、正文
新闻详情页通常会有这些部分:
h1标题time标签或时间 classauthor/sourcearticle/.content/.article-body这种正文容器
一个基础版本可以这样写:
from scrapling.fetchers import Fetcher
url = "https://example.com/news/123"
page = Fetcher.fetch(url)
data = {
"url": url,
"title": page.css("h1::text").get(""),
"published_at": page.css("time::text").get(""),
"author": page.css(".author::text").get(""),
"summary": page.css("meta[name='description']::attr(content)").get(""),
"content": "\n".join(page.css("article p::text").getall()),
}
print(data)
注意这里的重点不是标题,而是正文:
正文最好按段落提取,而不是整块直接拿。
也就是:
"\\n".join(page.css("article p::text").getall())
这个思路很重要,因为新闻站正文区旁边,经常藏着很多“假正文”。
为什么新闻正文推荐抓 p::text?
因为你真正想要的是:
- 主体段落
而不是:
- 图注
- 广告
- 相关推荐
- 页脚废话
如果你直接取整个 .article-body 的纯文本,常常会把一堆杂质也拖回来。
反而按段落抓,通常更稳。
常见正文候选选择器包括:
article p::text.article-content p::text.article-body p::text.news-content p::text.post-content p::text.entry-content p::text
这几个你记住了,已经能应付很多站。
一个更实用的文章提取函数
真实世界里,不同新闻站结构差异很大。
所以不要把希望压在一个选择器上。
你更应该准备一组 fallback。
from scrapling.fetchers import Fetcher
def extract_news_article(url: str) -> dict:
page = Fetcher.fetch(url)
title = (
page.css("h1::text").get("")
or page.css(".article-title::text").get("")
or page.css(".post-title::text").get("")
)
published_at = (
page.css("time::text").get("")
or page.css("time::attr(datetime)").get("")
or page.css("[class*='date']::text").get("")
or page.css("[class*='time']::text").get("")
)
author = (
page.css(".author::text").get("")
or page.css("[rel='author']::text").get("")
or page.css(".byline::text").get("")
or page.css("[class*='author']::text").get("")
)
summary = page.css("meta[name='description']::attr(content)").get("")
paragraphs = (
page.css("article p::text").getall()
or page.css(".article-content p::text").getall()
or page.css(".post-content p::text").getall()
or page.css(".entry-content p::text").getall()
)
content = "\n".join(text.strip() for text in paragraphs if text.strip())
return {
"url": url,
"title": title.strip(),
"published_at": published_at.strip(),
"author": author.strip(),
"summary": summary.strip(),
"content": content,
}
这段代码最值钱的地方,不是语法,而是思路:
新闻文章提取要接受“结构不稳定”这个现实。
你不是在抓一个页面模板,你是在和很多风格各异的网站打交道。
第三步:从列表页拿详情链接
一篇文章抓通之后,再去做批量。
列表页一般只负责一件事:
把文章链接交出来。
from scrapling.fetchers import Fetcher
list_url = "https://example.com/news"
page = Fetcher.fetch(list_url)
links = page.css(".news-list a::attr(href)").getall()
print(links)
如果你发现抓到很多乱七八糟的链接,就缩小范围。
比如:
links = page.css(".headline a::attr(href)").getall()
或者:
links = page.css("article h2 a::attr(href)").getall()
不要贪心抓所有 a 标签。
那和用渔网捞牙签差不多。🪥
相对链接别忘了处理
很多新闻站给你的链接长这样:
/news/123.html
这不是完整 URL,要补全:
from urllib.parse import urljoin
links = [urljoin(list_url, link) for link in raw_links]
这一步特别常见,也特别容易忘。
忘了之后你会得到一堆:
“为什么请求 /news/123.html 会报错?”
答案是:因为那不是互联网地址,那只是它家门牌号。😅
第四步:把列表页和详情页串起来
到了这里,你已经有了完整的采集链路:
- 列表页提链接
- 进入详情页
- 提正文
下面是一个够用的实战模板:
from urllib.parse import urljoin
from scrapling.fetchers import Fetcher
def extract_news_article(url: str) -> dict:
page = Fetcher.fetch(url)
paragraphs = page.css("article p::text").getall()
content = "\n".join(text.strip() for text in paragraphs if text.strip())
return {
"url": url,
"title": page.css("h1::text").get("").strip(),
"published_at": page.css("time::text").get("").strip(),
"author": page.css(".author::text").get("").strip(),
"summary": page.css("meta[name='description']::attr(content)").get("").strip(),
"content": content,
}
list_url = "https://example.com/news"
page = Fetcher.fetch(list_url)
raw_links = page.css(".news-list a::attr(href)").getall()
links = [urljoin(list_url, link) for link in raw_links]
results = []
for url in links:
try:
item = extract_news_article(url)
if item["title"] and item["content"]:
results.append(item)
except Exception as e:
print("抓取失败:", url, e)
print("总数:", len(results))
这时你已经不是“试试看”了。
这已经是一个真正可用的 新闻抓取脚本 雏形。
如果正文抓不到,多半不是你笨,是页面在演
很多新闻站现在都会用前端渲染。
表现通常是:
- 页面打开有内容
Fetcher.fetch()拿到的 HTML 却很空
这时候就要换 DynamicFetcher:
from scrapling.fetchers import DynamicFetcher
def extract_dynamic_news(url: str) -> dict:
page = DynamicFetcher.fetch(
url,
headless=True,
network_idle=True,
)
content = "\n".join(
text.strip()
for text in page.css("article p::text").getall()
if text.strip()
)
return {
"url": url,
"title": page.css("h1::text").get("").strip(),
"published_at": page.css("time::text").get("").strip(),
"author": page.css(".author::text").get("").strip(),
"content": content,
}
network_idle=True 很适合新闻详情页,因为不少正文模块是首屏后再补渲染的。
简单理解就是:
先让页面把戏演完,再去抄台词。
再难一点,就上 StealthyFetcher
如果你已经用了浏览器模式,结果页面还是:
- 访问异常
- 风控拦截
- 加载不完整
那就试试:
from scrapling.fetchers import StealthyFetcher
page = StealthyFetcher.fetch(
"https://example.com/news/123",
headless=True,
network_idle=True,
)
别把它当默认选项,但也别把它当最后一天的传说装备。
更合理的顺序还是:
FetcherDynamicFetcherStealthyFetcher
一步步升级,定位问题更清楚。
CLI 其实也很适合抓文章正文
如果你只是临时想抓一篇文章,不一定非得先写脚本。
Scrapling CLI 有个对新闻提取特别友好的参数:
scrapling extract get "https://example.com/news/123" article.md --ai-targeted
这个模式会尽量:
- 保留主体内容
- 去掉很多噪音标签
- 让输出更适合阅读和二次处理
如果是动态新闻页:
scrapling extract fetch "https://example.com/news/123" article.md --ai-targeted
如果再难一点:
scrapling extract stealthy-fetch "https://example.com/news/123" article.md --ai-targeted
这非常适合前期试站。
你先确认“能不能干净抓下来”,再决定值不值得写完整程序。

一个更稳的正文选择器策略
新闻站的 class 命名,通常没有什么仁慈可言。
所以推荐你准备一个候选列表:
CONTENT_SELECTORS = [
"article p::text",
".article-content p::text",
".article-body p::text",
".news-content p::text",
".post-content p::text",
".entry-content p::text",
".content p::text",
]
然后按顺序试:
def extract_content(page):
for selector in CONTENT_SELECTORS:
texts = page.css(selector).getall()
texts = [t.strip() for t in texts if t.strip()]
if texts:
return "\n".join(texts)
return ""
这是一个很小的技巧,但实战价值很高。
因为现实世界不讲究“优雅统一”,它讲究“今天这个站点 class 名怎么又改了”。
常见坑,提前帮你踩一下
坑 1:抓到的是列表摘要,不是正文
原因:
- 你一直停留在列表页
解决:
- 一定要进入详情页再提正文
坑 2:正文里混入推荐阅读
原因:
- 正文容器范围太大
解决:
- 优先抓
p::text - 缩小正文容器范围
坑 3:发布时间为空
原因:
- 有的站把时间放在
datetime属性里
解决:
- 同时尝试
time::text和time::attr(datetime)
坑 4:抓不到内容
原因:
- 页面是 JS 渲染
解决:
- 改用
DynamicFetcher
坑 5:抓到的不是文章,而是验证页
原因:
- 反爬触发了
解决:
- 改用
StealthyFetcher - 检查返回 HTML 是否已经变成风控页
如果文章量变大,建议升级成 Spider
当你开始:
- 连续翻很多新闻列表页
- 抓几百篇文章
- 需要暂停与恢复
那就别继续把所有逻辑塞在一个 for 循环里了。
这时候可以升级成 Scrapling 的 Spider。

简单理解:
- 列表页负责产出详情链接
- 详情页负责产出文章数据
- Spider 负责调度和并发
如果你只是抓几十篇,普通脚本就够。
如果你准备长期跑站点,Spider 会让你后面轻松很多。
最后总结:新闻抓取的核心不是“抓”,而是“净”
用 Scrapling 抓新闻文章,最关键的不是“我会发请求”。
真正的关键是:
我能不能把正文抓干净。
所以一个靠谱的顺序应该是:
- 先抓通单篇详情页
- 再把标题、时间、作者、正文提稳
- 再去做列表页批量抓取
- 抓不到时再升级到动态抓取和 Stealth
别反过来。
不然你会很快得到一个高并发、很努力、但抓回来的全是广告的系统。🙂
这就不叫新闻采集,这叫互联网垃圾搬运。
读者反馈
你希望这篇文章补充什么?
可以留下你想看的功能、改进建议或后续教程方向。我会定期整理。
最新建议
还没有建议,你可以抢个沙发。
为了去重统计,只保存 IP、浏览器信息和本机指纹的哈希,不保存明文 IP。