1. 项目概述:Python爬虫构建个人数据管理工具
这个项目源于一个常见的个人数据管理痛点:我们每天在各种平台产生的订单、物流、投资等数据分散在不同系统中,难以统一管理和分析。作为一名长期使用Python处理数据的开发者,我决定打造一个自动化资产清单工具,将散落在各处的"订单/投递"类数据集中管理。
这个工具的核心功能是通过Python爬虫技术抓取目标平台数据,然后提供两种持久化存储方案:CSV导出适合快速查看和简单分析,SQLite数据库则便于复杂查询和长期积累。相比手动导出或依赖平台提供的有限历史记录,这套方案能实现:
- 自动化定时抓取,避免遗漏
- 统一数据格式,消除平台差异
- 完整历史存档,支持任意时间范围回溯
- 本地化存储,保障数据隐私
从技术栈看,项目涉及:
- 爬虫基础(requests/BeautifulSoup)
- 反爬应对策略(UserAgent轮换、请求间隔)
- 数据清洗(pandas)
- 存储方案(CSV/SQLite)
- 异常处理(重试机制、日志记录)
提示:虽然项目以"个人数据"为场景,但相同技术可应用于小型企业资产管理、电商店铺订单归档等场景,只需调整爬取目标和字段映射。
2. 技术选型与核心组件解析
2.1 爬虫框架选择:轻量级 vs Scrapy
对于这种定向抓取固定几个页面的场景,我放弃了重量级的Scrapy框架,选择requests+BeautifulSoup组合。原因有三:
- 目标明确:只需抓取登录后的"我的订单"等有限页面,不需要爬取整站
- 开发效率:Scrapy的学习曲线和项目配置成本对于简单任务过高
- 维护成本:轻量级方案更易于添加平台特定的反爬逻辑
核心组件版本:
python复制requests==2.31.0 # HTTP请求
beautifulsoup4==4.12.0 # HTML解析
pandas==2.0.3 # 数据清洗与CSV导出
sqlite3==3.41.2 # 内置数据库支持
2.2 数据存储方案对比
| 存储方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| CSV | 无需环境依赖,Excel可直接打开 | 无数据类型校验,重复写入效率低 | 快速查看、简单分析 |
| SQLite | 支持SQL查询,数据完整性好 | 需要专用工具查看 | 长期积累、复杂分析 |
实际项目中我同时实现了两种方案:
- CSV作为即时查看的"热数据"出口
- SQLite作为长期存储的"冷数据"仓库
2.3 反爬策略设计
针对目标平台的常见防护措施,实现了三级防御:
- 基础伪装:随机UserAgent+Referer
python复制headers = {
'User-Agent': random.choice(user_agents),
'Referer': 'https://www.target-site.com/account'
}
- 请求节制:固定间隔+随机延迟
python复制time.sleep(1 + random.random()) # 1-2秒间隔
- 异常处理:状态码检测+HTML特征验证
python复制if '验证码' in response.text:
raise CaptchaException('触发验证码')
3. 核心实现步骤详解
3.1 登录会话保持
大多数个人中心需要登录,这里采用requests.Session()维持会话:
python复制def login(username, password):
session = requests.Session()
login_data = {
'username': username,
'password': password,
'token': get_dynamic_token()
}
resp = session.post(LOGIN_URL, data=login_data)
resp.raise_for_status()
return session # 返回已登录的session
注意:遇到验证码时可考虑:
- 手动输入(适合低频操作)
- 接入打码平台API(需成本)
- 降低请求频率(最经济方案)
3.2 订单数据抓取与解析
以电商平台订单为例,典型解析流程:
python复制def parse_orders(session):
orders = []
page = 1
while True:
url = f"{ORDER_URL}?page={page}"
html = session.get(url).text
soup = BeautifulSoup(html, 'html.parser')
items = soup.select('.order-item') # CSS选择器定位订单项
if not items:
break
for item in items:
orders.append({
'order_id': item.select_one('.order-id').text.strip(),
'date': parse_date(item.select_one('.date').text),
'amount': float(item.select_one('.amount').text[1:]),
'status': item.select_one('.status').text
})
page += 1
return pd.DataFrame(orders)
常见问题处理:
- 动态加载:改用Selenium或分析XHR请求
- 数据在图片中:OCR识别或放弃该字段
- 分页异常:增加最大页数限制
3.3 数据存储实现
CSV导出方案
python复制def save_to_csv(df, filename):
# 如果文件存在则追加,否则创建
mode = 'a' if os.path.exists(filename) else 'w'
header = False if mode == 'a' else True
df.to_csv(filename, mode=mode, header=header, index=False)
SQLite存储方案
python复制def init_db(db_path='orders.db'):
conn = sqlite3.connect(db_path)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS orders
(id TEXT PRIMARY KEY, date TEXT, amount REAL, status TEXT)''')
conn.commit()
return conn
def save_to_db(conn, df):
df.to_sql('orders', conn, if_exists='append', index=False)
4. 项目进阶与优化方向
4.1 定时自动化执行
使用APScheduler实现定时任务:
python复制from apscheduler.schedulers.blocking import BlockingScheduler
def job():
session = login(USERNAME, PASSWORD)
df = parse_orders(session)
save_to_csv(df, 'orders.csv')
save_to_db(conn, df)
scheduler = BlockingScheduler()
scheduler.add_job(job, 'interval', hours=6) # 每6小时执行一次
scheduler.start()
4.2 可视化监控
利用pandas+matplotlib生成简单报表:
python复制def generate_report(df):
# 按月统计订单量
monthly = df.groupby(df['date'].dt.to_period('M')).size()
monthly.plot(kind='bar', title='Monthly Orders')
plt.savefig('report.png')
4.3 异常通知机制
集成邮件通知异常情况:
python复制def send_alert(error):
import smtplib
msg = f"Subject: 爬虫异常\n\n{str(error)}"
smtp = smtplib.SMTP('smtp.example.com')
smtp.sendmail('from@example.com', 'to@example.com', msg)
5. 常见问题与解决方案
5.1 登录失效问题
现象:运行一段时间后突然无法获取数据
排查:
- 检查session有效期(有些平台会定时过期)
- 查看返回内容是否包含登录跳转
解决方案:
python复制def safe_get(session, url, retry=3):
for _ in range(retry):
resp = session.get(url)
if 'login' in resp.url: # 被跳转到登录页
session = login(USERNAME, PASSWORD) # 重新登录
continue
return resp
raise Exception("登录失效")
5.2 数据重复存储
现象:同一条订单被多次记录
解决方案:
- SQLite使用PRIMARY KEY或UNIQUE约束
sql复制CREATE TABLE orders (
order_id TEXT PRIMARY KEY,
...
);
- 插入前检查存在性
python复制def exists_in_db(conn, order_id):
c = conn.cursor()
c.execute("SELECT 1 FROM orders WHERE id=?", (order_id,))
return c.fetchone() is not None
5.3 字段类型转换异常
现象:金额、日期等字段格式不一致
处理方案:
python复制def safe_convert(value, type_func, default=None):
try:
return type_func(value)
except (ValueError, TypeError):
return default
# 使用示例
amount = safe_convert(price_text[1:], float, 0.0)
date = safe_convert(date_text, parse_date, datetime.now())
6. 项目扩展思路
6.1 多平台支持
通过抽象基类实现可扩展架构:
python复制class PlatformSpider(ABC):
@abstractmethod
def login(self): pass
@abstractmethod
def fetch_orders(self): pass
class AmazonSpider(PlatformSpider):
def login(self):
# 亚马逊特有登录逻辑
pass
def fetch_orders(self):
# 亚马逊订单解析
pass
6.2 数据聚合分析
跨平台数据统一分析:
python复制def analyze_spending(dfs):
all_orders = pd.concat(dfs)
by_month = all_orders.groupby(
[all_orders['platform'], all_orders['date'].dt.to_period('M')]
)['amount'].sum()
return by_month.unstack('platform').plot(kind='bar')
6.3 移动端适配
使用Flask构建简单API:
python复制from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/orders')
def get_orders():
conn = sqlite3.connect('orders.db')
df = pd.read_sql("SELECT * FROM orders", conn)
return jsonify(df.to_dict('records'))
这个项目从最初简单的脚本已经逐步发展成一个完整的个人数据管理系统。在实际开发中,最大的教训是:不要试图一次性完美解决所有问题。我的建议实施路径是:
- 先实现核心平台的基础抓取
- 建立可靠的数据存储机制
- 逐步添加异常处理和监控
- 最后考虑扩展性和可视化
对于想尝试类似项目的开发者,可以从最简单的单平台CSV导出开始,逐步迭代完善。完整项目代码我已整理在GitHub仓库中(需替换为实际地址),包含详细注释和示例数据。
