返回博客列表用 Scrapling 抓新闻文章:从列表页到正文提取的实战教程
techLive content

用 Scrapling 抓新闻文章:从列表页到正文提取的实战教程

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

2026年5月20日Scrapling · 新闻抓取 · 文章正文提取 · 网页抓取 · Python 爬虫 · 动态页面 · 内容提取 · 教程
用 Scrapling 抓新闻文章:从列表页到正文提取的实战教程

用 Scrapling 抓新闻文章:别再把广告和“相关推荐”当正文了 📰

抓新闻文章这件事,看起来很简单。

你以为流程是:

打开网页 → 抓正文 → 下班。

现实流程通常是:

打开网页 → 抓到导航栏 → 抓到广告位 → 抓到“相关推荐” → 抓到作者头像旁边的迷之文本 → 怀疑人生。

所以这篇文章不讲虚的。

我们就专门聊一件事:

怎么用 Scrapling 抓新闻文章,并尽量把真正的正文抓干净。

Scrapling news cover

如果你正在找:

  • 新闻抓取教程
  • 文章正文提取方法
  • Python 爬虫抓新闻页
  • Scrapling 实战案例

那这篇就是为你准备的。


为什么新闻站比普通页面更容易把人气笑?

因为新闻页最烦的地方,不是“没有内容”,而是“内容太多,而且很多不是你想要的”。

常见噪音包括:

  • 顶部导航
  • 热门推荐
  • 广告位
  • 分享按钮
  • 图片说明
  • 责任编辑信息
  • 上一篇 / 下一篇
  • 自动插入的关联阅读

于是很多人第一次抓新闻页时,会得到一个看起来像正文、其实像拼盘的结果。

所以真正正确的思路是:

  1. 先抓文章详情页链接
  2. 再进入详情页
  3. 单独提取标题、时间、作者、正文
  4. 尽量把推荐阅读和广告排除掉

换句话说:

新闻抓取不是“抓页面”,而是“拆页面”。


先把目标字段定清楚

别一上来就“先抓抓看”。

那种写法最后通常会变成:

final_final_real_final_data_v2.json

建议一开始就定好结构:

TEXT
{
    "url": "",
    "title": "",
    "published_at": "",
    "author": "",
    "summary": "",
    "content": "",
}

这样后面无论你要:

  • 导出 JSON
  • 存数据库
  • 做向量检索
  • 喂给 AI

都比较舒服。


第一步:先别批量,先把一篇文章抓通

这是很多新手最容易跳过的一步。

结果就是:

  • 批量链接抓到了 300 条
  • 每条正文都是空的
  • 你又得回头重写

我们先看单篇文章。

TEXT
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())

这一步只做三件事:

  1. 看页面能不能正常请求
  2. 看标题能不能拿到
  3. 看这个站是不是静态页面

先把这三件事搞清楚,后面就好办很多。


第二步:提取标题、时间、作者、正文

新闻详情页通常会有这些部分:

  • h1 标题
  • time 标签或时间 class
  • author / source
  • article / .content / .article-body 这种正文容器

一个基础版本可以这样写:

TEXT
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)

注意这里的重点不是标题,而是正文:

正文最好按段落提取,而不是整块直接拿。

也就是:

TEXT
"\\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。

TEXT
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,
    }

这段代码最值钱的地方,不是语法,而是思路:

新闻文章提取要接受“结构不稳定”这个现实。

你不是在抓一个页面模板,你是在和很多风格各异的网站打交道。


第三步:从列表页拿详情链接

一篇文章抓通之后,再去做批量。

列表页一般只负责一件事:

把文章链接交出来。

TEXT
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)

如果你发现抓到很多乱七八糟的链接,就缩小范围。

比如:

TEXT
links = page.css(".headline a::attr(href)").getall()

或者:

TEXT
links = page.css("article h2 a::attr(href)").getall()

不要贪心抓所有 a 标签。

那和用渔网捞牙签差不多。🪥


相对链接别忘了处理

很多新闻站给你的链接长这样:

TEXT
/news/123.html

这不是完整 URL,要补全:

TEXT
from urllib.parse import urljoin

links = [urljoin(list_url, link) for link in raw_links]

这一步特别常见,也特别容易忘。

忘了之后你会得到一堆:

“为什么请求 /news/123.html 会报错?”

答案是:因为那不是互联网地址,那只是它家门牌号。😅


第四步:把列表页和详情页串起来

到了这里,你已经有了完整的采集链路:

  1. 列表页提链接
  2. 进入详情页
  3. 提正文

下面是一个够用的实战模板:

TEXT
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

TEXT
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

如果你已经用了浏览器模式,结果页面还是:

  • 访问异常
  • 风控拦截
  • 加载不完整

那就试试:

TEXT
from scrapling.fetchers import StealthyFetcher

page = StealthyFetcher.fetch(
    "https://example.com/news/123",
    headless=True,
    network_idle=True,
)

别把它当默认选项,但也别把它当最后一天的传说装备。

更合理的顺序还是:

  1. Fetcher
  2. DynamicFetcher
  3. StealthyFetcher

一步步升级,定位问题更清楚。


CLI 其实也很适合抓文章正文

如果你只是临时想抓一篇文章,不一定非得先写脚本。

Scrapling CLI 有个对新闻提取特别友好的参数:

TEXT
scrapling extract get "https://example.com/news/123" article.md --ai-targeted

这个模式会尽量:

  • 保留主体内容
  • 去掉很多噪音标签
  • 让输出更适合阅读和二次处理

如果是动态新闻页:

TEXT
scrapling extract fetch "https://example.com/news/123" article.md --ai-targeted

如果再难一点:

TEXT
scrapling extract stealthy-fetch "https://example.com/news/123" article.md --ai-targeted

这非常适合前期试站。

你先确认“能不能干净抓下来”,再决定值不值得写完整程序。

Scrapling shell


一个更稳的正文选择器策略

新闻站的 class 命名,通常没有什么仁慈可言。

所以推荐你准备一个候选列表:

TEXT
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",
]

然后按顺序试:

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::texttime::attr(datetime)

坑 4:抓不到内容

原因:

  • 页面是 JS 渲染

解决:

  • 改用 DynamicFetcher

坑 5:抓到的不是文章,而是验证页

原因:

  • 反爬触发了

解决:

  • 改用 StealthyFetcher
  • 检查返回 HTML 是否已经变成风控页

如果文章量变大,建议升级成 Spider

当你开始:

  • 连续翻很多新闻列表页
  • 抓几百篇文章
  • 需要暂停与恢复

那就别继续把所有逻辑塞在一个 for 循环里了。

这时候可以升级成 Scrapling 的 Spider

Spider architecture

简单理解:

  • 列表页负责产出详情链接
  • 详情页负责产出文章数据
  • Spider 负责调度和并发

如果你只是抓几十篇,普通脚本就够。

如果你准备长期跑站点,Spider 会让你后面轻松很多。


最后总结:新闻抓取的核心不是“抓”,而是“净”

用 Scrapling 抓新闻文章,最关键的不是“我会发请求”。

真正的关键是:

我能不能把正文抓干净。

所以一个靠谱的顺序应该是:

  1. 先抓通单篇详情页
  2. 再把标题、时间、作者、正文提稳
  3. 再去做列表页批量抓取
  4. 抓不到时再升级到动态抓取和 Stealth

别反过来。

不然你会很快得到一个高并发、很努力、但抓回来的全是广告的系统。🙂

这就不叫新闻采集,这叫互联网垃圾搬运。

读者反馈

你希望这篇文章补充什么?

可以留下你想看的功能、改进建议或后续教程方向。我会定期整理。

位读者看过
条建议
0/500

最新建议

还没有建议,你可以抢个沙发。

为了去重统计,只保存 IP、浏览器信息和本机指纹的哈希,不保存明文 IP。