使用Regex从HTML中抓取元素

时间:2016-01-29 22:18:20

标签: php html regex

我已经在Stack Overflow及其姐妹网站上大量阅读了这篇文章,我知道使用正则表达式解析HTML并不是最好的做法。我没有尝试进行任何严肃的解析或非常具体的解析,只需抓住几页中非常一致的几个重复元素。然后从这些元素中,我将执行其他网络抓取任务。

我的一般问题在于我试图抓住元素,包括开启和关闭元素。 (特别是在这种情况下,一组' li'元素)

<li id="result_0" data-asin="<8 char hash>"> ........ </li>
<li id="result_1" data-asin="<8 char hash>"> ........ </li>
<li id="result_2" data-asin="<8 char hash>"> ........ </li>
<li id="result_3" data-asin="<8 char hash>"> ........ </li>
<li id="result_4" data-asin="<8 char hash>"> ........ </li>
....
<li id="result_15" data-asin="<8 char hash>"> ........ </li>
<li id="result_16" data-asin="<8 char hash>"> ........ </li>
<li id="result_17" data-asin="<8 char hash>"> ........ </li>
...

我使用的代码是(PHP):

$pattern = '/[<][l][i]\s[i][d][=]["][a-z]{6}[_][0-9]{1,2}[^li]+/';
$matches = array();
$topics = array();
preg_match_all($pattern, $source, $matches);
var_dump($matches);

和$ matches返回

array (size=1)
    0 => 
        array (size=28)
              0 => string '<li id="result_0" data-as' (length=25)
              1 => string '<li id="result_1" data-as' (length=25)
              2 => string '<li id="result_2" data-as' (length=25)
              3 => string '<li id="result_3" data-as' (length=25)
 ......
 ......

我知道我会停留在&#39; i&#39;在data-asin因为[^ li]但是我不知道该如何说:接受换行符和除&#34;&lt; / li&gt;&#34;

之外的所有字符

注意:在LI元素之间没有其他LI元素可以搞砸寻找结束LI元素

还有:

[<][l][i]\s[i][d][=]["]

开始我的模式看起来像垃圾。有没有办法将文字文本分组并搜索它? (例如:寻找 - &gt;&#34;&lt; li id =&#39;&#34;)我假设这将导致我搜索我的&#34;&lt; / li&gt;& #34;同样。

对于最后一次&lt; / li&gt;,如何搜索UNTIL&lt; / li&gt;所有内容?

6 个答案:

答案 0 :(得分:2)

真的非常更好地使用解析器和一些xpath查询,例如要获取所有列表项,您只需要两行:

$xml = simplexml_load_file($url);
$items = $xml->xpath("//li[starts-with(@id, 'result_')]");
foreach ($items as $item) {
    // do sth. with the item
}

特别是当您的data-asin属性包含<>

答案 1 :(得分:1)

我的前言是我不熟悉PHP,但正则表达式在各种语言中通常是相同或相似的。

简化模式:/<li id="result_\d+" data-asin=".{8}">[^<]+<\/li>/

如果您只想盲目地抓取所有li代码,无论iddata-asin属性如何,都可以进一步简化。

答案 2 :(得分:1)

排序的正则表达式

<(li|ol|otherelement)[\s\S]+?<\/(\1)>

在第一个()中,你可以放置你想要你的正则表达式找到的所有元素,并且(\ 1)反向引用将确保匹配它们的结束标记。 [\ S \ S] +?基本上是所有人物,甚至新行,一个或多个? - 这使得它很难确保捕获该元素类型的第一个可能的结束标记。

答案 3 :(得分:1)

<li id="result_0" data-asin="<8 char hash>"> ........ </li>

~\Q<li id="\E([^"]*)\Q" data-asin="\E([a-zA-Z]{8})\Q">\E(.*)\Q</li>\E~

https://regex101.com/r/lI0zR5/1

答案 4 :(得分:0)

更简单的模式

(?<=li id\=).*(?=\<\/li\>)

答案 5 :(得分:0)

我能给你的最好建议是阅读正则表达式教程,了解你的正则表达式方法有什么问题。否则为了获得你想要的东西,用正则表达式搜索纯文本的html并不是好方法。使用html结构:

$dom = new DOMDocument;
$dom->loadHTML($html);

$lis = $dom->getElementsByTagName('li');

foreach($lis as $li) {
    if (preg_match('/^[a-z]{6}_[0-9]{1,2}$/', $li->getAttribute('id')))
        echo $dom->saveHTML($li) . PHP_EOL;
}