Python爬虫实战:用requests库爬取电商数据并保存到Excel
·
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')
七、新手注意事项
- 遵守 robots.txt:爬取前先查看目标网站的 robots.txt
- 控制请求频率:不要请求过快,尊重服务器
- 数据仅供学习:爬取的数据仅用于学习研究,不要商用
- 处理异常:网络请求可能失败,务必做好异常处理
- 合法合规:遵守相关法律法规,不要爬取敏感数据
八、完整流程总结
发送请求 → 获取HTML → 解析数据 → 数据清洗 → 保存Excel
| | | | |
设置UA 响应文本 提取字段 去重转换 格式化输出
设置头 编码处理 结构化 空值处理 自动筛选
代理池 超时控制 存储列表 类型转换 列宽设置
九、扩展建议
- 使用
Scrapy框架处理大型爬虫项目 - 使用
Selenium或Playwright处理动态渲染页面 - 使用
Redis构建分布式爬虫队列 - 使用数据库(MySQL/MongoDB)存储大量数据
如果这篇文章对你有帮助,欢迎点赞、收藏、关注! 有任何问题欢迎在评论区交流,我会及时回复。后续会分享更多 Python 爬虫实战教程,敬请期待!
作者:tlyyxjz | 转载请注明出处
更多推荐




所有评论(0)