很多人刚学 Python 数据采集时,会把注意力全放在“怎么把网页抓下来”。

我刚开始也这样。代码跑通,终端里打印出一大片 HTML,就以为事情已经完成了一半。后来真正做项目才发现,网页采集只占很小一部分。数据能不能去重、能不能统一格式、最后能不能回答一个具体问题,才决定这份数据有没有用。

所以这篇不讲复杂反爬,也不碰登录、验证码和高频请求。我用一个公开的练习网站,带你从头跑通三个步骤:

用 Python 采集网页数据;

用 Pandas 清洗脏数据;

统计作者和标签,生成分析结果。

如果你是零基础,先把这条完整流程跑通,比一开始研究代理、并发和浏览器自动化更合适。

准备 Python 环境

先确认电脑安装了 Python 3.10 或更高版本。

在终端执行:

python --version

然后安装本次要用的库:

pip install requests beautifulsoup4 pandas matplotlib

四个库分工很清楚:

requests:发送网页请求;

BeautifulSoup:从 HTML 中提取内容;

pandas:清洗和统计数据;

matplotlib:生成简单图表。

建议新建一个文件夹,再创建 main.py 文件。后面的代码全部放进这个文件即可。

先采集网页数据

本次使用 quotes.toscrape.com。这是专门供采集练习使用的网站,页面中包含名言、作者和标签,还有分页结构。

先看采集部分:

from pathlib import Path

import time

import requests

import pandas as pd

from bs4 import BeautifulSoup

BASE_URL = "https://quotes.toscrape.com/page/{}/"

HEADERS = {

"User-Agent": (

"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "

"AppleWebKit/537.36 Chrome/124.0 Safari/537.36"

)

}

def collect_quotes(max_pages=5):

records = []

session = requests.Session()

for page in range(1, max_pages + 1):

url = BASE_URL.format(page)

print(f"正在采集:{url}")

response = session.get(

url,

headers=HEADERS,

timeout=10

)

response.raise_for_status()

soup = BeautifulSoup(response.text, "html.parser")

quote_blocks = soup.select(".quote")

if not quote_blocks:

break

for block in quote_blocks:

quote = block.select_one(".text").get_text(

strip=True

).strip("“”")

author = block.select_one(".author").get_text(

strip=True

)

tags = [

tag.get_text(strip=True)

for tag in block.select(".tag")

]

records.append({

"quote": quote,

"author": author,

"tags": "|".join(tags),

"source_url": url

})

if not soup.select_one("li.next a"):

break

time.sleep(1)

return pd.DataFrame(records)

这段代码做了几件事。

首先,requests.Session() 会复用连接,比每次单独请求稍稳一些。其次,每个请求都设置了十秒超时,避免网络异常时程序一直卡住。最后,每采完一页暂停一秒,不连续轰炸网站。

我通常会保留 source_url 字段。以后发现某条数据不对,可以直接回到原页面核查。很多新手为了让表格看起来干净,会过早删除来源字段,后面排错反而更麻烦。

清洗采集结果

采集下来的数据不要直接拿去分析。先保留一份原始文件,再生成清洗后的版本。

def clean_quotes(df):

cleaned = df.copy()

cleaned["quote"] = (

cleaned["quote"]

.str.replace(r"\s+", " ", regex=True)

.str.strip()

)

cleaned["author"] = (

cleaned["author"]

.str.replace(r"\s+", " ", regex=True)

.str.strip()

)

cleaned["tags"] = cleaned["tags"].apply(

lambda value: "|".join(

sorted({

tag.strip().lower()

for tag in value.split("|")

if tag.strip()

})

)

)

cleaned = cleaned.dropna(

subset=["quote", "author"]

)

cleaned = cleaned[

(cleaned["quote"] != "")

& (cleaned["author"] != "")

]

cleaned = cleaned.drop_duplicates(

subset=["quote", "author"]

)

cleaned["quote_length"] = (

cleaned["quote"].str.len()

)

return cleaned.reset_index(drop=True)

这里主要处理四类问题:

删除多余空格;

过滤空内容;

统一标签格式;

按“名言加作者”去重。

去重时不要只按作者判断。一个作者可能有多条名言,只保留一条会误删正常数据。相反,只按名言去重也可能遇到同一句话被不同页面重复收录。实际项目中,去重字段要根据业务含义确定。

quote_length 是一个派生字段。它不是网页直接提供的,而是根据名言文本计算出来的,后面可以用来观察文本长度。

做第一轮数据分析

零基础的数据分析不用一上来就做复杂模型。先回答三个简单问题:

一共采集了多少条记录?

哪些作者出现得最多?

哪些标签最常见?

def analyze_quotes(df):
   print(f"\n清洗后记录数:{len(df)}")
   print(f"作者数量:{df['author'].nunique()}")
   print(
       f"平均文本长度:"
       f"{df['quote_length'].mean():.1f}"
   )

   author_top = (
       df["author"]
       .value_counts()
       .head(10)
   )

   tag_data = (
       df.assign(tag=df["tags"].str.split("|"))
       .explode("tag")
   )

   tag_top = (
       tag_data.loc[tag_data["tag"] != "", "tag"]
       .value_counts()
       .head(10)
   )

   print("\n出现次数最多的作者:")
   print(author_top)

   print("\n出现次数最多的标签:")
   print(tag_top)

   return author_top, tag_top

这里的 value_counts() 用于统计每个值出现的次数。

标签稍微特殊一点。一条名言可能同时带有多个标签,所以需要先用 split() 拆分,再通过 explode() 把一行展开成多行,最后才能准确计数。

这也是数据清洗中很常见的情况:原始字段适合展示,不一定适合统计。分析之前,往往要先调整数据结构。

保存数据和分析图

最后补上文件保存与可视化代码:

def save_chart(author_top):

Path("output").mkdir(exist_ok=True)

chart_data = author_top.sort_values()

ax = chart_data.plot(

kind="barh",

figsize=(9, 6),

color="#4C78A8"

)

ax.set_title("Top Authors by Quote Count")

ax.set_xlabel("Quote Count")

ax.set_ylabel("Author")

figure = ax.get_figure()

figure.tight_layout()

figure.savefig(

"output/top_authors.png",

dpi=150

)

def main():

Path("data").mkdir(exist_ok=True)

raw_df = collect_quotes(max_pages=5)

if raw_df.empty:

raise RuntimeError("未采集到数据,请检查网络或页面结构。")

raw_df.to_csv(

"data/raw_quotes.csv",

index=False,

encoding="utf-8-sig"

)

clean_df = clean_quotes(raw_df)

clean_df.to_csv(

"data/clean_quotes.csv",

index=False,

encoding="utf-8-sig"

)

author_top, tag_top = analyze_quotes(clean_df)

author_top.rename("count").to_csv(

"data/author_top.csv",

encoding="utf-8-sig"

)

tag_top.rename("count").to_csv(

"data/tag_top.csv",

encoding="utf-8-sig"

)

save_chart(author_top)

print("\n处理完成,结果已保存到 data 和 output 目录。")

if __name__ == "__main__":

main()

把前面的代码按顺序放在同一个 main.py 文件中,然后执行:

python main.py

正常情况下,你会得到这些文件:

data/

├── raw_quotes.csv

├── clean_quotes.csv

├── author_top.csv

└── tag_top.csv

output/

└── top_authors.png

其中,raw_quotes.csv 是未经修改的原始数据,clean_quotes.csv 是清洗后的数据。两份都保留,方便后续核对。

运行失败时先检查什么

第一次运行,常见问题基本集中在下面几类。

如果提示 ModuleNotFoundError,说明依赖库没有安装成功。根据报错中的库名重新执行 pip install。

如果提示连接超时,可以先在浏览器里打开目标网站,确认当前网络能够访问,再适当调大 timeout。不要一看到超时就直接加并发,那通常只会让问题更难判断。

如果程序正常运行但结果为空,通常是网页结构发生变化。可以打印一小段 HTML 检查:

print(response.text[:500])

如果页面内容正常,就重新核对 .quote、.text 和 .author 这些 CSS 选择器。

零基础下一步学什么

跑完这个项目后,我建议按下面的顺序继续练习:

给采集函数增加失败重试和日志记录;

学习 CSV、Excel、JSON 三种常见数据格式;

使用 Pandas 处理缺失值、异常值和日期字段;

学习分组统计、透视表和多条件筛选;

最后再接触浏览器自动化、代理调度和并发采集。

顺序不要反过来。

我见过不少刚入门的人,第一周就开始研究代理池和多线程,结果连重复数据都没处理,采集十万条后才发现真正能用的不到一半。对零基础来说,先保证数据采得准、洗得干净、能够回答问题,比单纯追求采集数量重要得多。

还有一条边界需要提前说清楚:练习时优先使用公开测试站点、自有页面或已经获得授权的数据源。正式采集前要查看网站条款、访问限制和数据使用要求,不绕过登录、验证码或明确的访问控制。

把这套流程跑通,你就已经完成了一个最小但完整的数据项目:从网页获得原始数据,整理成结构化表格,再用统计结果回答具体问题。后面换成商品、资讯或公开目录,底层思路不会有太大变化。

原文来自邦阅网 (52by.com) - www.52by.com/article/236928

声明:该文观点仅代表作者本人,邦阅网系信息发布平台,仅提供信息存储空间服务,若存在侵权问题,请及时联系邦阅网或作者进行删除。

延伸阅读
评论
登录 后参与评论
发表你的高见

擅长:跨境数据采集