Python爬虫实战:多线程爬取\+数据清洗\+可视化(附完整源码)

摘要: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池),进一步提升爬虫开发能力。

相关推荐
郭涤生2 小时前
C++ 回调较容易出错问题
开发语言·c++
开源盛世!!2 小时前
4.20-4.22
java·服务器·开发语言
MediaTea2 小时前
Scikit-learn:一个最小机器学习工作流示例
人工智能·python·机器学习·scikit-learn
qq_349317482 小时前
Layui如何修改表格单元格内文字的行间距
jvm·数据库·python
MmeD UCIZ2 小时前
GO 快速升级Go版本
开发语言·redis·golang
2301_775148152 小时前
Redis如何实现用户标签管理_利用Set结构存储唯一属性集合
jvm·数据库·python
tERS ERTS2 小时前
头歌答案--爬虫实战
java·前端·爬虫
m0_596406372 小时前
mysql如何配置审计日志输出_mysql audit_log_format设置
jvm·数据库·python
Fate_I_C2 小时前
Kotlin函数一
android·开发语言·kotlin