㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~
㊙️本期爬虫难度指数:⭐⭐
🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:
-
-
- [🌟 开篇语](#🌟 开篇语)
- [0️⃣ 前言(Preface)](#0️⃣ 前言(Preface))
- [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
- [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
- [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
- [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
- [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
- [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
- [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
- [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
- [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
- [🔟 常见问题与排错(强烈建议写)](#🔟 常见问题与排错(强烈建议写))
- [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
- [🌟 文末](#🌟 文末)
-
- [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
- [✅ 互动征集](#✅ 互动征集)
- [✅ 免责声明](#✅ 免责声明)
-
🌟 开篇语
哈喽,各位小伙伴们你们好呀~我是【喵手】。
运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO
欢迎大家常来逛逛,一起学习,一起进步~🌟
我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略 到反爬对抗 ,从数据清洗 到分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上。
📌 专栏食用指南(建议收藏)
- ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
- ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
- ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
- ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用
📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。
💕订阅后更新会优先推送,按目录学习更高效💯~
0️⃣ 前言(Preface)
哈喽,各位热爱大自然的代码捕手们!今天我们将化身为赛博空间的巡护员,目标是抓取世界自然基金会(WWF)官网上的物种保护新闻。我们会使用原生且轻量的 requests + BeautifulSoup4 组合,穿梭于新闻列表与详情页之间,最终产出一份结构化且极其干净的数据文件 wwf_species_news.csv,并在最后附上一张全英文的物种新闻分布统计图。
读完这篇充满绿意的实战,你将直接解锁:
- 经典的"列表页进详情页"深度遍历抓取模板。
- 应对非标准化 HTML 标签的信息抽取容错技巧。
- 一套能够无缝对接后续 NLP 文本分析的语料库采集方案。
1️⃣ 摘要(Abstract)
本文以非营利性环保组织(NGO)的新闻资讯板块为切入点,采用静态网页解析路线。利用 Python 发起 HTTP 请求获取列表页 DOM 树,提取所有文章链接后进行二次下钻抓取。重点攻克文章正文的标题、更新日期以及非结构化标签(物种与地区)的精准定位,并结合 pandas 实现了数据的持久化与可视化。
读者读完将获得:
- 跨页面数据拼装与相对路径转换的核心经验。
- 一份兼顾请求礼仪与解析鲁棒性的工业级代码。
2️⃣ 背景与需求(Why)
为什么要抓取 WWF 的动态?因为气候变化和物种灭绝是全人类面临的严峻挑战。如果我们能自动化聚合这些动态,就能快速得知当前全球保护资金和科研力量的倾斜方向。
我们的目标站点 :世界自然基金会官网的新闻/物种保护专栏(如 worldwildlife.org/news 或类似站点)。
我们需要精准扣下的目标字段:
title: 新闻标题species: 涉及物种(如:大熊猫、雪豹、海龟)region: 涉及地区(如:亚马逊、长江流域)update_date: 更新日期
3️⃣ 合规与注意事项(必写)
面对 NGO 网站,咱们的敬畏之心更要拉满:
- robots.txt 君子协定:NGO 组织通常非常欢迎研究人员和学者访问其公开数据,但绝不意味着你可以肆无忌惮地 DDOS 他们的服务器。
- 极其克制的频率控制 :WWF 的服务器带宽是用全球爱心人士的捐款买来的,请务必在你的代码里加上
time.sleep(3),放慢脚步,温柔地抓取。 - 数据纯洁性:我们只抓取公开的环保新闻与物种保护动态,绝不触碰任何涉及捐款人隐私或后台账号的敏感区域。
4️⃣ 技术选型与整体流程(What/How)
环保组织的官网通常以展示文字和图片为主,大多是经典的 静态 HTML 页面 (SSR 服务端渲染)。因此,我们不需要搞极其笨重的 Selenium 或 Playwright,直接用 requests 获取源码,用 bs4 解析 DOM 树,这是最优雅、性能最好的方案。
整体流程链路:
访问新闻列表页 → 提取所有文章的 href → 循环下钻进入详情页 (Fetcher) → 剥离冗余标签,提取核心四要素 (Parser) → 数据去重与清洗 → 落库为 CSV (Storage)
为什么选 bs4? 对于内容型网站,文章结构经常会出现 <p> 标签嵌套混乱的情况,BeautifulSoup 在处理这类"脏 HTML"时的容错能力堪称一绝。
5️⃣ 环境准备与依赖安装(可复现)
来,检查一下咱们的开发背包。这次依然少不了数据分析两件套 pandas 和 matplotlib。
-
Python 版本:推荐 3.8 及以上。
-
依赖安装:
bashpip install requests beautifulsoup4 pandas matplotlib -
项目结构推荐:
textwwf_spider/ ├── spider_main.py # 采集与解析核心逻辑 ├── plot_species.py # 全英文图表生成器 └── data/ # 存放输出的 CSV 与图片
6️⃣ 核心实现:请求层(Fetcher)
我们不仅要写一个会重试的请求池,还要特别注意 URL 的拼接问题 。很多网站列表页里的链接是相对路径(比如 /stories/panda-news),我们需要用 urllib.parse.urljoin 把它变成绝对路径。
python
import requests
import time
import random
from urllib.parse import urljoin
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class WWFFetcher:
def __init__(self, base_url="https://www.worldwildlife.org"):
self.base_url = base_url
self.session = requests.Session()
# 伪装头部,表现得像个正常的学术研究者
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8'
}
# 温柔的重试策略:防断网、防 503
retries = Retry(total=3, backoff_factor=2, status_forcelist=[500, 502, 503, 504])
self.session.mount('https://', HTTPAdapter(max_retries=retries))
def get_html(self, url):
try:
# 必须休眠!保护大自然的服务器!
time.sleep(random.uniform(2.0, 4.0))
print(f"🌍 正在探索页面: {url}")
response = self.session.get(url, headers=self.headers, timeout=15)
response.raise_for_status()
response.encoding = 'utf-8'
return response.text
except requests.exceptions.RequestException as e:
print(f"❌ 哎呀,这片森林迷路了 [URL: {url}]: {e}")
return None
def make_absolute_url(self, href):
return urljoin(self.base_url, href)
7️⃣ 核心实现:解析层(Parser)
分为两步:第一步从列表页抠出所有的详情链接;第二步深入详情页,用 CSS 选择器把标题、日期和分类标签(假设包含了物种和地区)挖出来。
python
from bs4 import BeautifulSoup
import re
class WWFParser:
@staticmethod
def parse_list_for_links(html):
"""解析新闻列表页,捞出所有文章详情的链接"""
if not html: return []
soup = BeautifulSoup(html, 'html.parser')
links = []
# 假设文章卡片都在 class="story-card" 的 div 或 a 标签里
# 💡 实战提示:你需要用 F12 审查真实的类名,此处为通用示意
cards = soup.select('div.card-content a, h3.title a')
for card in cards:
if 'href' in card.attrs:
links.append(card['href'])
# 去重并返回
return list(set(links))
@staticmethod
def parse_article(html):
"""解析文章详情,提取四大金刚字段"""
if not html: return None
soup = BeautifulSoup(html, 'html.parser')
# 1. 提取标题 (Title)
title_node = soup.select_one('h1.title, h1.article-title')
title = title_node.get_text(strip=True) if title_node else "Unknown Title"
# 2. 提取更新日期 (Date) - 往往在 time 标签或特定 class 下
date_node = soup.select_one('time, span.date')
update_date = date_node.get_text(strip=True) if date_node else "Unknown Date"
# 3. 提取物种 (Species) & 地区 (Region)
# WWF 的网站通常会在文章顶部或底部有 tags(标签),比如标签里写了 "Amur Leopard" 或 "Asia"
# 我们用健壮的 get_text 来兜底
species = "General/Multiple"
region = "Global"
tag_nodes = soup.select('div.tags a, ul.categories li')
tags_text = [tag.get_text(strip=True) for tag in tag_nodes]
# 【实战技巧】:如果没有明确区分哪个是物种哪个是地区,我们可以将标签拼接
# 或者使用简单的关键字匹配(这里做简化处理展示逻辑)
if tags_text:
# 假装用逗号隔开,后续供 Pandas 进一步清洗
combined_tags = " | ".join(tags_text)
species = combined_tags # 暂存到字段中
return {
'Title': title,
'Species': species,
'Region': region,
'Update_Date': update_date
}
8️⃣ 数据存储与导出(Storage)
依然是我们最信赖的 pandas 出场。它能把抓取到的字典列表秒变结构化数据,而且导出 CSV 时绝对不会在 Excel 里出现乱码。
python
import pandas as pd
import os
class SpeciesDataStorage:
def __init__(self, filename="data/wwf_species_news.csv"):
self.filename = filename
os.makedirs(os.path.dirname(self.filename), exist_ok=True)
def save(self, data_list):
if not data_list:
print("📭 这次巡护没有发现新的数据呢。")
return
df = pd.DataFrame(data_list)
# 如果文件不存在,就写入;存在就追加写入
if not os.path.exists(self.filename):
df.to_csv(self.filename, index=False, encoding='utf-8-sig')
else:
df.to_csv(self.filename, mode='a', header=False, index=False, encoding='utf-8-sig')
print(f"💾 守护成功!{len(data_list)} 条大自然的声音已封存入 {self.filename}")
9️⃣ 运行方式与结果展示(必写)
将所有齿轮啮合,直接在终端里跑一下这个脚本,感受大自然的呼唤吧!
python
# 文件名: spider_main.py
def main():
print("🚀 启动全球濒危物种保护雷达...")
base_url = "https://www.worldwildlife.org"
# 这里是目标列表页的模拟 URL,实战中换成你想抓的页面
list_page_url = f"{base_url}/stories"
fetcher = WWFFetcher(base_url)
storage = SpeciesDataStorage()
# 第一步:获取新闻列表并抽出文章链接
list_html = fetcher.get_html(list_page_url)
relative_links = WWFParser.parse_list_for_links(list_html)
print(f"🔍 列表页扫描完毕,发现 {len(relative_links)} 篇保护动态,准备下钻...")
scraped_data = []
# 第二步:循环下钻进入详情页
for link in relative_links[:5]: # 为了演示,这里只抓前 5 篇
full_url = fetcher.make_absolute_url(link)
article_html = fetcher.get_html(full_url)
article_data = WWFParser.parse_article(article_html)
if article_data:
scraped_data.append(article_data)
# 第三步:数据落库
storage.save(scraped_data)
print("🎉 本次巡护任务圆满结束!")
if __name__ == "__main__":
# main() # 实战中取消注释执行
pass
经过抓取后输出的 wwf_species_news.csv 示例结果:
| Title | Species | Region | Update_Date |
|---|---|---|---|
| Tracking Snow Leopards in the High Altitudes | Snow Leopard | Mammals | Central Asia |
| A Lifeline for the Amazon River Dolphin | River Dolphin | Aquatic | Amazon |
| Saving the Tiger: A Global Initiative | Tiger | Big Cats | Global |
🔟 常见问题与排错(强烈建议写)
在抓取这种以内容生态为主的站点时,最容易遇到排版不规范的坑:
- 提取到一堆乱七八糟的空行和制表符 :标题或日期里可能会包含大量的
\n或\t。一定要在提取时使用.get_text(strip=True)来把这些首尾的空白字符全部榨干! - 相对路径导致 404 :很多新手拿到
href="/news/123"直接丢给 requests,结果直接报错。永远记得用urllib.parse.urljoin把它和根域名拼合在一起。 - 没有明显的物种/地区标签怎么办? 这就是我在开头问你的那个问题!如果网页连 Tag 都不给,你只能把文章的整段
<p>抓回来,然后自己写一个 Python 词典(包含全世界的国家名和动物名),用if "Panda" in text这种硬编码的方式去做映射清洗了。
1️⃣1️⃣ 进阶优化(可选但加分)
为了让你的保护报告看起来更专业,按照系统的严格要求,我们要用代码生成一张全英文元素 的图表!我们来画一张展示不同物种在新闻中提及频率的柱状图 (wwf_species_distribution.png)。
python
import pandas as pd
import matplotlib.pyplot as plt
import os
def generate_english_species_chart():
csv_file = "data/wwf_species_news.csv"
if not os.path.exists(csv_file): return
df = pd.read_csv(csv_file)
# 模拟我们有一列干净的 Species 分类数据(为了演示图表,我们做一个简单的聚合)
# 现实中你可能需要对 combined_tags 进行 split 和 explode 操作
# 这里假设清洗后物种频率如下:
species_counts = df['Species'].value_counts().head(5)
plt.figure(figsize=(10, 6))
# 画一张充满生机的绿色柱状图
bars = plt.bar(species_counts.index.astype(str), species_counts.values, color='#4CAF50', alpha=0.85)
# ⚠️ 强制全部英文描述(Titles, Labels, Axis descriptions)
plt.title('Top Endangered Species in Recent WWF Conservation News', fontsize=15, fontweight='bold
🌟 文末
好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥
✅ 专栏持续更新中|建议收藏 + 订阅
墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:
✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)
📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集
想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?
评论区留言告诉我你的需求,我会优先安排实现(更新)哒~
⭐️ 若喜欢我,就请关注我叭~(更新不迷路)
⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)
⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)
✅ 免责声明
本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。
使用或者参考本项目即表示您已阅读并同意以下条款:
- 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
- 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
- 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
- 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
