[日志]中国十大名花

凌霜傲雪 梅花

    梅花原产我国西南及长江以南地区,可以露地栽培,北方多做室内盆栽。梅花为蔷薇科李属落叶乔木,树干紫褐或灰褐色,小枝绿色,叶卵形至阔卵形。早春叶前可开花,花瓣5片,花色主要有大红、桃红、粉色、白色等,清雅芳香。核果近球形,果期为6至7月。园林中多用于庭院绿化或盆栽观赏。

    梅花傲风雪、斗严寒的精神,象征着中华民族坚贞不屈的伟大风骨。

总领群芳 牡丹

    牡丹雍容华贵,被人们誉为“花中之王”,它是中华民族兴旺发达、美好幸福的象征。牡丹以山东菏泽、河南洛阳为栽培中心,园艺品种约有500余个。牡丹为芍药科落叶灌木,株高1至2米,叶互生,二回三出羽状复叶,花单生于当年生枝顶,花形美丽,花色丰富,花色有红、粉、黄、白、绿、紫等。花期4月至5月,果熟期为9月,可播种、分株、嫁接繁殖。

    牡丹可以人工催花,如需要春节开花,立秋后起苗装盆,放入冷室,11月下旬,将小苗移入18℃至25℃的温室内进行养护管理,适量施肥浇水,50至60天后便能开花。牡丹花枝可供切花,根皮入药,有活血、镇痛的效果。

独立冰霜 菊花

    菊花是我国传统名花,按植株形态可分为3种类型,一为独本栽菊,花头大、植株健壮;二为切花菊,世界各国广为栽培;三为地被菊,植株低矮,花朵小,抗性强。菊花独立冰霜、坚贞不屈,格外受到人们青睐。

    菊花为菊科多年生宿根草本植物,茎直立多分枝,叶卵形或广披针形,边缘深裂。头状花序单生或数个聚生于茎顶,园艺品种较多,常见栽培有玫红、紫红、墨红、黄、白、绿等花色。也有一花两色品种。花期10月至12月。生产中多采用扦插法,有些菊花可用青蒿做砧木,进行芽接育苗,例如悬崖菊、什锦菊等。菊花为短日照植物,每日8至10小时日照,70天左右就能开花。菊花的花可入药,有清热、明目、降血压之效。

从容优雅 兰花

    兰花,素有“花中君子”、“王者之香”的美誉。中国兰又名地生兰,按其形态,可分为春兰、蕙兰、建兰、墨兰、寒兰等。我国云南、四川、广东、福建,中原及华北山区均有野生。兰花喜温暖、湿润、半阴环境,适宜在疏松的腐殖质土中生长、分株繁殖,要求适量施肥和及时浇水。一般来说,“五一”之后需将苗盆移至通风凉爽的荫棚下,进行养护管理,立秋后再搬入室内,这样有利于花芽形成,适时开花。兰花可以装点书房和客厅,还能净化空气。兰花的花枝可做切花。

热情如火 月季

    月季不但是我国传统名花,而且是世界著名花卉,世界各国广为栽培。月季按花朵大小、形态性状,可分为:现代月季、丰花月季、藤本月季和微型月季四类。月季顾名思义,它是月月有花、四季盛开。现代月季由中国月月红小花月季与欧洲大花蔷薇杂交而成,花期以5月和9月开花最盛。藤本月季枝条呈藤蔓状,花朵较大。丰花月季花朵中等而密集,花期从5月中一直能开到10月。微型月季株型低矮,花朵亦小,终年开花,适宜室内盆栽。月季在园林中多用于庭院绿化,亦可种植在专类园。扦插、嫁接繁殖,可以芽接亦能靠接。月季花可提取香精,用于食品及化妆品香料;花入药有活血、散瘀之效。

繁花似锦 杜鹃

    杜鹃为杜鹃花科杜鹃花属常绿或半常绿灌木,世界著名观赏花卉。据不完全统计,全世界杜鹃属植物约有800余种,而原产我国的就有650种之多。

    近年来,我国引进大量西洋杜鹃。西洋杜鹃株型低矮,花朵密集,花色丰富,适宜室内盆栽,花期正值我国春节之际,受到花卉爱好者青睐。杜鹃喜温暖、半阴环境,宜于酸性腐殖土生长。扦插、高枝压条或嫁接繁殖。室内盆栽,花后要控制浇水,盆土“见干见湿”即可,土壤过干或过湿容易造成大量落叶。通常在4月中旬将苗盆移到室外通风凉棚下,不能强光暴晒,定期浇灌经过发酵的青草水或0.2%的硫酸亚铁水,可以防止叶尖枯黄。杜鹃花可入药,尤其是我国东北华北野生的映山红,疗效十分显著。

清新脱俗 荷花

    荷花又名莲花、水芙蓉,是我国著名水生花卉,栽培历史悠久。荷花为睡莲科多年生水生草本植物,根茎肥大、有节,俗称“莲藕”,叶盾形,分为“浮叶”和“立叶”两种。花有单瓣和重瓣之分,花色有桃红、黄色、白色,亦有复色品种。荷花在我国各地多有栽培,有的可观花,有的可生产莲藕,有的专门生产莲子。荷花是布置水景园的重要水生花卉,它与睡莲、水葱、蒲草配植,使水景园格外秀丽壮观。荷花花期7月至8月,果熟期9月。播种、植藕繁殖。莲藕、莲子可食用,莲蓬、莲子心入药,有清热、安神之效。
 
富丽堂皇 茶花

    茶花是“花中娇客”,四季常青,冬春之际开红、粉、白花,花朵宛如牡丹,有单瓣,有重瓣。茶花为山茶科山茶属常绿灌木或乔木,叶互生、椭圆形、革质,有光泽。产于我国云南、四川,南方地区多用于庭院绿化,北方均室内盆栽。茶花喜温暖、湿润气候,夏季要求阴蔽环境,宜于酸性土生长。播种、扦插、嫁接繁殖。茶花可以人工控制花期,若需春节开花,可在12月初增加光照和气温,一般情况下,在25℃温度条件下,40天就能开花,若需延期开花,可将苗盆放于2℃~3℃冷室,若需“五一”开花,可提前40天加温催花。

十里飘香 桂花

    桂花在国庆节前后开花,“金风送爽,十里飘香”是吉祥如意的象征。桂花为木犀科常绿小乔木,南方地区多用于庭院绿化;北方均室内盆栽。桂花品种较多,常见栽培有4种:金桂(花金黄色)、银桂(花黄白色)、丹桂(橙红色)和四季桂(花乳白色)。桂花可用嫁接和高取压条育苗。春季进行枝接或靠接,秋季进行芽接,砧木可选用桂花实生苗或女贞。桂花经济价值很高,花可以提取香料,也可熏制花茶。

凌波玉立 水仙

    水仙素有“凌波仙子”的雅称,是我国传统名花。漳州水仙最负盛名,它鳞茎大、形态美、花朵多、馥郁芳香,深受国人喜爱,同时畅销国际市场。水仙是冬季观赏花卉,可以用水泡养,亦能盆栽。可用鳞茎繁殖,常见栽培品种有“金盏银台”(单瓣花)和“玉玲珑”(重瓣花)。

    水仙茎叶清秀,花香宜人可用于装点书房、客厅,格外生机盎然。水仙茎叶多汁有小毒,不可误食,牲畜误食会导致痉挛。鳞茎捣烂外敷,可以治疗疮 痈肿




D:\Python\python.exe D:\A-PythonCode\UserManagementSystem\app01\crawler.py 🚀 开始爬取百度百科《中国大名》 📌 目标网址:https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD%E5%8D%81%E5%A4%A7%E5%90%8D%E8%8A%B1 ⚠️ 仅用于学习交流,请遵守法律法规。 🌐 正在启动浏览器... 🔍 正在访问:https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD%E5%8D%81%E5%A4%A7%E5%90%8D%E8%8A%B1 ✅ 页面加载完成! 📄 已保存页面快照用于调试:debug_baike_full.html 🔍 开始分析文本,查找大名... 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 ✅ 已添加:梅 🌸 发现卉:桂 🌸 发现卉:桂 ✅ 已添加:桂 🌸 发现卉:梅 ✅ 已添加:梅 🌸 发现卉:牡丹 ✅ 已添加:牡丹 🌸 发现卉:兰 ✅ 已添加:兰 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 ✅ 已添加:梅 🌸 发现卉:荷 🌸 发现卉:荷 ✅ 已添加:荷 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 ✅ 已添加:梅 🌸 发现卉:荷 ✅ 已添加:荷 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 🌸 发现卉:梅 ✅ 已添加:梅 ✅ 成功提取 10 种卉信息。 💾 数据已保存至:china_top_ten_famous_flowers.csv 🎉 爬取完成!共提取 10 种中国。 📊 示例预览: 🌸 梅 | 描述:收藏查看我的收藏4360有用+1663中国大名分别是:中之魁—梅中之王—牡丹、凌霜绽妍—菊、君子之—兰中皇后—月季中西施—杜鹃、... 🌸 桂 | 描述:中国大名分别是:中之魁—梅中之王—牡丹、凌霜绽妍— 种名贵又美丽的地方名。 梅被誉为“中国大名之首”。[1]这分别包含着中国不... 🌸 梅 | 描述:梅梅(Prunus mume):蔷薇科,木本植物。形态多样,通常为小乔木,高4-10 m,树皮紫红色,叶片呈弧型或椭圆形,叶边常具小锐锯齿,灰绿色。单生或有时...
10-15
# -*- coding: utf-8 -*- """ 中国大名爬虫脚本(供 Spring Boot 调用) 功能:爬取百度百科“中国大名”,输出 JSON 格式数据到 stdout """ from playwright.sync_api import sync_playwright import time from bs4 import BeautifulSoup import re import json import sys URL = "https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD%E5%8D%81%E5%A4%A7%E5%90%8D%E8%8A%B1" KNOWN_FLOWERS = ["梅", "牡丹", "菊", "兰", "月季", "杜鹃", "山茶", "荷", "桂", "水仙"] def fetch_page(): print("🌐 正在加载页面...", file=sys.stderr) with sync_playwright() as p: browser = p.chromium.launch(headless=True, timeout=10000) context = browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/126.0.0 Safari/537.36" ) page = context.new_page() try: page.goto(URL, wait_until="networkidle", timeout=15000) time.sleep(2) page.evaluate("window.scrollTo(0, document.body.scrollHeight / 2)") time.sleep(1) content = page.content() browser.close() return content except Exception as e: print(f"❌ 页面加载失败: {e}", file=sys.stderr) browser.close() return None def extract_field(text, patterns): for pattern in patterns: match = re.search(pattern, text, re.IGNORECASE) if match: return match.group(1).strip() return "" def parse_results(html_content): soup = BeautifulSoup(html_content, 'html.parser') for tag in soup(["script", "style", "nav", "footer"]): tag.decompose() text_blocks = [] for tag in soup.find_all(['p', 'div', 'span', 'li']): text = tag.get_text(strip=True) if len(text) > 10 and not text.startswith(('参考资料:', '编辑', '图集')): text_blocks.append(text) if not text_blocks: return [] results_map = {} context_window = [] max_context = 6 for block in text_blocks: context_window.append(block) if len(context_window) > max_context: context_window.pop(0) matched_flower = None for flower in KNOWN_FLOWERS: if re.search(rf"(^|[\s(\(,、。;]{flower})[^\w]", block + " ", flags=re.IGNORECASE): matched_flower = flower break elif re.search(rf"^[\s]*{flower}[\s(\(]", block, flags=re.IGNORECASE): matched_flower = flower break if matched_flower: full_context = ' '.join(context_window).strip()[:500] current_len = len(full_context) if (matched_flower not in results_map or len(results_map[matched_flower]['description']) < current_len): results_map[matched_flower] = { "rank": len(results_map) + 1, "name": matched_flower, "family": "", "origin": "", "bloomingPeriod": "", # 驼峰命名适配 Java "symbolism": "", "description": full_context } results = sorted(results_map.values(), key=lambda x: x['rank']) for item in results: desc = item['description'] item['family'] = extract_field(desc, [ r"科属[::]\s*([^\s,。]+(?:科|属))", r"属于[::]\s*([^\s,。]+(?:科|属))", r"是\s*([^\s,。]+科)[\s和与及]" ]) or item['family'] item['origin'] = extract_field(desc, [ r"[原产|分布][地于::]\s*([^\s,。]+)", r"原产于\s*([^\s,。]+)", r"产地[为::]\s*([^\s,。]+)" ]) or item['origin'] item['bloomingPeriod'] = extract_field(desc, [ r"期[为::]\s*([^\s,。]+)", r"开时间[为::]\s*([^\s,。]+)", r"通常在\s*([^\s,。]+)\s*开" ]) or item['bloomingPeriod'] item['symbolism'] = extract_field(desc, [ r"象征[着]?[::]\s*([^\s,。]+)", r"寓意[是为::]\s*([^\s,。]+)", r"代表\s*([^\s,。]+)", r"体现\s*([^\s,。]+)" ]) or item['symbolism'] return results if __name__ == "__main__": html = fetch_page() if not html: print(json.dumps({"error": "Failed to load page"}, ensure_ascii=False), end="") sys.exit(1) flowers = parse_results(html) if not flowers: print(json.dumps({"error": "No flowers extracted"}, ensure_ascii=False), end="") sys.exit(1) # 输出 JSON 到 stdout(这是关键!Spring Boot 会读这个) print(json.dumps(flowers, ensure_ascii=False, indent=2), end="") 根据这个做
10-16
package com.shop.jieyou.controller; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.shop.jieyou.common.Result; import com.shop.jieyou.entity.UserItem; import com.shop.jieyou.service.PythonService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @CrossOrigin(origins = "*") @RequestMapping("/api") public class FlowerController { @Autowired private PythonService pythonService; /** * 获取中国大名数据 * GET /api/flowers */ @GetMapping("/flowers") public Result<List<Map<String, Object>>> getTopTenFlowers() { try { List<Map<String, Object>> flowers = pythonService.getFlowers(); return Result.success(flowers); } catch (Exception e) { // 统一返回 Result 错误格式,避免混用 ResponseEntity return Result.error("500", "获取卉数据失败:" + e.getMessage()); } } /** * 强制刷新缓存并重新爬取数据 * POST /api/flowers/refresh */ @PostMapping("/flowers/refresh") public Result<Map<String, Object>> refreshData() { try { // 假设你在 PythonService 中添加了 clearCache 方法来强制刷新 // pythonService.clearCache(); // 确保此方法存在 List<Map<String, Object>> flowers = pythonService.getFlowers(); // 构造返回信息 Map<String, Object> data = new HashMap<>(); data.put("message", "数据已刷新"); data.put("count", flowers.size()); return Result.success(data); } catch (Exception e) { return Result.error("500", "刷新失败:" + e.getMessage()); } } private static final String INPUT_PATH="src/main/resources/scripts/input.json"; private static final String OUTPUT_PATH="src/main/resources/scripts/output.json"; private static final String PYTHON_SCRIPT="src/main/resources/scripts/collaborative.py"; @GetMapping("/recommend") public Result recommendFlowers(@RequestParam("userId") Long userId) { try { // 1. 查询用户-商品行为数据 List<UserItem> matrix = pythonService.getUserItemMatrix(); // 2. 写入 JSON 文件供 Python 使用 ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(new File(INPUT_PATH), matrix); // 3. 调用 Python 脚本 ProcessBuilder pb = new ProcessBuilder("python", PYTHON_SCRIPT, String.valueOf(userId)); pb.redirectErrorStream(true); Process process = pb.start(); int exitCode = process.waitFor(); if (exitCode != 0) { HashMap<String, Object> map = new HashMap<>(); return Result.error("error", "Python script failed"); } // 4. 读取 Python 输出结果 JsonNode result = mapper.readTree(new File(OUTPUT_PATH)); return Result.success(result); } catch (Exception e) { // 统一返回 Result 错误格式,避免混用 ResponseEntity return Result.error("500", e.getMessage()); } } } 注释
10-17
package com.shop.jieyou.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.shop.jieyou.common.Result; import com.shop.jieyou.entity.UserItem; import com.shop.jieyou.service.ItemService; import com.shop.jieyou.service.PythonService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.util.*; import java.util.concurrent.TimeUnit; /** * 卉相关接口控制器 * 提供三大功能: * 1. 获取中国大名数据(来自爬虫或缓存) * 2. 手动刷新卉数据(强制重新爬取) * 3. 基于用户行为的卉推荐(调用Python协同过滤脚本) */ @RestController @CrossOrigin(origins = "*") // 允许所有域访问,用于前端开发调试(生产环境建议限制域名) @RequestMapping("/api") public class FlowerController { @Autowired private PythonService pythonService; // 注入业务服务层,处理数据获取与推荐逻辑 @Autowired private ItemService itemService; /** * 接口:GET /api/flowers * 功能:获取“中国大名”数据列表 * 数据来源:可能来自数据库、Redis 缓存 或 调用 Python 爬虫脚本 * * @return Result<List<Map<String, Object>>> 返回包含卉信息的成功响应 */ @GetMapping("/flowers") public Result<List<Map<String, Object>>> getTopTenFlowers() { try { // 调用服务层获取卉数据(内部可能带缓存机制) List<Map<String, Object>> flowers = pythonService.getFlowers(); return Result.success(flowers); // 成功返回数据 } catch (Exception e) { // 捕获异常并统一返回错误码和消息,避免暴露堆栈给前端 return Result.error("500", "获取卉数据失败:" + e.getMessage()); } } /** * 接口:POST /api/flowers/refresh * 功能:强制刷新卉数据缓存,触发重新爬取 * 使用场景:管理员手动更新数据时调用 * * @return Result<Map<String, Object>> 返回刷新结果信息 */ @PostMapping("/flowers/refresh") public Result<Map<String, Object>> refreshData() { try { // TODO: 如果实现了 clearCache 方法,请取消注释并调用 // pythonService.clearCache(); // 清除旧缓存,下次 getFlowers 将重新爬取 // 重新获取最新数据(假设此时会触发爬虫) List<Map<String, Object>> flowers = pythonService.getFlowers(); // 构造返回信息 Map<String, Object> data = new HashMap<>(); data.put("message", "数据已刷新"); data.put("count", flowers.size()); return Result.success(data); } catch (Exception e) { return Result.error("500", "刷新失败:" + e.getMessage()); } } // ========== 推荐系统相关常量定义 ========== /** * 输入文件路径:Java 将用户-商品行为数据写入此 JSON 文件供 Python 脚本读取 * 注意:src/main/resources 是编译后打包进 jar 的资源目录,不适合运行时写入! * 建议改为外部路径如 "./data/input.json" */ private static final String INPUT_PATH = "src/main/resources/scripts/input.json"; /** * 输出文件路径:Python 脚本将推荐结果写入此文件,Java 再读取返回给前端 */ private static final String OUTPUT_PATH = "src/main/resources/scripts/output.json"; /** * Python 协同过滤脚本路径 * 注意:resources 目录下的 .py 文件在打包后无法直接作为可执行脚本运行 * 更佳做法是将脚本放在项目外部或使用 ProcessBuilder 启动独立服务 */ private static final String PYTHON_SCRIPT = "src/main/resources/scripts/collaborative.py"; /** * 接口:GET /api/recommend?userId=123 * 功能:为指定用户生成个性化卉推荐列表 * 实现方式:Java 查询数据库 → 写入 JSON 文件 → 调用 Python 脚本计算 → 读取结果返回 * * @param userId 用户ID,必填参数 * @return Result<JsonNode> 推荐的商品ID数组(如 [101, 105, 108]) */ @GetMapping("/recommend") public Result recommendFlowers(@RequestParam("userId") Long userId) { try { // 1. 获取用户行为数据 List<UserItem> matrix = pythonService.getUserItemMatrix(); // 2. 调用 Python 脚本(通过 stdin/stdout 通信) ProcessBuilder pb = new ProcessBuilder("python", PYTHON_SCRIPT, String.valueOf(userId)); pb.redirectErrorStream(true); // 合并错误流 Process process = pb.start(); // 3. 将数据写入脚本的标准输入 ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(process.getOutputStream(), matrix); process.getOutputStream().close(); // 关闭输入,通知Python结束读取 // 4. 读取Python脚本的输出(推荐结果) JsonNode result = mapper.readTree(process.getInputStream()); // 5. 等待脚本执行完毕 int exitCode = process.waitFor(); if (exitCode != 0) { return Result.error("500", "Python script failed with exit code: " + exitCode); } System.out.println(result); return Result.success(result); } catch (Exception e) { e.printStackTrace(); return Result.error("500", "推荐生成失败:" + e.getMessage()); } } @PostMapping("/predict") public ResponseEntity<String> predict(@RequestParam("file") MultipartFile file) { try { // 保存上传的文件到临时路径 String tempDir = System.getProperty("java.io.tmpdir"); File tempFile = new File(tempDir, file.getOriginalFilename()); file.transferTo(tempFile); // 调用 Python 脚本执行预测 ProcessBuilder pb = new ProcessBuilder( "D:\\Python\\python.exe", "D:/DevCode/商城/Shop-master/shop-springboot/src/main/resources/scripts/image_classifier.py", "predict", tempFile.getAbsolutePath() ); pb.redirectErrorStream(true); // 合并 stdout 和 stderr Process process = pb.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); StringBuilder output = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { output.append(line); } int exitCode = process.waitFor(); if (exitCode == 0) { return ResponseEntity.ok(output.toString().trim()); } else { return ResponseEntity.status(500).body("{\"error\": \"Prediction failed\"}"); } } catch (Exception e) { return ResponseEntity.status(500).body("{\"error\": \"" + e.getMessage() + "\"}"); } } private static final String PYTHON_EXECUTABLE = "D:\\Python\\python.exe"; // 或 "python3" private static final String INFER_SCRIPT_PATH = "D:/DevCode/商城/Shop-master/shop-springboot/src/main/resources/scripts/python-model/infer.py"; private final ObjectMapper objectMapper = new ObjectMapper(); /** * 对接前端 /api/uploadPython?file=imageUrl */ @PostMapping("/uploadPython") public ResponseEntity<?> classifyImage(@RequestParam String file) { if (file == null || file.trim().isEmpty()) { return ResponseEntity.badRequest().body(error("400", "缺少图片URL")); } File tempImageFile = null; Process process = null; try { // 1. 从 URL 下载图片到本地临时文件 tempImageFile = downloadImageToFile(file); // 2. 调用 Python 脚本处理该临时文件 ProcessBuilder pb = new ProcessBuilder( PYTHON_EXECUTABLE, INFER_SCRIPT_PATH, tempImageFile.getAbsolutePath() // 传入真实文件路径 ); pb.redirectErrorStream(true); // 合并 stdout 和 stderr process = pb.start(); // 3. 读取 Python 输出 StringBuilder output = new StringBuilder(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { output.append(line).append("\n"); } } int exitCode = process.waitFor(); if (exitCode != 0) { return ResponseEntity.status(500).body(error("500", "Python执行失败")); } // 4. 提取有效 JSON 结果(跳过 TensorFlow 日志) JsonNode resultNode = extractValidJson(output.toString()); if (resultNode == null) { return ResponseEntity.status(500).body(error("500", "无法解析识别结果")); } if (resultNode.has("error")) { return ResponseEntity.status(500).body(error("500", "识别错误:" + resultNode.get("error").asText())); } // 5. 构造成功响应 System.out.println(resultNode); Iterator<Map.Entry<String, JsonNode>> fields = resultNode.fields(); String predictedClass = null; double confidence = 0.0; while (fields.hasNext()) { Map.Entry<String, JsonNode> entry = fields.next(); double prob = entry.getValue().asDouble(); if (prob > confidence) { confidence = prob; predictedClass = entry.getKey(); } } if (predictedClass == null) { return ResponseEntity.status(500).body(error("500", "无法识别任何类别")); } Map<String, Object> result = new HashMap<>(); result.put("predicted_class", predictedClass); result.put("confidence", confidence); return ResponseEntity.ok(success(result)); } catch (Exception e) { e.printStackTrace(); return ResponseEntity.status(500).body(error("500", "服务异常:" + e.getMessage())); } finally { // 6. 确保临时文件被删除 if (tempImageFile != null && tempImageFile.exists()) { boolean deleted = tempImageFile.delete(); if (!deleted) { System.err.println("⚠️ 无法删除临时文件: " + tempImageFile.getAbsolutePath()); } } // 清理子进程资源 if (process != null) { process.destroyForcibly(); } } } /** * 从图片 URL 下载并保存为临时 .jpg 文件 */ private File downloadImageToFile(String imageUrl) throws IOException { URL url = new URL(imageUrl); URLConnection conn = url.openConnection(); conn.setConnectTimeout(10000); conn.setReadTimeout(30000); // 检查响应码 if (conn instanceof java.net.HttpURLConnection) { int statusCode = ((java.net.HttpURLConnection) conn).getResponseCode(); if (statusCode != 200) { throw new IOException("HTTP " + statusCode + " - 无法下载图片:" + imageUrl); } } // 创建临时文件 File tempFile = Files.createTempFile(UUID.randomUUID().toString(), ".jpg").toFile(); tempFile.deleteOnExit(); // JVM退出时自动删除 // 写入文件 try (InputStream in = conn.getInputStream(); FileOutputStream out = new FileOutputStream(tempFile)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } return tempFile; } /** * 从输出中提取有效的 JSON 对象(忽略日志前缀) */ private JsonNode extractValidJson(String rawOutput) { try { // 尝试查找最后一个完整的 { ... } int start = rawOutput.lastIndexOf('{'); int end = rawOutput.lastIndexOf('}'); if (start != -1 && end > start) { String jsonStr = rawOutput.substring(start, end + 1); return objectMapper.readTree(jsonStr); } return objectMapper.readTree(rawOutput.trim()); } catch (JsonProcessingException e) { System.err.println("JSON解析失败:\n" + rawOutput); return null; } } private Map<String, Object> success(Object data) { Map<String, Object> map = new HashMap<>(); map.put("code", 200); map.put("msg", "success"); map.put("data", data); return map; } private Map<String, Object> error(String code, String msg) { Map<String, Object> map = new HashMap<>(); map.put("code", code); map.put("msg", msg); return map; } }注释
10-22
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值