摘要:Python爬虫是数据采集的核心工具,广泛应用于数据分析、舆情监控、竞品分析等场景。本文基于Python3.10,使用requests、BeautifulSoup、多线程、pandas等工具,实现多线程爬取某电商平台商品数据、数据清洗、数据可视化的完整流程,附详细注释与实战技巧,解决爬虫中的反爬问题(UA伪装、IP代理、请求频率控制),适合Python初学者与数据分析爱好者快速上手爬虫开发。
一、前言:Python爬虫的核心价值与技术选型
在大数据时代,数据是核心资产,而爬虫是获取公开数据的重要手段。Python凭借其简洁的语法、丰富的第三方库,成为爬虫开发的首选语言。本文选取某电商平台商品数据作为爬取目标,实现从数据爬取、数据清洗到数据可视化的全流程,掌握多线程爬取、反爬处理、数据处理的核心技巧。
技术选型:requests(发送HTTP请求)、BeautifulSoup(解析HTML页面)、threading(多线程爬取)、pandas(数据清洗与处理)、matplotlib(数据可视化)、fake_useragent(UA伪装)、requests-proxies(IP代理)。
二、环境搭建:Python爬虫环境配置
本次实战基于Python3.10,使用pip管理依赖,安装核心第三方库:
bash
# 安装核心依赖
pip install requests beautifulsoup4 pandas matplotlib fake-useragent requests-proxies
项目目录结构设计:
text
python-spider-project/
├── src/
│ ├── spider/ # 爬虫核心代码
│ │ ├── __init__.py
│ │ ├── crawler.py # 多线程爬虫
│ │ └── anti_crawl.py # 反爬处理
│ ├── data/ # 数据存储目录
│ │ ├── raw_data.csv # 原始爬取数据
│ │ └── clean_data.csv # 清洗后数据
│ ├── analysis/ # 数据分析与可视化
│ │ ├── __init__.py
│ │ └── visualize.py # 数据可视化
│ └── main.py # 入口文件
└── requirements.txt # 依赖配置
三、核心功能实现(分模块讲解+源码)
3.1 反爬处理(UA伪装+IP代理+请求频率控制)
电商平台通常会有反爬机制,如限制UA、限制IP、限制请求频率,本文通过以下方式解决反爬问题:
python
# src/spider/anti_crawl.py(反爬处理工具)
from fake_useragent import UserAgent
import random
import time
# UA伪装:随机生成User-Agent
def get_random_ua():
ua = UserAgent()
return ua.random
# IP代理:从代理池获取随机IP(此处使用免费代理,实际生产建议使用付费代理)
def get_random_proxy():
# 免费代理池(可替换为付费代理API)
proxies_pool = [
{"http": "http://123.45.67.89:8080", "https": "https://123.45.67.89:8080"},
{"http": "http://98.76.54.32:8080", "https": "https://98.76.54.32:8080"},
]
return random.choice(proxies_pool) if proxies_pool else None
# 请求频率控制:随机延迟,避免请求过于频繁
def random_delay(min_delay=0.5, max_delay=2):
time.sleep(random.uniform(min_delay, max_delay))
3.2 多线程爬取商品数据
使用多线程爬取电商平台商品数据,提升爬取效率,核心逻辑:确定爬取URL、发送请求、解析页面、提取数据、存储数据。
python
# src/spider/crawler.py(多线程爬虫)
import requests
from bs4 import BeautifulSoup
import threading
import pandas as pd
from .anti_crawl import get_random_ua, get_random_proxy, random_delay
# 全局锁,避免多线程写入数据冲突
lock = threading.Lock()
# 商品数据存储列表
product_data = []
# 爬取单页商品数据
def crawl_single_page(page_num):
# 爬取URL(以某电商平台为例,替换为实际URL)
url = f"https://example.com/products?page={page_num}"
headers = {
"User-Agent": get_random_ua(),
"Referer": "https://example.com/",
"Accept-Language": "zh-CN,zh;q=0.9"
}
proxies = get_random_proxy()
try:
# 发送请求
response = requests.get(url, headers=headers, proxies=proxies, timeout=10)
response.raise_for_status() # 抛出HTTP错误
soup = BeautifulSoup(response.text, "html.parser")
# 解析页面,提取商品数据(根据实际页面结构调整)
products = soup.find_all("div", class_="product-item")
for product in products:
# 提取商品名称
name = product.find("h3", class_="product-name").text.strip() if product.find("h3", class_="product-name") else "未知"
# 提取商品价格
price = product.find("span", class_="product-price").text.strip() if product.find("span", class_="product-price") else "0"
# 提取商品销量
sales = product.find("span", class_="product-sales").text.strip() if product.find("span", class_="product-sales") else "0"
# 提取商品评分
score = product.find("span", class_="product-score").text.strip() if product.find("span", class_="product-score") else "0"
# 数据存入列表(加锁,避免多线程冲突)
with lock:
product_data.append({
"name": name,
"price": price,
"sales": sales,
"score": score,
"page": page_num
})
print(f"第{page_num}页爬取完成,共爬取{len(products)}个商品")
random_delay() # 控制请求频率
except Exception as e:
print(f"第{page_num}页爬取失败:{str(e)}")
# 失败重试(最多重试3次)
retry_count = 0
while retry_count < 3:
try:
crawl_single_page(page_num)
break
except:
retry_count += 1
print(f"第{page_num}页重试第{retry_count}次")
# 多线程爬取多页数据
def multi_thread_crawl(start_page, end_page):
threads = []
for page_num in range(start_page, end_page + 1):
# 创建线程
thread = threading.Thread(target=crawl_single_page, args=(page_num,))
threads.append(thread)
thread.start()
# 等待所有线程执行完成
for thread in threads:
thread.join()
# 将数据存入CSV文件(原始数据)
df = pd.DataFrame(product_data)
df.to_csv("../data/raw_data.csv", index=False, encoding="utf-8-sig")
print(f"多线程爬取完成,共爬取{len(product_data)}个商品,数据已保存至raw_data.csv")
3.3 数据清洗与可视化
爬取的原始数据存在缺失值、格式不规范等问题,需进行数据清洗,然后使用matplotlib实现数据可视化,直观展示数据分布。
python
# src/analysis/visualize.py(数据清洗与可视化)
import pandas as pd
import matplotlib.pyplot as plt
# 设置中文字体(避免乱码)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 数据清洗
def clean_data():
# 读取原始数据
df = pd.read_csv("../data/raw_data.csv", encoding="utf-8-sig")
# 1. 处理缺失值(删除缺失值过多的行)
df = df.dropna(subset=["name", "price"], how="any")
# 2. 处理价格数据(提取数字,转换为float类型)
df["price"] = df["price"].str.extract(r'(\d+\.\d+|\d+)').astype(float)
# 3. 处理销量数据(提取数字,转换为int类型)
df["sales"] = df["sales"].str.extract(r'(\d+)').astype(int)
# 4. 处理评分数据(转换为float类型)
df["score"] = df["score"].astype(float)
# 5. 去重(根据商品名称去重)
df = df.drop_duplicates(subset=["name"], keep="first")
# 保存清洗后的数据
df.to_csv("../data/clean_data.csv", index=False, encoding="utf-8-sig")
print(f"数据清洗完成,清洗后共{len(df)}个商品,数据已保存至clean_data.csv")
return df
# 数据可视化
def data_visualize(df):
# 1. 商品价格分布直方图
plt.figure(figsize=(10, 6))
plt.hist(df["price"], bins=20, color="skyblue", edgecolor="black")
plt.title("商品价格分布")
plt.xlabel("价格(元)")
plt.ylabel("商品数量")
plt.savefig("../data/price_distribution.png", dpi=300, bbox_inches="tight")
plt.close()
# 2. 商品销量与评分关系散点图
plt.figure(figsize=(10, 6))
plt.scatter(df["score"], df["sales"], color="orange", alpha=0.6)
plt.title("商品销量与评分关系")
plt.xlabel("评分")
plt.ylabel("销量")
plt.savefig("../data/sales_score_relation.png", dpi=300, bbox_inches="tight")
plt.close()
# 3. 各页面商品数量柱状图
page_count = df["page"].value_counts().sort_index()
plt.figure(figsize=(12, 6))
page_count.plot(kind="bar", color="green")
plt.title("各页面商品数量分布")
plt.xlabel("页码")
plt.ylabel("商品数量")
plt.xticks(rotation=0)
plt.savefig("../data/page_product_count.png", dpi=300, bbox_inches="tight")
plt.close()
print("数据可视化完成,图表已保存至data目录")
3.4 入口文件(整合全流程)
python
# src/main.py(入口文件)
from spider.crawler import multi_thread_crawl
from analysis.visualize import clean_data, data_visualize
if __name__ == "__main__":
# 1. 多线程爬取数据(爬取第1-10页)
print("开始多线程爬取商品数据...")
multi_thread_crawl(start_page=1, end_page=10)
# 2. 数据清洗
print("\n开始数据清洗...")
clean_df = clean_data()
# 3. 数据可视化
print("\n开始数据可视化...")
data_visualize(clean_df)
print("\n全流程执行完成!")
四、总结与延伸
本文完整实现了Python多线程爬虫的全流程,包括反爬处理、多线程爬取、数据清洗、数据可视化,掌握了Python爬虫的核心技巧,解决了实际爬取中的反爬问题。
延伸学习:可深入研究动态页面爬取(使用Selenium、Playwright)、爬虫框架(Scrapy)的使用、IP代理池的搭建、数据存储(MySQL、MongoDB),以及更复杂的反爬机制(验证码识别、Cookie池),进一步提升爬虫开发能力。