1. 项目背景与核心价值
爬取电子书畅销榜数据是许多数据分析师、产品经理和内容运营人员的刚需。得到App作为国内领先的知识服务平台,其电子书畅销榜反映了当前市场的热门阅读趋势和用户偏好。通过Python爬虫获取这些数据,我们可以实现:
- 竞品分析:了解竞争对手的电子书销售策略和定价规律
- 市场调研:掌握用户偏好的书籍类型和主题变化
- 内容推荐:为自有平台的书籍推荐系统提供数据支持
- 价格监控:跟踪电子书价格波动,把握最佳促销时机
这个项目特别适合:
- 刚学习Python爬虫的新手作为实战练习
- 需要获取电子书市场数据的内容运营人员
- 对知识付费领域感兴趣的数据分析师
2. 技术方案设计
2.1 整体架构设计
项目采用分层架构设计,各模块职责分明:
code复制数据采集层 → 数据处理层 → 数据存储层 → 数据分析层
关键组件选型:
- 请求库:Requests + Retrying(处理网络波动)
- 解析库:BeautifulSoup + lxml(HTML解析)
- 存储:MySQL + CSV双备份(防止数据丢失)
- 调度:APScheduler(定时任务管理)
2.2 反爬策略应对方案
得到App采用了多种反爬机制,我们的应对策略包括:
- 请求头伪装:完整模拟Chrome浏览器headers
- IP轮换:使用免费代理IP池(注意:绝对不要使用任何违规代理服务)
- 请求频率控制:随机延迟1-3秒 between requests
- Cookie更新:定期获取新的会话cookie
- 验证码识别:预留Tesseract OCR接口(备用方案)
重要提示:严格遵守robots.txt规则,设置合理的爬取间隔,避免对服务器造成负担
3. 核心实现步骤
3.1 环境准备与依赖安装
首先创建Python虚拟环境(推荐3.8+版本):
bash复制python -m venv ddbook_env
source ddbook_env/bin/activate # Linux/Mac
ddbook_env\Scripts\activate # Windows
安装必要依赖库:
bash复制pip install requests beautifulsoup4 lxml pandas pymysql apscheduler retrying
3.2 页面分析与接口抓取
通过浏览器开发者工具分析得到App的网页结构:
- 使用Chrome打开得到App网页版
- 按F12进入开发者工具
- 切换到Network选项卡
- 刷新页面并筛选XHR请求
关键发现:
- 畅销榜数据通过API接口返回(通常是JSON格式)
- 真实接口地址类似:
https://www.dedao.cn/api/v1/book/rank - 请求需要携带特定headers和cookie
3.3 核心爬取代码实现
python复制import requests
from retrying import retry
from bs4 import BeautifulSoup
import pandas as pd
import time
import random
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://www.dedao.cn/ebook/rank'
}
@retry(stop_max_attempt_number=3, wait_fixed=2000)
def fetch_rank_data(page=1):
url = f'https://www.dedao.cn/api/v1/book/rank?page={page}'
try:
resp = requests.get(url, headers=HEADERS, timeout=10)
resp.raise_for_status()
return resp.json()
except Exception as e:
print(f"请求失败: {e}")
return None
def parse_rank_data(json_data):
books = []
for item in json_data['data']['list']:
book = {
'rank': item['rank'],
'title': item['title'],
'author': item['author'],
'price': item['price'],
'original_price': item['original_price'],
'discount': item['discount'],
'rating': item['rating'],
'publish_date': item['publish_date']
}
books.append(book)
return books
3.4 数据存储实现
MySQL存储方案设计:
python复制import pymysql
from datetime import datetime
def save_to_mysql(books):
conn = pymysql.connect(
host='localhost',
user='root',
password='yourpassword',
database='book_rank'
)
with conn.cursor() as cursor:
sql = """INSERT INTO dedao_rank
(rank, title, author, price, original_price,
discount, rating, publish_date, crawl_time)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"""
for book in books:
cursor.execute(sql, (
book['rank'], book['title'], book['author'],
book['price'], book['original_price'],
book['discount'], book['rating'],
book['publish_date'], datetime.now()
))
conn.commit()
conn.close()
同时实现CSV备份:
python复制def save_to_csv(books, filename='dedao_rank.csv'):
df = pd.DataFrame(books)
df['crawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
try:
existing = pd.read_csv(filename)
updated = pd.concat([existing, df])
updated.to_csv(filename, index=False)
except FileNotFoundError:
df.to_csv(filename, index=False)
4. 高级技巧与优化方案
4.1 增量爬取策略
为避免重复爬取相同数据,实现增量爬取:
python复制def get_last_crawl_time():
"""获取上次爬取的最新记录时间"""
conn = pymysql.connect(...)
with conn.cursor() as cursor:
cursor.execute("SELECT MAX(publish_date) FROM dedao_rank")
return cursor.fetchone()[0]
def filter_new_books(books, last_date):
return [b for b in books if b['publish_date'] > last_date]
4.2 数据可视化方案
使用Matplotlib生成简单的趋势图:
python复制import matplotlib.pyplot as plt
def plot_price_distribution(df):
plt.figure(figsize=(10, 6))
df['price'].plot(kind='hist', bins=20)
plt.title('电子书价格分布')
plt.xlabel('价格(元)')
plt.ylabel('数量')
plt.grid(True)
plt.savefig('price_distribution.png')
4.3 异常处理增强
完善各种异常情况的处理:
python复制def safe_crawl():
try:
last_date = get_last_crawl_time() or '2000-01-01'
for page in range(1, 6): # 爬取前5页
data = fetch_rank_data(page)
if not data:
continue
books = parse_rank_data(data)
new_books = filter_new_books(books, last_date)
if new_books:
save_to_mysql(new_books)
save_to_csv(new_books)
time.sleep(random.uniform(1, 3))
except Exception as e:
print(f"爬取过程中出错: {e}")
# 发送邮件通知
send_alert_email(str(e))
5. 常见问题与解决方案
5.1 请求被拒绝(403错误)
可能原因及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 403 Forbidden | 请求头不完整 | 补全所有必要headers |
| 突然无法获取数据 | IP被封禁 | 更换IP或增加延迟 |
| 返回空白数据 | 需要登录 | 获取有效cookie |
5.2 数据解析失败
常见解析问题处理:
python复制def safe_parse(text):
try:
soup = BeautifulSoup(text, 'lxml')
# 尝试多种选择器
title = (soup.select_one('.title') or
soup.select_one('h1') or
soup.find('title')).get_text().strip()
return title
except Exception:
return '解析失败'
5.3 数据库连接问题
连接池优化方案:
python复制from DBUtils.PooledDB import PooledDB
pool = PooledDB(
creator=pymysql,
maxconnections=5,
host='localhost',
user='root',
password='yourpassword',
database='book_rank'
)
def get_conn():
return pool.connection()
6. 项目扩展方向
6.1 多平台数据对比
扩展爬取其他电子书平台数据:
python复制PLATFORMS = {
'dedao': 'https://www.dedao.cn/api/v1/book/rank',
'kindle': 'https://www.amazon.cn/gp/bestsellers/digital-text',
'wechat': 'https://weread.qq.com/web/rank'
}
def multi_platform_crawl():
results = {}
for name, url in PLATFORMS.items():
data = fetch_data(url)
results[name] = parse_data(data)
return results
6.2 自动化邮件报告
设置定时发送数据报告:
python复制import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
def send_report():
# 获取最新数据
df = pd.read_sql("SELECT * FROM dedao_rank ORDER BY crawl_time DESC LIMIT 10", conn)
# 创建邮件内容
msg = MIMEMultipart()
msg['Subject'] = '得到电子书畅销榜日报'
msg.attach(MIMEText(df.to_html(), 'html'))
# 发送邮件
with smtplib.SMTP('smtp.example.com', 587) as server:
server.login('user@example.com', 'password')
server.sendmail('user@example.com', 'receiver@example.com', msg.as_string())
6.3 价格监控预警
设置价格波动提醒:
python复制def check_price_drop():
sql = """
SELECT title, price, original_price
FROM dedao_rank
WHERE crawl_time > DATE_SUB(NOW(), INTERVAL 1 DAY)
AND price < 0.7 * original_price
"""
df = pd.read_sql(sql, conn)
if not df.empty:
send_alert("发现降价电子书", df.to_string())
在实际项目中,我建议每天定时爬取2-3次,避免给服务器造成过大压力。同时,保存历史数据时建议按日期分表,例如dedao_rank_20230801这样的表名格式,方便后续分析时间序列数据。
