食鲜配·智厨 :数据采集项目实践

食鲜配·智厨 :数据采集项目实践

综合设计——多源异构数据采集与融合应用综合实践

这个项目属于哪个课程 https://edu.cnblogs.com/campus/fzu/2025DataCollectionandFusiontechnology
组名 风雨无组
项目名字及简介 食鲜配·智厨是一个基于Python 3.7+和Django 2.0.7的智能电商菜谱一体化平台,实现了"购物-菜谱-健康"的完整闭环,为用户提供"买什么-怎么做-吃得健康"一站式解决方案。
项目logo 341db5339e9873a3c2392516989b27d6_720
团队成员学号 102302101、102302102、102302103、102302104、102302105、102302106、102302109、102302110
项目目标 实现网页端多源异构数据采集,构建用户画像并完成食材与菜谱的智能推荐,实现 AI 分析与 Web 应用展示,形成完整的数据采集与融合闭环。
其他参考文献 https://github.com/CRIPAC-DIG/SR-GNN
gitee仓库地址 https://gitee.com/ding41/buy-menu

系统架构设计

系统整体采用 B/S 架构,构建如下闭环流程:
多源数据采集 → 数据分析与融合 → 推荐算法 → Web系统展示 → 用户交互 → 行为反馈

1. 技术选型说明

模块 技术
后端 Python、Django
前端 HTML / CSS / JavaScript
数据库 SQLite
数据分析 Pandas、文本分析
推荐逻辑 标签匹配 + 点击量排序
AI功能 通用大模型 + 自定义逻辑
部署 云服务器环境

核心功能设计与实现

后台管理系统

商品全生命周期管理
菜谱管理与食材绑定
用户行为数据可视化

前台核心功能

(1)商品浏览与购物车
分类浏览、关键词搜索
分量与处理方式选择
购物车实时价格更新
(2)菜谱推荐与双向推荐算法
商品 → 菜谱推荐:基于商品标签匹配菜谱食材
菜谱 → 商品推荐:基于菜谱所需食材匹配商品
支持“一键加购”
(3)AI 智能分析与个性化模块
食材搭配合理性分析
健康饮食偏好设置(低糖/低盐等)
AI 助手提供烹饪与处理建议

个人任务概述

本项目是一个围绕数据采集和应用展开的个人项目任务分工,主要包含五个核心任务:

  1. 开发菜谱视频爬虫
  2. 搭建AI聊天助手
  3. 设计后台管理原型
  4. 设计优化前端登录注册页
  5. 搭建前后端页面搜索逻辑

本文将详细介绍这些核心任务的实现过程、关键技术和代码细节。

技术栈

类别 技术
后端框架 Django
前端技术 HTML5, CSS3, JavaScript, jQuery
数据处理 Pandas
网络请求 Requests
AI服务 DeepSeek API
数据存储 SQLite (开发环境), MySQL (生产环境)
开发工具 PyCharm, VS Code

核心任务实现

1. 开发菜谱视频爬虫

菜谱视频爬虫是本项目的核心功能之一,用于从B站自动搜索并匹配与菜谱相关的视频内容。

核心功能

  • 从CSV文件读取菜谱数据
  • 智能提取菜谱关键词
  • 调用B站API搜索相关视频
  • 将匹配的视频链接保存回CSV文件

完整代码实现

import pandas as pd
import requests
import time
import urllib.parse
import logging
import re

# --- 配置日志 ---  # 设置日志记录,便于调试和监控
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# --- 全局设置 ---  # 定义输入输出文件和请求头
INPUT_CSV = '家常菜.csv' # 输入CSV文件路径
OUTPUT_CSV = '家常菜_updated.csv' # 输出CSV文件路径
HEADERS = { # 请求头配置,模拟浏览器行为
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'Referer': 'https://www.bilibili.com/'
}

def clean_text_for_search(title, desc, steptext):
    """
    终极版三级智能清洗函数,依次分析 title, desc, steptext。
    """
    # 定义停用词列表,用于过滤无意义的词汇
    stop_words = {
        '的', '地', '了', '吧', '吗', '啊', '!', '!', '?', '?', '༄',
        '家常菜', '下饭菜', '快手', '美味', '好吃', '简单', '学会',
        '分钟', '一道', '这个', '教程', '做法', '菜谱', '食谱', '好菜'
    }

    def get_clean_query_from_text(text):
        """辅助函数,用于从单段文本中提取关键词"""
        if not isinstance(text, str) or not text: # 检查文本是否有效
            return ""
        # 移除所有非中文字符、字母和数字,以及数字编号
        cleaned_text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z]', ' ', text)
        cleaned_text = re.sub(r'\b\d+\b', '', cleaned_text) # 移除独立的数字
        word_list = cleaned_text.split() # 分割文本为单词列表
        filtered_words = [word for word in word_list if word not in stop_words] # 过滤停用词
        query = "".join(filtered_words) # 合并为查询字符串
        return query.strip() # 去除首尾空格

    # --- 第一级:分析标题 ---  # 优先从标题提取关键词
    query = ""
    if title and title not in ["家常菜"]: # 检查标题是否有效
        # 移除所有括号和标签及其内容
        processed_title = re.sub(r'[\[【《##·|].*?[\]】》·|]', '', title) # 移除方括号、书名号等
        processed_title = re.sub(r'(.*?)', '', processed_title) # 移除中文括号
        processed_title = re.sub(r'\(.*?\)', '', processed_title) # 移除英文括号
        query = get_clean_query_from_text(processed_title) # 提取关键词

    # --- 第二级:如果标题无效,分析描述 ---  # 标题提取失败则从描述提取
    if len(query) < 2: # 检查关键词长度是否足够
        logging.info(f"标题 '{title[:20]}...' 无效,尝试从描述中提取...")
        first_sentence_desc = re.split(r'[。,!]', desc)[0] if isinstance(desc, str) else "" # 取描述的第一句话
        query = get_clean_query_from_text(first_sentence_desc) # 提取关键词

    # --- 第三级:如果描述也无效,分析步骤 ---  # 描述提取失败则从步骤提取
    if len(query) < 2: # 检查关键词长度是否足够
        logging.info(f"描述 '{str(desc)[:20]}...' 无效,尝试从步骤中提取...")
        # 从步骤文本中提取,移除引导性的数字和符号
        cleaned_step = re.sub(r'^\d*[️⃣#\s]*', '', steptext) if isinstance(steptext, str) else ""
        first_part_step = re.split(r'[。,!#]', cleaned_step)[0] # 取步骤的第一部分
        query = get_clean_query_from_text(first_part_step) # 提取关键词
    
    # 最后过滤一次通用词
    if query == "家常":
        return ""
        
    return query

# B站视频搜索函数
def search_bilibili_video(query, session):
    if not query: # 检查查询词是否有效
        logging.warning("清洗后的关键词为空,跳过搜索。")
        return None
    
    api_url = "https://api.bilibili.com/x/web-interface/search/type" # B站搜索API
    params = {'search_type': 'video', 'keyword': query, 'order': 'totalrank', 'page': 1} # 搜索参数
    
    try:
        logging.info(f"正在通过API搜索清洗后的关键词: '{query}'")
        response = session.get(api_url, params=params, timeout=15) # 发送请求
        response.raise_for_status() # 检查请求是否成功
        data = response.json() # 解析响应数据
        
        if data.get('code') == 0: # 检查API返回状态
            video_list = data.get('data', {}).get('result', []) # 获取视频列表
            if video_list: # 检查视频列表是否为空
                video_url = video_list[0].get('arcurl') # 获取第一个视频的URL
                if video_url: # 检查URL是否有效
                    clean_url = video_url.split('?')[0] # 去除URL参数
                    logging.info(f"成功找到视频链接: {clean_url}")
                    return clean_url
            else:
                logging.warning(f"API为 '{query}' 返回了结果,但视频列表为空。")
        else:
            logging.warning(f"API返回错误: {data.get('message')}")
            
        return None
    
    except Exception as e: # 捕获异常
        logging.error(f"处理 '{query}' 时发生错误: {e}")
        return None

# 主程序入口
def main():
    try:
        df = pd.read_csv(INPUT_CSV, encoding='utf-8').fillna('') # 读取CSV文件,填充空值
    except FileNotFoundError:
        logging.error(f"错误:找不到文件 '{INPUT_CSV}'。")
        return
    
    if 'videourl' not in df.columns: # 检查是否有视频URL列
        df['videourl'] = '' # 添加视频URL列
    
    with requests.Session() as session: # 创建会话对象,保持连接
        session.headers.update(HEADERS) # 设置请求头
        
        try:
            logging.info("正在初始化会话并获取B站Cookie...")
            session.get("https://www.bilibili.com", timeout=15) # 获取B站Cookie
            logging.info("Cookie获取成功,开始处理任务。")
        except requests.exceptions.RequestException as e:
            logging.error(f"初始化会话失败: {e}")
            return
        
        # 遍历CSV文件中的每一行
        for index, row in df.iterrows():
            if row.get('videourl', ''): # 检查是否已经有视频URL
                continue # 跳过已处理的行
            
            # <<< 核心修改:将三个字段都传入分析引擎 >>>
            search_query = clean_text_for_search(row['title'], row['desc'], row['steptext']) # 提取关键词
            
            video_url = search_bilibili_video(search_query, session) # 搜索视频
            
            if video_url: # 检查是否找到视频
                df.loc[index, 'videourl'] = video_url # 保存视频URL
            
            time.sleep(1) # 设置请求间隔,避免触发反爬机制
            logging.info("等待 1 秒后继续...")
            
    df.to_csv(OUTPUT_CSV, index=False, encoding='utf-8-sig') # 保存结果到CSV文件
    logging.info(f"处理完成!结果已保存到 '{OUTPUT_CSV}'。")

# 程序入口
if __name__ == '__main__':
    main()

技术亮点

  1. 三级智能关键词提取机制

    • 优先从标题提取关键词,标题最能反映菜谱的核心信息
    • 标题提取失败则从描述中提取第一句话
    • 描述提取失败最后从步骤文本中提取
  2. 多维度文本清洗

    • 移除括号、标签、数字编号等无关信息
    • 过滤常见的无意义词汇(停用词)
    • 处理空值和异常情况,确保程序稳健运行
  3. 完善的反爬策略

    • 使用Session对象保持会话状态,提高请求成功率
    • 自动获取并携带B站Cookie
    • 设置合理的请求间隔(1秒),避免触发B站的反爬机制
  4. 详细的日志记录

    • 记录程序运行状态和关键操作
    • 便于调试和监控程序运行情况

2. 搭建AI聊天助手

AI聊天助手是基于DeepSeek API实现的智能对话系统,提供菜谱推荐、烹饪技巧等服务。

核心功能

  • 流式响应,提升用户体验
  • 聊天记录保存功能
  • 响应式UI设计
  • 安全的API Key管理

技术实现

AI聊天助手主要通过static/js/ai-chatbot/chatbot.js文件实现,核心功能包括:

  1. 流式响应处理:实时展示AI回复,避免用户长时间等待
  2. 聊天记录管理:使用localStorage保存最近20条聊天记录
  3. 响应式UI:适配不同屏幕尺寸,提供良好的移动端体验
  4. 安全机制:支持从localStorage读取API Key,避免硬编码

关键代码片段

// AI聊天助手核心代码片段
async function callAI(prompt) {
    if (!API_KEY || !API_KEY.startsWith("sk-")) {
        addMessage("抱歉,主人还没给我配置正确的 DeepSeek API Key 哦。", 'ai'); return;
    }

    // 创建一个新的、空的AI消息气泡,准备接收数据流
    const aiMessageElement = addMessage("", 'ai');
    const bubble = aiMessageElement.querySelector('.message-bubble');
    bubble.classList.add('is-thinking'); // 添加闪烁光标效果

    try {
        const response = await fetch("https://api.deepseek.com/chat/completions", {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_KEY}` },
            body: JSON.stringify({ 
                model: "deepseek-chat", 
                messages: [{ role: "user", content: prompt }],
                stream: true // 关键:开启流式传输
            })
        });

        if (!response.ok) { throw new Error(`API 请求失败: ${response.statusText}`); }
        
        const reader = response.body.getReader();
        const decoder = new TextDecoder("utf-8");
        let fullResponse = "";

        // 循环读取数据流
        while (true) {
            const { done, value } = await reader.read();
            if (done) break; // 数据流结束

            const chunk = decoder.decode(value);
            const lines = chunk.split("\n").filter(line => line.trim().startsWith("data: "));

            for (const line of lines) {
                const jsonStr = line.replace("data: ", "");
                if (jsonStr === "[DONE]") { // DeepSeek的结束标志
                    break;
                }
                try {
                    const parsed = JSON.parse(jsonStr);
                    const content = parsed.choices[0].delta.content || "";
                    if (content) {
                        fullResponse += content;
                        // 将Markdown实时渲染并更新到气泡中
                        bubble.innerHTML = marked.parse(fullResponse);
                        messagesContainer.scrollTop = messagesContainer.scrollHeight; // 实时滚动到底部
                    }
                } catch (error) {
                    // 忽略解析错误,继续处理下一行
                }
            }
        }
        
    } catch (error) {
        console.error("调用 DeepSeek 流式 API 时出错:", error);
        bubble.innerHTML = `抱歉,我好像遇到了一点问题:${error.message}`;
    } finally {
        // 请求结束后,移除思考中的光标效果
        bubble.classList.remove('is-thinking');
    }
}

image

3. 设计后台管理原型

后台管理原型设计用于管理菜谱数据、用户信息和系统配置。

核心功能

  • 菜谱数据管理(增删改查)
  • 用户信息管理
  • 系统配置

4. 设计优化前端登录注册页

前端登录注册页是用户与系统交互的入口,提供了良好的用户体验和安全保障。

核心功能

  • 用户登录
  • 用户注册
  • 表单验证
  • 密码安全

技术实现

登录页面设计

<!-- 登录页面核心结构 -->
<div class="login_form_bg">
    <div class="login_form_wrap clearfix">
        <div class="login_banner fl">
            <div class="slogan">日夜兼程 · 急速送达</div>
            <a href="{% url 'df_goods:index' %}">
                <img src="/static/images/logo02.png" alt="logo">
            </a>
        </div>
        <div class="login_form fr">
            <div class="login_title clearfix">
                <h1>用户登录</h1>
                <a href="{% url "df_user:register" %}">立即注册</a>
            </div>
            <div class="form_input">
                <form action="{% url "df_user:login_handle" %}" method="post">
                    {% csrf_token %}
                    <input type="text" name="username" class="name_input" value="{{ username }}" placeholder="请输入用户名">
                    <div class="user_error">输入错误</div>
                    <input type="password" name="pwd" class="pass_input" value="{{ password }}" placeholder="请输入密码">
                    <div class="pwd_error">输入错误</div>
                    <div class="more_input clearfix">
                        <input type="checkbox" name="jizhu" value="1" checked="checked">
                        <label>记住用户名</label>
                        <a href="#">忘记密码</a>
                    </div>
                    <input type="submit" name="" value="登录" class="input_submit">
                </form>
            </div>
        </div>
    </div>
</div>

注册页面设计

<!-- 注册页面核心结构 -->
<div class="register_con">
    <div class="l_con fl">
        <div class="reg_slogan">足不出户  ·  新鲜每一天</div>
        <a href="/" class="reg_logo"><img src="/static/images/logo02.png"></a>
    </div>

    <div class="r_con fr">
        <div class="reg_title clearfix">
            <h1>用户注册</h1>
            <a href="{% url "df_user:login" %}">登录</a>
        </div>
        <div class="reg_form clearfix">
            <form action="{% url "df_user:register_handle" %}" id='reg_form' method="post">
                {% csrf_token %}
                <ul>
                    <li>
                        <label>用户名:</label>
                        <input type="text" name="user_name" id="user_name">
                        <span class="error_tip">提示信息</span>
                    </li>
                    <li>
                        <label>密码:</label>
                        <input type="password" name="pwd" id="pwd">
                        <span class="error_tip">提示信息</span>
                    </li>
                    <li>
                        <label>确认密码:</label>
                        <input type="password" name="confirm_pwd" id="cpwd">
                        <span class="error_tip">提示信息</span>
                    </li>
                    <li>
                        <label>邮箱:</label>
                        <input type="text" name="email" id="email">
                        <span class="error_tip">提示信息</span>
                    </li>
                    <li class="agreement">
                        <input type="checkbox" name="allow" id="allow" checked="checked">
                        <label>同意《天天生鲜用户使用协议》</label>
                        <span class="error_tip2">提示信息</span>
                    </li>
                    <li class="reg_sub">
                        <input type="submit" value="注 册">
                    </li>
                </ul>                
            </form>
        </div>
    </div>
</div>

技术亮点

  1. 现代化UI设计

    • 简洁美观的界面设计
    • 统一的色彩搭配和视觉风格
    • 清晰的信息层级
  2. 实时表单验证

    • 前端实时验证用户输入
    • 提供即时反馈和错误提示
    • 减少用户等待时间
  3. 响应式布局

    • 适配不同设备和屏幕尺寸
    • 提供一致的用户体验
  4. 安全保障

    • 使用CSRF令牌防止跨站请求伪造
    • 密码加密存储
    • 输入验证和过滤
      image
      image

5. 搭建前后端页面搜索逻辑

前后端页面搜索逻辑实现了商品的多维度搜索功能,支持标题、内容、简介等字段的模糊查询。

核心功能

  • 多字段模糊搜索
  • 搜索结果分页
  • 热门商品推荐
  • 搜索状态提示

技术实现

后端搜索逻辑

# views.py中的搜索功能实现
def ordinary_search(request):
    """通用搜索视图"""
    search_keywords = request.GET.get('q', '') # 获取搜索关键词
    pindex = request.GET.get('pindex', 1) # 获取当前页码
    
    # 使用Q对象进行多字段模糊查询
    goods_list = GoodsInfo.objects.filter(
        Q(gtitle__icontains=search_keywords) | # 标题模糊查询
        Q(gcontent__icontains=search_keywords) | # 内容模糊查询
        Q(gjianjie__icontains=search_keywords) # 简介模糊查询
    ).order_by("-gclick") # 按人气倒序

    search_status = 1 # 搜索状态,1表示有结果,0表示无结果
    if not goods_list.exists(): # 检查搜索结果是否为空
        search_status = 0
        # 如果搜索结果为空,推荐4个热门商品
        goods_list = GoodsInfo.objects.all().order_by("-gclick")[:4]

    paginator = Paginator(goods_list, 4) # 每页显示4个商品
    page = paginator.get_page(pindex) # 获取当前页商品列表

    # 构建上下文
    context = {
        'title': '搜索列表',
        'search_status': search_status,
        'guest_cart': 1 if 'user_id' in request.session else 0,
        'cart_num': cart_count(request),
        'page': page,
        'paginator': paginator,
        'query': search_keywords, # 将搜索关键词传递给模板
    }
    return render(request, 'df_goods/ordinary_search.html', context) # 渲染搜索结果页面

前端搜索页面

<!-- 搜索结果页面核心结构 -->
<div class="main_wrap clearfix">
    <ul class="goods_type_list clearfix">
        {% for item in page %} <!-- 遍历商品列表 -->
        <li>
            <a href="{% url 'df_goods:detail' item.id %}"><img src="{{ MEDIA_URL }}{{ item.gpic }}"></a>
            <h4><a href="{% url 'df_goods:detail' item.id %}">{{ item.gtitle }}</a></h4>
            <div class="operate">
                <span class="prize">¥{{ item.gprice }}</span>
                <span class="unit">{{ item.gunit }}</span>
                <a href="{% url 'df_cart:add' item.id 1 %}" class="add_goods" title="加入购物车"></a>
            </div>
        </li>
        {% endfor %}
    </ul>

    <!-- 分页部分 -->
    <div class="pagenation">
        <!-- 上一页 -->
        {% if page.has_previous %}
            <a href="{% url "df_goods:ordinary_search" %}?q={{ query }}&amp;pindex={{ page.previous_page_number }}">上一页</a>
        {% endif %}

        <!-- 页面数字 -->
        {% for pindex in paginator.page_range %}
            {% if pindex == page.number %}
                <a href="#" class="active">{{ pindex }}</a>
            {% else %}
                <a href="{% url "df_goods:ordinary_search" %}?q={{ query }}&amp;pindex={{ pindex }}">{{ pindex }}</a>
            {% endif %}
        {% endfor %}

        <!-- 下一页 -->
        {% if page.has_next %}
            <a href="{% url "df_goods:ordinary_search" %}?q={{ query }}&amp;pindex={{ page.next_page_number }}">下一页></a>
        {% endif %}
    </div>
</div>

技术亮点

  1. 多字段搜索

    • 支持标题、内容、简介等多个字段的模糊查询
    • 提高搜索的准确性和全面性
  2. 智能推荐

    • 搜索结果为空时推荐热门商品
    • 提升用户体验和系统可用性
  3. 分页优化

    • 使用Django内置的分页功能
    • 提升页面加载速度和用户体验
  4. 用户体验

    • 清晰的搜索结果展示
    • 完整的分页导航
    • 搜索状态提示
      image

技术难点与解决方案

1. 反爬机制应对

问题:B站有严格的反爬机制,频繁请求可能导致IP被封

解决方案

  • 使用Session对象保持会话状态
  • 自动获取并携带B站Cookie
  • 设置合理的请求间隔(1秒)
  • 实现完善的错误处理机制

2. 智能文本处理

问题:菜谱数据中的文本信息包含大量无关内容,影响搜索准确性

解决方案

  • 实现三级智能关键词提取机制
  • 使用正则表达式移除括号、标签和数字
  • 建立停用词表,过滤无意义词汇

3. 性能优化

问题:搜索功能和爬虫功能在大数据量下性能下降

解决方案

  • 实现搜索结果分页
  • 优化数据库查询,添加索引
  • 限制爬虫请求频率
  • 使用异步处理提升响应速度

项目总结与展望

项目总结

  1. 完整的数据采集系统

    • 实现了菜谱视频爬虫,自动采集和匹配视频内容
    • 支持多维度数据处理和清洗
  2. 智能交互功能

    • 搭建了AI聊天助手,提供智能问答服务
    • 支持流式响应和聊天记录管理
  3. 完善的管理功能

    • 设计了后台管理原型,便于管理和维护
    • 实现了用户管理和权限控制
  4. 优化的用户界面

    • 设计了现代化的登录注册页面
    • 实现了友好的搜索界面和结果展示
  5. 强大的搜索功能

    • 支持多字段模糊搜索
    • 实现了分页和热门商品推荐

未来展望

  1. 功能扩展

    • 扩展菜谱视频爬虫支持更多平台
    • 增强AI聊天助手的智能程度,支持更多场景
    • 完善后台管理功能,添加更多统计和分析功能
  2. 性能优化

    • 优化搜索算法,提升搜索准确性和速度
    • 改进爬虫策略,提高数据采集效率
    • 优化系统架构,提升系统性能和稳定性
  3. 用户体验

    • 添加更多个性化功能,提升用户体验
    • 优化移动端体验,提供更好的移动服务
    • 增加社交功能,提高用户活跃度
  4. 安全性提升

    • 加强系统安全防护,防止黑客攻击
    • 完善数据备份和恢复机制
    • 提高系统的可靠性和可用性

结语

项目的成功实现离不开团队成员的协作和努力,也体现了我们在数据采集、前端开发和后端技术方面的能力。未来,我们将继续优化系统功能,为用户提供更好的服务。

posted @ 2025-12-19 14:10  ooozyz  阅读(14)  评论(0)    收藏  举报