You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编程新手请教:如何合理搭建Django网页爬虫可视化项目目录结构?

针对Django爬虫+可视化项目的目录结构建议与代码优化

Hey there! 你的思路其实已经很靠谱了——把爬虫、数据模型、可视化逻辑拆分到不同文件是Django项目解耦的关键,这能让代码更易维护和扩展。我来给你细化一下推荐的目录结构,再针对你当前的代码提些小建议:

推荐的项目目录结构(以你的league_app为例)

假设你的项目根目录是football_scraper,应用是league_app,推荐结构如下:

football_scraper/
├── league_app/
│   ├── __init__.py
│   ├── models.py          # 数据模型,存储爬取到的球员数据
│   ├── views.py           # 可视化视图,处理页面渲染、图表数据传递
│   ├── scrapers/          # 新建爬虫模块目录,把爬虫逻辑归类
│   │   ├── __init__.py
│   │   ├── player_scraper.py  # 专门处理球员数据爬取的代码(替代你想的scrape.py)
│   │   └── ...            # 如果有其他类型爬虫,比如球队数据,可新增文件
│   ├── templates/
│   │   └── league_app/
│   │       └── player.html
│   └── static/            # 存放可视化相关的JS/CSS(比如Chart.js)
│       └── league_app/
│           ├── js/
│           └── css/
└── manage.py

各模块作用说明

  • scrapers目录:把爬虫代码按功能拆分(比如球员、球队、赛事),比单个scrape.py更清晰,后续扩展新爬虫时不会让文件臃肿。你可以在player_scraper.py里写一个类或者函数来处理球员数据的爬取。
  • models.py:定义Player和PlayerStats这类模型,把爬取到的数据持久化到数据库,而不是每次请求都重复爬取(减少重复请求,提升性能,还能避免被目标网站封禁IP)。
  • views.py:专注于处理用户请求、从数据库获取数据、准备可视化需要的上下文,必要时调用爬虫更新数据,最后渲染模板。

你的views.py代码优化建议

先指出你当前代码里的几个小问题:

  1. 导入了BeautifulSoup但没有初始化soup对象
  2. 直接在视图里写爬虫逻辑会导致每次请求页面都爬取一次,效率极低
  3. 缺少异常处理,请求失败或找不到指定标签时会直接抛出错误

优化后的代码拆分示例

1. 把爬虫逻辑移到scrapers/player_scraper.py

# league_app/scrapers/player_scraper.py
import requests
from bs4 import BeautifulSoup

def get_player_stats(first_name, last_name):
    try:
        # 统一转大写适配目标网站URL规则
        last_name_first_letter = last_name[0].upper()
        last_name_first_four = last_name[:4].upper()
        first_name_first_two = first_name[:2].upper()
        url = f'https://www.pro-football-reference.com/players/{last_name_first_letter}/{last_name_first_four}{first_name_first_two}00.htm'
        
        # 加超时和请求头,避免被拦截
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
        page = requests.get(url, headers=headers, timeout=10)
        page.raise_for_status()  # 主动抛出HTTP错误
        
        soup = BeautifulSoup(page.content, 'html.parser')
        week_number = 251
        stats_row = soup.find("tr", {"id": f"stats.{week_number}"})
        
        if not stats_row:
            return None
        
        stats_relevant = ['pass_yds', 'pass_td', 'rush_yds']
        stat_list = []
        for stat in stats_relevant:
            stat_elem = stats_row.find("td", {"data-stat": stat})
            # 处理缺失数据的情况
            if stat_elem and stat_elem.text.strip():
                stat_list.append(int(stat_elem.text.strip()))
            else:
                stat_list.append(0)
        return stat_list
    except Exception as e:
        print(f"爬取错误: {str(e)}")
        return None

2. 在models.py里定义数据模型

# league_app/models.py
from django.db import models

class Player(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    created_at = models.DateTimeField(auto_now_add=True)

class PlayerStats(models.Model):
    player = models.ForeignKey(Player, on_delete=models.CASCADE)
    pass_yds = models.IntegerField()
    pass_td = models.IntegerField()
    rush_yds = models.IntegerField()
    week_number = models.IntegerField()
    updated_at = models.DateTimeField(auto_now=True)

3. 修改views.py,专注于视图逻辑

# league_app/views.py
from django.shortcuts import render
from .scrapers.player_scraper import get_player_stats
from .models import Player, PlayerStats

def PlayerDetailView(request):
    first_name = 'Drew'
    last_name = 'Brees'
    week_number = 251
    
    # 先尝试从数据库获取已存储的数据
    player, created = Player.objects.get_or_create(first_name=first_name, last_name=last_name)
    stats = PlayerStats.objects.filter(player=player, week_number=week_number).first()
    
    if not stats:
        # 数据库无数据时调用爬虫
        stat_list = get_player_stats(first_name, last_name)
        if stat_list:
            stats = PlayerStats.objects.create(
                player=player,
                pass_yds=stat_list[0],
                pass_td=stat_list[1],
                rush_yds=stat_list[2],
                week_number=week_number
            )
        else:
            stat_list = [0, 0, 0]  # 爬取失败时返回默认值
    else:
        # 从数据库读取数据
        stat_list = [stats.pass_yds, stats.pass_td, stats.rush_yds]
    
    context = {'player_name': f"{first_name} {last_name}", 'stats': stat_list}
    return render(request, 'league_app/player.html', context)

额外实用建议

  • 定时更新数据:如果需要定期刷新数据,可以用django-crontab或者Celery实现定时爬取任务,不要每次请求都触发爬虫。
  • 可视化实现:在模板player.html里可以用Chart.js这类轻量库,把stat_list传递给JS后生成柱状图或折线图。
  • IP封禁规避:给爬虫请求加随机延迟、使用代理池,避免频繁请求目标网站导致IP被封禁。

内容的提问来源于stack exchange,提问作者Jan F.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:18:21