这个基于Django框架的Python爬虫项目,本质上是一个网络小说领域的垂直数据分析系统。它通过自动化采集主流小说平台的公开数据,结合多维度的热度计算模型,为网络文学研究者、网文作者、平台运营方提供数据驱动的决策支持。我在实际开发中发现,这类系统在网文IP孵化、题材趋势预测、读者行为分析等领域有着广泛的应用场景。
项目的技术栈选择非常典型:Django作为后端框架提供稳定的数据管理和API服务,Scrapy或Requests+BeautifulSoup作为爬虫工具链,配合Pandas+Numpy进行数据清洗和分析,最后用ECharts或Pyecharts实现可视化。这种组合兼顾了开发效率和系统性能,特别适合中小规模的数据采集分析场景。
选择Django而非Flask的主要考虑是其自带的管理后台(Admin)和ORM系统。对于需要频繁进行数据CRUD操作的热度分析系统,Django Admin可以快速生成数据管理界面,省去大量前端开发工作。实测中,用Django Admin配合simpleui主题库,3天内就能搭建出功能完善的后台管理系统。
爬虫部分采用Scrapy-Redis分布式架构而非单机爬虫,这是考虑到小说平台的反爬机制。通过动态User-Agent池、IP代理中间件和随机请求间隔(建议设置在3-5秒)的组合策略,我们的测试爬虫在起点中文网连续运行72小时未被封禁。具体配置示例:
python复制# settings.py关键配置
DOWNLOAD_DELAY = random.uniform(3, 5)
ROBOTSTXT_OBEY = False
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'scrapy_user_agents.middlewares.RandomUserAgentMiddleware': 400,
'scrapy_proxy_pool.middlewares.ProxyPoolMiddleware': 610,
'scrapy_proxy_pool.middlewares.BanDetectionMiddleware': 620,
}
热度分析的核心在于指标体系的建立。我们设计了四级指标体系:
对应的Django模型关键字段设计:
python复制class Novel(models.Model):
platform = models.CharField(max_length=20) # 平台来源
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
category = models.CharField(max_length=20) # 题材分类
word_count = models.IntegerField()
is_vip = models.BooleanField()
class NovelMetrics(models.Model):
novel = models.ForeignKey(Novel, on_delete=models.CASCADE)
crawl_time = models.DateTimeField(auto_now_add=True)
clicks = models.IntegerField()
collections = models.IntegerField()
# 其他20+个指标字段...
heat_score = models.FloatField() # 最终热度值
小说平台的反爬策略通常包括:
我们的解决方案是:
一个典型的API逆向案例:
python复制def generate_chapter_signature(chapter_id):
"""逆向得到的起点章节内容签名算法"""
secret = 'xxxxxx' # 通过JS逆向获取的密钥
timestamp = int(time.time())
raw = f"{chapter_id}-{timestamp}-{secret}"
return hashlib.md5(raw.encode()).hexdigest()
原始爬取数据需要经过严格清洗:
清洗流程的Pandas实现示例:
python复制def clean_novel_data(raw_df):
# 万/亿单位转换
df = raw_df.copy()
df['clicks'] = df['clicks'].apply(lambda x: float(x[:-1])*10000 if '万' in x else x)
# 去除机器人刷榜数据
q1 = df['collections'].quantile(0.25)
q3 = df['collections'].quantile(0.75)
iqr = q3 - q1
df = df[~((df['collections'] < (q1 - 1.5*iqr)) |
(df['collections'] > (q3 + 1.5*iqr)))]
# 作者信息补全
author_map = df.groupby('author_id')['author'].first().to_dict()
df['author'] = df['author_id'].map(author_map)
return df
采用熵权法客观确定各指标权重,避免主观偏差。核心步骤:
数据标准化:Min-Max归一化处理
$$ x_{ij}' = \frac{x_{ij} - min(x_j)}{max(x_j) - min(x_j)} $$
计算信息熵:
$$ e_j = -\frac{1}{\ln n} \sum_{i=1}^n p_{ij} \ln p_{ij} $$
其中 $p_{ij} = x_{ij}' / \sum_{i=1}^n x_{ij}'$
确定权重:
$$ w_j = \frac{1 - e_j}{\sum_{k=1}^m (1 - e_k)} $$
Python实现代码:
python复制from sklearn.preprocessing import MinMaxScaler
import numpy as np
def entropy_weight(data):
# data: DataFrame 每列为一个指标
scaler = MinMaxScaler()
X = scaler.fit_transform(data)
# 避免log(0)
X = np.where(X == 0, 1e-10, X)
# 计算概率矩阵
P = X / X.sum(axis=0)
# 计算信息熵
k = 1 / np.log(data.shape[0])
e = -k * (P * np.log(P)).sum(axis=0)
# 计算权重
w = (1 - e) / (1 - e).sum()
return w
为反映最新趋势,我们设计时间衰减因子:
$$ \alpha_t = e^{-\lambda t} $$
其中λ取0.1(半衰期约7天),t为数据时间间隔(天)
最终热度计算公式:
$$ HeatScore = \alpha_t \cdot \sum_{j=1}^m w_j x_{ij}' $$
通过django-import-export库实现数据导入导出,配合django-admin-charts添加趋势图表:
python复制# admin.py配置示例
from import_export import resources
from admincharts.admin import AdminChartMixin
class NovelResource(resources.ModelResource):
class Meta:
model = Novel
@admin.register(NovelMetrics)
class NovelMetricsAdmin(AdminChartMixin, admin.ModelAdmin):
resource_class = NovelResource
list_display = ('novel', 'heat_score', 'crawl_time')
def get_chart_options(self, request):
return {
'heat_trend': {
'title': '热度趋势',
'chart_type': 'line',
'queryset': self.get_queryset(request),
'fields': ['heat_score'],
'date_field': 'crawl_time',
'group_by': 'day'
}
}
使用Pyecharts生成交互式图表,关键配置项:
python复制from pyecharts import options as opts
from pyecharts.charts import Bar, Timeline
def create_heat_timeline(data):
tl = Timeline()
for day in sorted(data['date'].unique()):
day_data = data[data['date'] == day]
bar = (
Bar()
.add_xaxis(day_data['title'].tolist())
.add_yaxis("热度值", day_data['heat_score'].round(2).tolist())
.set_global_opts(
title_opts=opts.TitleOpts(title=f"小说热度榜 {day}"),
visualmap_opts=opts.VisualMapOpts(
min_=0, max_=100, dimension=1
)
)
)
tl.add(bar, day)
return tl
推荐使用Docker-Compose编排服务:
yaml复制version: '3'
services:
web:
build: .
command: gunicorn novel_analysis.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- redis
- postgres
spider:
build: .
command: scrapy crawl qidian
volumes:
- .:/code
depends_on:
- redis
redis:
image: redis:alpine
ports:
- "6379:6379"
postgres:
image: postgres:13
environment:
POSTGRES_PASSWORD: example
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
数据库查询优化:
python复制metrics = NovelMetrics.objects.select_related('novel')\
.filter(crawl_time__gte=timezone.now()-timedelta(days=30))\
.prefetch_related('novel__category')
缓存策略:
爬虫优化:
Q:爬取频率设置多少合适?
A:建议:
Q:遇到验证码怎么办?
A:分级处理方案:
Q:热度值波动过大如何解决?
A:处理方法:
Q:不同平台数据如何标准化比较?
A:采用分位数归一化:
python复制def quantile_normalize(df):
rank_mean = df.stack().groupby(df.rank(method='first').stack().astype(int)).mean()
return df.rank(method='min').stack().astype(int).map(rank_mean).unstack()
读者画像分析:结合书评情感分析构建读者画像
python复制from snownlp import SnowNLP
def analyze_comment_sentiment(text):
return SnowNLP(text).sentiments
题材流行度预测:使用LSTM模型预测未来3个月题材趋势
python复制from keras.models import Sequential
from keras.layers import LSTM, Dense
model = Sequential([
LSTM(64, input_shape=(30, len(features))),
Dense(1, activation='sigmoid')
])
跨平台热度对比:建立平台间热度换算公式
$$ Heat_{A} = \alpha Heat_{B} + \beta $$
在实际开发中,我发现这类系统的商业价值往往体现在三个方面:为网文作者提供选题参考、帮助平台发现潜力作品、辅助版权采购决策。一个值得分享的经验是:在数据采集阶段就要考虑后续的分析需求,比如我们后来增加的"章节完读率"指标,就需要在爬虫阶段就记录每章的阅读进度数据。