2012-02-03 114 views
1

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

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

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

  • TKG BOUILLOIRE TKG - JK 1008 RWD
  • TKG JK 1008 RWD
  • TKG KALORIK - JK 1008 RWD - Bouilloire ELECTRIQUE SANS费尔360°
  • TKG Bouilloire ELECTRIQUE SANS FIL 1,7-升2000瓦特的POI TKG胭脂等相思
  • TKG KALORIK - JK 1008 RWD - Bouilloire ELECTRIQUE SANS费尔360°
  • TKG JK 1008 RWD BOUILLOIRES

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

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

你有什么线索吗?

干杯 萨科

+0

嗨,根据提供的信息,这看起来像一个销售网站。你可以说得更详细点吗?你可以改变html输出吗?它被用作下拉菜单吗? – 2012-02-03 18:32:43

+1

如果你已经知道你需要提取什么,那么不要费力提取它。如果你不知道你需要提取什么,那么你需要描述你想提取的***模式***,否则没有人可以回答你的问题。 – FtDRbwLXw6 2012-02-03 18:33:33

+1

在我看来,这听起来像你有某个地方的设计问题。强制使用独特的产品编号,以避免混淆购买什么和订购。使用唯一的产品编号开始或结束上述每个字符串。 – phpmeh 2012-02-03 18:34:37

回答

2

你可以分析字符串多少重叠(并产生出现在大部分字/字符串列表),然后选择最相关的词。

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

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

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

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

上述方法假定您正在构建一些自动爬虫,尝试将多个来源的日期放在一起。如果您希望允许访问者搜索您的网站并返回所有查询的正确产品页面,那么我会建议潜入一些文本搜索(主要数据分析任何人?)。或者只是使用一些现成的解决方案。

+0

谢谢彼得 - 真的很有用。大量的想法来探索。 – 2012-02-04 09:09:31

1

只是一些想法

<?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 
+0

谢谢托马斯;非常好的策略和一个很好的起点! – 2012-02-04 09:10:02

2

在比较购物引擎工作过(尽管不是在这个问题上明确),我猜想,像你描述的问题非常困难。我的建议是放弃,只是选择“最好”的字符串,而不是试图合成或提取“产品名称”(这是一个模糊的概念)。您尝试提取产品名称的大多数想法都会产生不一致且令人沮丧的结果。例如,仅仅看看你给出的例子,朴素的算法可能会产生像“Jk 1008 Rwd”这样的神秘结果,或者像“BouilloireÉlectrique”那样非常模糊的东西。即使托马斯的聪明和好看的结果将失败的许多产品,或产生令人尴尬的非语法结果。我脑海中涌现出的很多想法都会去掉类似“BouilloireÉlectrique”这样的词,这对用户体验和SEO来说都不是最理想的。

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

在您给出的字符串列表中,此方法倾向于从第四个不完整的字符串移开,因为它不包含高度加权的1008号型号(大概在电水壶中不常见)。如果你有很多低信息,不完整的产品名称,这可能是一个问题。然后质心可能不会特别接近包含型号的名称。正如我所说,这是一个难题。

其他的想法:采摘前n个最常用的词可能更好地工作比我猜它的

  1. 托马斯的启发。或者,可能有另一种启发式方法来检测它何时效果不佳
  2. 查找大多数字符串常见的长子字符串,并选择IDF权重和最高的子字符串。

延伸阅读:

TF-IDF

Centroid

Vector Space Model

+0

谢谢!一些不错的文章,你指出我! – 2012-02-04 09:13:44

0

在执行你们带来了一些想法的首种尝试。

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); 
    } 

} 
相关问题