Python爬虫实战:用requests库爬取电商数据并保存到Excel

爬虫是 Python 最经典的应用场景之一。本文将从零开始,带你用 requests 库爬取电商商品数据,并使用 pandas 和 openpyxl 将数据清洗后保存到 Excel 文件。包含完整的反爬处理策略,适合新手学习。

一、准备工作

1.1 安装依赖库

首先我们需要安装以下 Python 库:

pip install requests pandas openpyxl fake-useragent

各库的作用:

  • requests:发送 HTTP 请求,获取网页内容
  • pandas:数据处理和清洗
  • openpyxl:读写 Excel 文件
  • fake-useragent:生成随机 User-Agent

1.2 项目结构

spider_project/
├── spider.py          # 主爬虫脚本
├── data_clean.py      # 数据清洗脚本
├── requirements.txt   # 依赖列表
└── output/            # 输出目录
    └── products.xlsx  # 最终 Excel 文件

二、基础爬虫实现

2.1 发送第一个请求

我们先从最简单的请求开始:

import requests

# 发送 GET 请求
url = "https://books.toscrape.com/"
response = requests.get(url)

# 查看响应状态码
print(f"状态码: {response.status_code}")
print(f"响应长度: {len(response.text)}")

# 查看部分响应内容
print(response.text[:500])

2.2 解析 HTML 内容

我们使用 Python 内置的 html.parser 或者更强大的 lxml 来解析 HTML:

from html.parser import HTMLParser

class ProductParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.products = []
        self.current_product = {}
        self.capture = False

    def handle_starttag(self, tag, attrs):
        attrs_dict = dict(attrs)
        if tag == 'article' and 'product_pod' in attrs_dict.get('class', ''):
            self.current_product = {}
        if tag == 'h3':
            self.capture = True

    def handle_data(self, data):
        if self.capture:
            self.current_product['title'] = data.strip()
            self.capture = False

parser = ProductParser()
parser.feed(response.text)
print(f"找到 {len(parser.products)} 个商品")

三、反爬处理策略

电商平台通常会有反爬机制,我们需要做好以下处理:

3.1 设置请求头

import requests
from fake_useragent import UserAgent

ua = UserAgent()

# 构造完整的请求头
headers = {
    'User-Agent': ua.random,
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'Accept-Encoding': 'gzip, deflate, br',
    'Connection': 'keep-alive',
    'Referer': 'https://books.toscrape.com/',
}

response = requests.get(url, headers=headers)

3.2 请求延迟

避免请求过快被识别为爬虫:

import time
import random

def crawl_with_delay(urls):
    results = []
    for url in urls:
        response = requests.get(url, headers=headers)
        results.append(response)
        # 随机延迟 1-3 秒
        delay = random.uniform(1, 3)
        print(f"等待 {delay:.2f} 秒...")
        time.sleep(delay)
    return results

3.3 使用代理 IP

# 代理池配置
proxy_list = [
    'http://123.45.67.89:8080',
    'http://98.76.54.32:3128',
    # 更多代理...
]

def get_random_proxy():
    proxy = random.choice(proxy_list)
    return {'http': proxy, 'https': proxy}

# 使用代理发送请求
try:
    proxy = get_random_proxy()
    response = requests.get(
        url,
        headers=headers,
        proxies=proxy,
        timeout=10
    )
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

3.4 重试机制

from retrying import retry

@retry(stop_max_attempt_number=3, wait_fixed=2000)
def fetch_url(url, headers=headers):
    response = requests.get(url, headers=headers, timeout=10)
    response.raise_for_status()
    return response

四、完整爬虫代码

下面是完整的爬虫代码,整合了所有反爬策略:

import requests
import time
import random
import json
from fake_useragent import UserAgent

class ECommerceSpider:
    # 电商数据爬虫

    def __init__(self):
        self.ua = UserAgent()
        self.session = requests.Session()
        self.data = []

    def get_headers(self):
        # 生成随机请求头
        return {
            'User-Agent': self.ua.random,
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Connection': 'keep-alive',
        }

    def crawl_page(self, page_num):
        # 爬取单页数据
        url = f"https://books.toscrape.com/catalogue/page-{page_num}.html"
        headers = self.get_headers()

        try:
            response = self.session.get(url, headers=headers, timeout=10)
            response.raise_for_status()
            return response.text
        except requests.exceptions.RequestException as e:
            print(f"第 {page_num} 页爬取失败: {e}")
            return None

    def parse_products(self, html):
        # 解析商品数据
        from html.parser import HTMLParser

        class BookParser(HTMLParser):
            def __init__(self):
                super().__init__()
                self.books = []
                self.current = {}
                self.in_title = False
                self.in_price = False
                self.in_rating = False

            def handle_starttag(self, tag, attrs):
                attrs_dict = dict(attrs)
                if tag == 'article' and 'product_pod' in attrs_dict.get('class', ''):
                    self.current = {}
                if tag == 'h3':
                    self.in_title = True
                if tag == 'p' and 'price_color' in attrs_dict.get('class', ''):
                    self.in_price = True
                if tag == 'p' and 'star-rating' in attrs_dict.get('class', ''):
                    rating = attrs_dict.get('class', '').split()[-1]
                    self.current['rating'] = rating

            def handle_data(self, data):
                if self.in_title:
                    self.current['title'] = data.strip()
                    self.in_title = False
                if self.in_price:
                    self.current['price'] = data.strip()
                    self.in_price = False

            def handle_endtag(self, tag):
                if tag == 'article' and self.current:
                    self.books.append(self.current)
                    self.current = {}

        parser = BookParser()
        parser.feed(html)
        return parser.books

    def run(self, max_pages=5):
        # 运行爬虫
        print("=== 开始爬取 ===")
        for page in range(1, max_pages + 1):
            print(f"正在爬取第 {page} 页...")
            html = self.crawl_page(page)
            if html:
                products = self.parse_products(html)
                self.data.extend(products)
                print(f"  获取到 {len(products)} 条数据")
            # 随机延迟
            time.sleep(random.uniform(1, 3))

        print(f"=== 爬取完成,共 {len(self.data)} 条数据 ===")
        return self.data

# 运行爬虫
spider = ECommerceSpider()
data = spider.run(max_pages=5)

五、数据清洗

爬取到的原始数据通常需要清洗。我们使用 pandas 进行处理:

import pandas as pd

def clean_data(raw_data):
    # 数据清洗
    df = pd.DataFrame(raw_data)
    print(f"原始数据量: {len(df)}")
    print(df.head())

    # 1. 去除重复数据
    df = df.drop_duplicates(subset=['title'])
    print(f"去重后数据量: {len(df)}")

    # 2. 清洗价格字段(去除货币符号,转换为数值)
    df['price'] = df['price'].str.replace('£', '').str.replace('Â', '').astype(float)
    df.rename(columns={'price': 'price_gbp'}, inplace=True)

    # 3. 评分映射
    rating_map = {
        'One': 1, 'Two': 2, 'Three': 3, 'Four': 4, 'Five': 5
    }
    df['rating_score'] = df['rating'].map(rating_map)

    # 4. 添加爬取时间
    df['crawl_time'] = pd.Timestamp.now()

    # 5. 去除空值
    df = df.dropna(subset=['title', 'price_gbp'])

    print(f"清洗后数据量: {len(df)}")
    return df

# 清洗数据
df = clean_data(data)
print(df.describe())

六、保存到 Excel

6.1 基础保存

# 最简单的保存方式
df.to_excel('output/products.xlsx', index=False)
print("数据已保存到 output/products.xlsx")

6.2 格式化保存

使用 openpyxl 进行格式化:

from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils.dataframe import dataframe_to_rows

def save_to_excel(df, filepath):
    # 格式化保存到 Excel
    wb = Workbook()
    ws = wb.active
    ws.title = "商品数据"

    # 标题样式
    header_font = Font(name='微软雅黑', size=11, bold=True, color='FFFFFF')
    header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
    border = Border(
        left=Side(style='thin'),
        right=Side(style='thin'),
        top=Side(style='thin'),
        bottom=Side(style='thin')
    )

    # 写入数据
    for row in dataframe_to_rows(df, index=False, header=True):
        ws.append(row)

    # 格式化标题行
    for cell in ws[1]:
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = Alignment(horizontal='center')
        cell.border = border

    # 设置列宽
    ws.column_dimensions['A'].width = 50  # 标题列
    ws.column_dimensions['B'].width = 15  # 价格列
    ws.column_dimensions['C'].width = 12  # 评分列

    # 添加自动筛选
    ws.auto_filter.ref = ws.dimensions

    wb.save(filepath)
    print(f"格式化数据已保存到 {filepath}")

save_to_excel(df, 'output/products_formatted.xlsx')

七、新手注意事项

  1. 遵守 robots.txt:爬取前先查看目标网站的 robots.txt
  2. 控制请求频率:不要请求过快,尊重服务器
  3. 数据仅供学习:爬取的数据仅用于学习研究,不要商用
  4. 处理异常:网络请求可能失败,务必做好异常处理
  5. 合法合规:遵守相关法律法规,不要爬取敏感数据

八、完整流程总结

发送请求 → 获取HTML → 解析数据 → 数据清洗 → 保存Excel
   |           |          |          |          |
  设置UA    响应文本   提取字段   去重转换   格式化输出
  设置头    编码处理   结构化     空值处理   自动筛选
  代理池    超时控制   存储列表   类型转换   列宽设置

九、扩展建议

  • 使用 Scrapy 框架处理大型爬虫项目
  • 使用 SeleniumPlaywright 处理动态渲染页面
  • 使用 Redis 构建分布式爬虫队列
  • 使用数据库(MySQL/MongoDB)存储大量数据

如果这篇文章对你有帮助,欢迎点赞、收藏、关注! 有任何问题欢迎在评论区交流,我会及时回复。后续会分享更多 Python 爬虫实战教程,敬请期待!

作者:tlyyxjz | 转载请注明出处

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐