【失敗】URLから製品名を取得する

Electron

server/server.js

import express from "express";
import fetch from "node-fetch";
import { JSDOM } from "jsdom";
import cors from "cors";

const app = express();
app.use(express.json());
app.use(cors());

// 単一URLからTitleを取得
async function fetchTitle(url) {
  try {
    const res = await fetch(url, { timeout: 8000});
    const html = await res.text();
    const dom = new JSDOM(html);
    const title = dom.window.document.querySelector("title")?.textContent || "";
    return { url, title };
  } catch (err) {
    console.error("タイトル取得エラー", err.message);
    return { url, title: null, error: true };
  }
}

-----------------------------------
これをすると
title: '' or  title: 'Access Denied'  or  title" null, error: true になってしまう
外部サイトがCDNを使って画像を表示している場合、CDNのアクセス制限で'Access Denied'が返る

また、node-fetchはブラウザのようにCookieやReferer、User-Agentを送信しないのでアクセスが成功しない結果、
title: '' となって空になる
const res = await fetch(url, {
      headers: {
        "User-Agent":
          "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
        "Accept": "text/html",
        "Referer": url,
      },
    });
のようにしてnode-fetchにUser-Agentをつけたがだめだった

ブラウザ側(Home.jsx)でFetchしてみたけどそれもCORSエラーとなってしまった
-----------------------------------



// 複数URLをまとめて処理
app.post("/get-title", async(req, res) => {
  const { urls } = req.body;
  if (!urls || !Array.isArray(urls)) {
    return res.status(400).json({ error: "urls配列が必要です" });
  }
  
  // 
  const results = await Promise.all(urls.map(fetchTitle));
  res.json(results);
});

app.listen(3000, () => console.log("Server running on http://localhost:3000"));

src/pages/CaptureFrame.jsx

import { useState, useRef } from 'react';

export default function CaptureFrame() {
  const videoRef = useRef(null);
  const [preview, setPreview] = useState(null);
  const [result, setResult] = useState(null);
  const [productNames, setProductNames] = useState([]);

  const fetchProductName = async (urls) => {
    try {
      const res = await fetch("http://localhost:3000/get-title", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ urls })
      });
      return await res.json();
    } catch (err) {
      console.error("製品名取得エラー", err);
      return [];
    }
  };

  const analyze = async () => {
    if (!preview) {
      alert("まずキャプチャを作成してください");
      return;
    }
    try {
      const res = await fetch("http://localhost:3000/analyze", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ image: preview })
      });
      const jsondata = await res.json();
      console.log("解析結果:", jsondata);
      setResult(jsondata);

      // visuallySimilarImagesのURL上位5件を取得してstateに保存
      if (jsondata.visuallySimilarImages?.length > 0) {
        const urls = jsondata.visuallySimilarImages.slice(0, 5).map((img) => img.url);
        const productTitles = await fetchProductName(urls);
        console.log("推定製品名:", productTitle);
        setProductName(productTitles);
      }
    } catch (err) {
      console.error("CaptureFrameエラー", err);
    }
  }

  return (
    {productNames.length > 0 && (
      <div>
        <h3>推定製品名</h3>
        <ul>
          {productNames.map((productName, index)) => (
            <li key={index}>
              <img src={productName.url} />
              <p>{productName.title}</p>
            </li>
          )}
        </ul>
      </div>
    )}
  );

}
BACK