【问题标题】:How to extract product names from a set of strings ? (php)如何从一组字符串中提取产品名称? (php)
【发布时间】:2012-02-03 18:28:57
【问题描述】:

我正在开发一个基于 PHP 的购物应用程序。我有我知道代表相同产品的字符串列表。这些字符串可能包含完整的产品名称或其中的一部分(完整的产品名称通常是品牌 + 型号)。

我想知道执行此产品名称提取的最佳方法是什么。

例如,这里是代表同一产品的字符串列表:

  • Tkg BOUILLOIRE TKG - JK 1008 RWD
  • Tkg Jk 1008 Rwd
  • Tkg Kalorik - JK 1008 RWD - Bouilloire Électrique sans Fil 360°
  • TKG Bouilloire électrique sans fil 1,7 升 2000 瓦 Pois TKG Rouge et blanc
  • Tkg Kalorik - JK 1008 RWD - Bouilloire Électrique sans Fil 360°
  • Tkg JK 1008 RWD 肉汁酒

我希望提取产品名称“Tkg JK 1008 RWD”。请注意,字符串 4 仅包含部分信息。

当我计算所有字符串中的重复单词时,我尝试了一种方法;但从那里,很难走得更远。

你有什么线索吗?

干杯 尼古拉斯

【问题讨论】:

  • 嗨,根据提供的信息,这看起来像是一个销售网站。你可以说得更详细点吗?你能改变html输出吗?它用作下拉菜单吗?
  • 如果您已经知道需要提取什么,那么就不要费心提取它了。如果你事先不知道要提取什么,那么你需要描述一下你要提取的pattern,否则没人能回答你的问题。
  • 在我看来,这听起来像是您在某个地方遇到了设计问题。强制使用唯一的产品编号,以避免混淆购买和订购的产品。以唯一的产品编号开始或结束上述每个字符串。

标签: php string extract text-extraction information-extraction


【解决方案1】:

您可以分析字符串重叠的程度(并生成出现在大多数字符串中的单词/子字符串列表),然后选择最相关的单词。

例如,如果单词出现在特定百分比的字符串中,您可以将它们识别为最有可能的产品名称候选者。 (与您所做的类似,但添加了阈值 - 例如,您可以看到 5 个单词出现在 88% 的字符串中,而其他单词的百分比要低得多 - 然后选择前 5 个作为产品名称。这不是确切的恐怕需要手动调整。)这应该可以收集大部分信息,但永远不会完美。

此外,您可以预先定义品牌列表并过滤掉这些词。我还将考虑单词的部分匹配,因为它们可能是手动数据输入的产物,并且总是可能存在拼写错误。您可以看到这是多么相关,如果您通过简单地丢弃它们获得足够强的“信号”,那么无需担心。

更进一步,您可以指定另一个过滤器来标记项目以供手动管理,但这可能非常耗时。

恐怕没有简单的答案。你所做的本质上是文本挖掘。我刚刚提出了一些可以帮助您开始的想法和起点。

假设您正在构建一些自动爬虫试图将来自多个来源的日期放在一起,上述方法将起作用。如果您想让访问者能够搜索您的网站并为所有查询返回正确的产品页面,那么我建议您深入进行一些文本搜索(任何人都可以进行主要数据分析?)。或者只是使用一些现成的解决方案。

【讨论】:

  • 谢谢 Petr - 真的很有用。有很多想法可以探索。
【解决方案2】:

在比较购物引擎工作过(虽然不是专门针对这个问题),我猜你描述的问题非常困难。我的建议是放弃并选择字符串中的“最佳”,而不是尝试合成或提取“该”产品名称(无论如何这是一个模糊的概念)。您用来尝试提取产品名称的大多数想法都会产生不一致和令人沮丧的结果。例如,仅查看您提供的示例,幼稚的算法可能会产生像“Jk 1008 Rwd”这样的神秘结果,或者像“Bouilloire Électrique”这样极其模糊的结果。即使是 Tomas 聪明而漂亮的结果也会在很多产品中失败,或者产生令人尴尬的不合语法的结果。我脑海中浮现的很多想法往往会去除诸如“Bouilloire Électrique”之类的类别词,这对于用户体验和 SEO 来说不是最理想的。

如果我处于您的位置,我可能会像这样对解决方案进行建模:计算标题中每个单词的 idf 权重(将您的所有产品或该类别中的所有产品视为文档空间)。然后将每个产品字符串转换为其 idf 权重向量,并计算该产品的所有权重向量的质心。找到最接近该质心的字符串,并将其称为“最佳”。使用该字符串作为产品名称。它并不完美,但在大多数情况下它可能运行良好。 Lucene(或您使用的任何搜索数据库)中可能有一个插件或查询可以为您做很多事情。

在您提供的字符串列表中,此方法倾向于远离第四个不完整的字符串,因为它不包括高度加权的型号 1008(可能在电热水壶中不常见)。如果你有很多信息量少、不完整的产品名称,这可能是个问题。那么质心可能不会特别接近包含型号的名称。正如我所说,这是一个难题。

其他想法:

  1. Thomas 选择前 n 个最常用词的启发式方法可能比我想象的效果更好。或者,可能还有另一种启发式方法来检测何时效果不佳
  2. 寻找大多数字符串共有的长子字符串,并选择具有最高 IDF 权重和的子字符串。

进一步阅读:

TF-IDF

Centroid

Vector Space Model

【讨论】:

    【解决方案3】:

    只是一些想法

    <?php
    // to lower case
    $string = strtolower(
    'Tkg BOUILLOIRE TKG - JK 10o8 RWD
    Tkg Jk 10o8 Rwd
    Tkg Kalorik - JK 10o8 RWD - Bouilloire Électrique sans Fil 360°
    TKG Bouilloire électrique sans fil 1,7 litre 2000 watts Pois TKG Rouge et blanc
    Tkg Kalorik - JK 10o8 RWD - Bouilloire Électrique sans Fil 360°
    Tkg JK 10o8 RWD BOUILLOIRES'
    );
    
    // remove new lines and explode by spaces
    $data = explode(' ', str_replace(array("\r\n", "\n", "\r"), ' ', $string));
    // count most popular words
    $count = array_count_values($data);
    // sort 
    arsort($count);
    // get first 6 most popular words
    $product = array_slice($count, 0, 6);
    // print product
    var_dump(implode(' ', array_keys($product)));
    ?>
    

    输出是:

    tkg rwd 1008 jk - bouilloire
    

    【讨论】:

    • 谢谢托马斯;非常好的战术,对我来说是一个很好的起点!
    【解决方案4】:

    第一次尝试实施你们带来的一些想法。

    class ProductNameExtraction {
    
        private $brandName = NULL;
        private $categoryName = NULL;
    
        private $modelName = NULL;
    
        /**
          * @param $A Array of string discribing the same product
          */
        public function __construct($A, $brandName, $categoryName) {
            $this->brandName = $brandName;
            $this->categoryName = $categoryName;
    
            $res = array();     
            foreach ($A as $k => $title) {
                $res[] = $this->cleanTitle($title);
            }
    
            $this->modelName = $this->computeProductName($res);
        }
    
        public function getModelName() {
            return $this->modelName;
        }
    
        private function computeProductName($A) {
            $s = NULL;
    
            foreach ($A as $k => $title) {
                $s .= $title . ' ';
            }
            $s = trim($s);
    
            $data = explode(' ', $s);
    
            // count most popular words
            $count = array_count_values($data);
    
            // Remove brand & category names
            unset($count[$this->cleanTitle($this->brandName)]);
            unset($count[$this->cleanTitle($this->categoryName)]);
    
            $s = '';
            $totalnb = sizeof($A);          
            foreach ($count as $k => $val) {
                if ($val / $totalnb > 0.5) {
                    $s .= $k . ' ';
                }
            }
    
            return $s;
        }
    
        private function cleanTitle($title) {
            // Remove extra spaces
            $title = trim($title);
            $title = preg_replace('/\s\s+/', ' ', $title);
    
            // Remove noise
            $title = str_replace(' - ', ' ', $title);
            $title = str_replace(array("\r\n", "\n", "\r"), ' ', $title);
    
            return strtoupper($title);
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2015-07-14
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-22
      相关资源
      最近更新 更多