preg_match 在不同的标签中

preg_match with in different tags

我需要一些帮助。我正在尝试从网站上抓取一些特定数据。

<tbody>
    <tr style="mso-yfti-irow: 1;">
        <td style="width: 184.4pt; border: none; border-left: solid windowtext 1.5pt; padding: 0cm 5.4pt 0cm 5.4pt;" valign="top" width="307">
            <p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal;">Certifikat springer 1000m</p>
        </td>

        <td style="width: 44.7pt; border: none; border-right: solid windowtext 1.5pt; padding: 0cm 5.4pt 0cm 5.4pt;" valign="top" width="75">
            <p class="MsoNormal" style="margin-bottom: .0001pt; text-align: right; line-height: normal;" align="right">90,-</p>
        </td>
    </tr>

    <tr style="mso-yfti-irow: 2;">
        <td style="width: 184.4pt; border: none; border-left: solid windowtext 1.5pt; padding: 0cm 5.4pt 0cm 5.4pt;" valign="top" width="307">
            <p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal;">Certifikat springer 1200m</p>
        </td>

        <td style="width: 44.7pt; border: none; border-right: solid windowtext 1.5pt; padding: 0cm 5.4pt 0cm 5.4pt;" valign="top" width="75">
            <p class="MsoNormal" style="margin-bottom: .0001pt; text-align: right; line-height: normal;" align="right">100,-</p>
        </td>
    </tr>   
</tbody>

我想要的是从 mos-yfti-irow1 获得 "Certifikat springer 1000" 和从下一个 TD 获得 90,-。但我不想在此输出中从 mos-yfti-irow2 获取数据。

我想开发一些东西,让人们可以比较我们体育团体与不同俱乐部的某些活动的价格。我不太确定该怎么做。

这是我目前拥有的,但无法真正发挥作用

    <?php 

    $file_string = file_get_contents('http://www.mfkviborg.dk/index.php?    option=com_content&view=article&id=21&Itemid=151');

    preg_match_all('/<p class="MsoNormal" style="margin-bottom: .0001pt;(.*)">(.*)<\/p>/i', $file_string, $links);

    ?>

    <p><strong>Links:</strong> <em>(Name - Link)</em><br />
    <?php
    echo '<ol>';
    for($i = 0; $i < count($links[1]); $i++) {
        echo '<li>' . $links[2][$i] . ' - ' . $links[1][$i] . '</li>';
    }
    echo '</ol>';
    ?>
</p>

有什么线索吗?

几个问题:

  • . 不匹配换行符,除非您在正则表达式末尾指定 s 修饰符。所以应该加上。

  • .*是贪心的,所以会尽可能匹配包括一些中间的</p>。它不应该那样做,所以添加一个 ? (在这两种情况下)

问题不大,但仍然值得改变:

  • 第一个捕获组可能没有给你有用的信息,所以去掉那里的括号。

  • .0001中的.被当作任意字符,所以要转义。一种方法是把它写成 [.]

这给你这行代码:

preg_match_all('/<p class="MsoNormal" style="margin-bottom: [.]0001pt;.*?">(.*?)<\/p>/is', 
             $file_string, $links);

使用DOM解析器

请注意,如果您的来源 HTML 只是略有变化(额外的间距或将双引号更改为单引号,或交换属性的位置...),您将遇到问题,并被要求进行调整代码。

使用DOMDocument interface together with a DOMXPath query要好得多。这是它的工作原理:

$doc = new DOMDocument();
libxml_use_internal_errors(true);
    $doc->loadHTML($file_string, LIBXML_NOCDATA | LIBXML_NOWARNING | LIBXML_NOERROR );
libxml_use_internal_errors(false);
$xpath = new DOMXPath($doc);
$nodes = $xpath->query("//p[contains(@class, 'MsoNormal') and contains(@style, 'margin-bottom: .0001pt')]");
foreach ($nodes as $node) {
    echo $node->textContent . "\n";
}

您也可以使用 load 方法代替 loadHTML 方法,并将 URL 作为第一个参数传递。

跟进

您在评论中要求通过 trstyle 属性中的 mso-yfti-irow 进一步过滤输出:

$nodes = $xpath->query("//tr[contains(@style, 'mso-yfti-irow')]//p[contains(@class, 'MsoNormal') and contains(@style, 'margin-bottom: .0001pt')]");

正如其他人所说,请改用像样的解析器,例如DOMDocument():

<?php
# set up the dom
$dom = new DOMDocument();
$dom->loadHTML($your_data_here, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);# | LIBXML_COMPACT | LIBXML_NOENT );

# set up the xpath
$xpath = new DOMXPath($dom);

foreach ($xpath->query("//tr[contains(@style, 'mso-yfti-irow: 1')]") as $row) {
    $text = $xpath->query("td/p/text()", $row);
    $certificate = $text[0]->nodeValue;
    $price = $text[1]->nodeValue;
    echo "$certificate | $price\n";
}
?>

这会产生您的示例字符串:

Certifikat springer 1000m | 90,-

该代码片段设置了 DOM 并随后使用 xpath 表达式对其进行查询,请参阅 PHP.net.

上的文档