别再手动找电影了!教你用Node.js + 豆瓣API快速搭建个人电影推荐小工具

发布时间:2026/6/4 2:03:02

别再手动找电影了!教你用Node.js + 豆瓣API快速搭建个人电影推荐小工具 用Node.js打造智能电影推荐引擎从豆瓣API到个性化筛选周末晚上打开流媒体平台面对海量影片却不知从何看起作为开发者我们可以用技术解决这个选择困难症。本文将带你用Node.js构建一个能理解你品味的电影推荐工具不仅自动获取豆瓣最新影讯还能根据你的偏好进行智能筛选。1. 环境准备与项目初始化首先确保你的系统已安装Node.js建议版本16然后创建一个新项目目录并初始化mkdir movie-recommender cd movie-recommender npm init -y安装核心依赖包npm install axios inquirer chalk oraaxios用于调用豆瓣APIinquirer创建交互式命令行界面chalk美化控制台输出ora添加加载动画新建index.js作为入口文件我们先来测试API连通性const axios require(axios); const testApi async () { try { const response await axios.get(https://api.douban.com/v2/movie/in_theaters?city北京); console.log(response.data.subjects[0]); } catch (error) { console.error(API请求失败:, error.message); } }; testApi();2. 核心API功能封装我们将豆瓣API的常用功能封装成独立模块创建douban.jsconst axios require(axios); class DoubanAPI { constructor() { this.baseURL https://api.douban.com/v2/movie; } async getInTheaters(city 北京, start 0, count 20) { const url ${this.baseURL}/in_theaters?city${encodeURIComponent(city)}start${start}count${count}; return this._request(url); } async search(keyword, tag , start 0, count 10) { let url ${this.baseURL}/search?q${encodeURIComponent(keyword)}; if (tag) url tag${encodeURIComponent(tag)}; url start${start}count${count}; return this._request(url); } async getTop250(start 0, count 10) { const url ${this.baseURL}/top250?start${start}count${count}; return this._request(url); } async _request(url) { try { const response await axios.get(url); return response.data; } catch (error) { console.error(API请求错误:, error.message); return null; } } } module.exports new DoubanAPI();3. 构建交互式命令行界面利用inquirer创建用户友好的交互界面在index.js中添加const inquirer require(inquirer); const chalk require(chalk); const ora require(ora); const douban require(./douban); const mainMenu [ { type: list, name: action, message: 请选择操作, choices: [ 查看正在热映, 搜索电影, 查看Top250, 退出 ] } ]; const cityQuestion { type: input, name: city, message: 输入城市名称默认北京, default: 北京 }; const searchQuestion [ { type: input, name: keyword, message: 输入电影名称或关键字 }, { type: input, name: tag, message: 输入电影类型标签可选 } ]; async function startApp() { while (true) { const { action } await inquirer.prompt(mainMenu); if (action 退出) break; const spinner ora(加载中...).start(); try { let movies []; switch (action) { case 查看正在热映: const { city } await inquirer.prompt(cityQuestion); const data await douban.getInTheaters(city); movies data?.subjects || []; break; case 搜索电影: const answers await inquirer.prompt(searchQuestion); const searchData await douban.search(answers.keyword, answers.tag); movies searchData?.subjects || []; break; case 查看Top250: const topData await douban.getTop250(); movies topData?.subjects || []; break; } spinner.succeed(加载完成); displayMovies(movies); } catch (error) { spinner.fail(操作失败); console.error(chalk.red(error.message)); } } } function displayMovies(movies) { console.log(\n); movies.forEach((movie, index) { console.log(chalk.bold(${index 1}. ${movie.title})); console.log( 评分: ${chalk.yellow(movie.rating.average)}/10); console.log( 类型: ${movie.genres.join( / )}); console.log( 导演: ${movie.directors.map(d d.name).join(、)}); console.log( 主演: ${movie.casts.slice(0, 3).map(c c.name).join(、)}); console.log(chalk.gray( ----------------------------)); }); console.log(\n); } startApp();4. 高级功能智能推荐算法基础的列表展示还不够智能我们来添加基于用户偏好的推荐逻辑。创建recommender.jsconst _ require(lodash); class MovieRecommender { constructor(movies) { this.movies movies; } byRating(minRating 7.5) { return this.movies.filter(m m.rating.average minRating); } byGenre(genres []) { if (!genres.length) return this.movies; return this.movies.filter(m genres.some(g m.genres.includes(g)) ); } byYearRange(startYear, endYear) { return this.movies.filter(m { const year parseInt(m.year); return year startYear year endYear; }); } combineFilters(filters {}) { let result [...this.movies]; if (filters.minRating) { result result.filter(m m.rating.average filters.minRating); } if (filters.genres?.length) { result result.filter(m filters.genres.some(g m.genres.includes(g)) ); } if (filters.yearRange) { const [start, end] filters.yearRange; result result.filter(m { const year parseInt(m.year); return year start year end; }); } return _.orderBy(result, [rating.average], [desc]); } } module.exports MovieRecommender;更新index.js添加推荐功能// 在文件顶部添加 const MovieRecommender require(./recommender); // 在displayMovies函数后添加 async function recommendMovies(movies) { const { filters } await inquirer.prompt([ { type: input, name: filters.minRating, message: 最低评分0-10默认7, default: 7, validate: input input 0 input 10 || 请输入0-10之间的数字 }, { type: input, name: filters.genres, message: 类型筛选多个类型用逗号分隔如喜剧,动作, filter: input input.split(,).map(s s.trim()) }, { type: input, name: filters.yearRange, message: 年份范围格式开始年份-结束年份如2010-2020, filter: input input.split(-).map(Number) } ]); const recommender new MovieRecommender(movies); const recommended recommender.combineFilters(filters); console.log(chalk.bold(\n推荐结果)); displayMovies(recommended); } // 修改startApp中的switch语句在获取电影后添加 const { wantRecommend } await inquirer.prompt({ type: confirm, name: wantRecommend, message: 是否要进行智能推荐 }); if (wantRecommend) { await recommendMovies(movies); }5. 数据持久化与用户偏好记录为了让系统记住你的偏好我们可以添加简单的本地存储功能。创建storage.jsconst fs require(fs); const path require(path); const storagePath path.join(__dirname, user_prefs.json); class UserStorage { static getPrefs() { try { if (fs.existsSync(storagePath)) { return JSON.parse(fs.readFileSync(storagePath)); } return {}; } catch (error) { console.error(读取用户偏好失败:, error); return {}; } } static savePrefs(prefs) { try { fs.writeFileSync(storagePath, JSON.stringify(prefs, null, 2)); return true; } catch (error) { console.error(保存用户偏好失败:, error); return false; } } static updatePrefs(newPrefs) { const current this.getPrefs(); return this.savePrefs({ ...current, ...newPrefs }); } } module.exports UserStorage;更新推荐功能记住用户偏好// 在recommendMovies函数顶部添加 const UserStorage require(./storage); const userPrefs UserStorage.getPrefs(); // 修改推荐问题的default值 { type: input, name: filters.minRating, message: 最低评分0-10默认7, default: userPrefs.minRating || 7, // ... } // 在recommendMovies函数最后添加 UserStorage.updatePrefs(filters);6. 性能优化与错误处理为了提升用户体验我们需要优化API调用和处理各种边界情况// 在douban.js的_request方法中添加重试逻辑 async _request(url, retries 3) { try { const response await axios.get(url, { timeout: 5000, headers: { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } }); return response.data; } catch (error) { if (retries 0) { await new Promise(resolve setTimeout(resolve, 1000)); return this._request(url, retries - 1); } throw new Error(API请求失败: ${error.message}); } } // 添加缓存机制 const cache new Map(); async _request(url) { if (cache.has(url)) { return cache.get(url); } // ...原有请求逻辑 cache.set(url, data); setTimeout(() cache.delete(url), 5 * 60 * 1000); // 5分钟缓存 return data; }7. 部署为全局命令行工具想让你的电影推荐工具像普通命令一样随时可用吗按以下步骤操作在package.json中添加{ bin: { movie-recommender: ./index.js } }在文件顶部添加shebang#!/usr/bin/env node运行以下命令安装全局npm link现在你可以在任何终端直接输入movie-recommender启动你的应用了8. 扩展思路Web界面与移动端适配虽然命令行工具很方便但也可以考虑扩展为Web应用安装Express和EJSnpm install express ejs创建server.jsconst express require(express); const app express(); const douban require(./douban); app.set(view engine, ejs); app.use(express.static(public)); app.get(/, async (req, res) { const movies await douban.getInTheaters(); res.render(index, { movies: movies.subjects }); }); app.listen(3000, () { console.log(Server running on http://localhost:3000); });创建views/index.ejs!DOCTYPE html html head title电影推荐/title style .movie-card { border: 1px solid #ddd; padding: 15px; margin: 10px; border-radius: 5px; } .rating { color: gold; font-weight: bold; } /style /head body h1正在热映/h1 % movies.forEach(movie { % div classmovie-card h2% movie.title %/h2 p评分: span classrating% movie.rating.average %/span/p p类型: % movie.genres.join(, ) %/p p导演: % movie.directors.map(d d.name).join(, ) %/p /div % }); % /body /html这个Node.js电影推荐工具从最初的API调用到完整的交互式应用展示了如何将各种技术整合解决实际问题。在实际使用中我发现最实用的功能是结合评分和类型的复合筛选这能快速找到符合个人口味的优质电影。

相关新闻