使用正则表达式模式重复捕获组

时间:2017-07-24 21:29:07

标签: php regex preg-match-all pcre digits

我正在尝试从网站上获取产品列表,包括各个产品代码。 产品代码是5位数代码,元素的复杂程度来自

<p>Part Number: 67001</p>

<p>Part Number: 50545 &ndash; 450g Cartridge 50525 - 2.5kg Tub 50520 - 20kg Pail 50555 - 55kg Drum 50575 *Indent - 175kg Drum</p>

不幸的是,5个数字模式遍布整个网页,所以我不能只使用/\d{5}/

我正在使用正则表达式,它只提取零件编号元素中的5位数字而不是网页的其余部分。

类似于:/\<p\>Part\s*Number\:\s*((\d{5}) repeat this capture group n times)\<\/p\>/

我知道我可以通过分阶段打破页面并逐个应用一个正则表达式来实现。例如

第一阶段/\<p\>Part\s*Number\:\s*.*?\<\/p\>/
第二阶段/\d{5}/

但它是否有可能以一种正则表达式模式进行,如果是这样的话?

2 个答案:

答案 0 :(得分:2)

我现在比一年前更聪明,所以我已经彻底清除了我原来的建议。尝试解析有效​​的html时,最好/最可靠的方法是使用dom解析器。 XPath使节点/元素搜索变得非常容易。一旦您取消了不包含<p>关键字的Part Number代码,正则表达式模式仍然是合适的工具。

代码:(Demo

$html = <<<HTML
<p>Zip Code: 99501</p>
<p>Part Number: 67001</p>
<p>Part Number: 98765 - 10000kg capacity</p>
<p>Some dummy/interfering text. Part Number: 12345</p>
<p>Zip Codes: 99501, 99524 , 85001 and 72201</p>
<p>Part Number: 50545 &ndash; 450g Cartridge 50525 - 2.5kg Tub 50520 - 20kg Pail 50555 - 55kg Drum 50575 *Indent - 175kg Drum</p>
HTML;

$partnos = [];

$dom = new DOMDocument;
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
foreach ($xpath->query("//p[starts-with(., 'Part Number: ')]") as $node) {
    // echo "Qualifying text: {$node->nodeValue}\n";
    if (preg_match_all('~\b\d{5}\b~', $node->nodeValue, $matches)) {
        $partnos = array_merge($partnos, $matches[0]); //or array_push($partnos, ...$matches[0]);
    }
}
var_export($partnos);

输出:

array (
  0 => '67001',
  1 => '98765',
  2 => '50545',
  3 => '50525',
  4 => '50520',
  5 => '50555',
  6 => '50575',
)

xpath查询说:

//p                  #find p tags at any level/position in the dom
[starts-with(.       #with a substring at the start of the node's text
, 'Part Number: ')]  #that literally matches "Part Number: "

正则表达式模式使用word boundary metacharacters\b)来区分部件号和非部件号。如果由于某些数据未在您的问题中显示而需要调整模式,请告诉我,我会提供进一步的指导。

最后,我确实调用了一个纯正的正则表达式解决方案,该解决方案在\G或之前的匹配之后将Part Number:合并到“继续”匹配,但这种类型的模式有点难以概念化并且再次在处理有效的html时,dom解析器是一个比正则表达式更稳定的工具。

答案 1 :(得分:0)

如果我理解你的问题你应该能够做到这一点:

Part\sNumber:\s(\d{5})

鉴于您的字符串包含所有Part Number,如下所示:

<p>Part Number: 67001</p>

<p>Part Number: 50545 &ndash; 450g Cartridge 50525 - 2.5kg Tub 50520 - 20kg Pail 50555 - 55kg Drum 50575 *Indent - 175kg Drum</p>

<p>Part Number: 23425 - 55kg Drum 50575 *Indent - 175kg Drum</p>

<p>Part Number: 52232</p>