0

これは私が取り組んできた楽しい小さなものです。私は多くの解決策を見つけましたが、本当に正しいものはありません。目標は、この「p タグが 3 つ以上連続している場合にのみ一致する」ことです。

ですから、これは正しいはずだと思いますが、そうではありません。

<p.*>(.*)<\/p>(?=\s?<p){3,}

基本的に私の言葉でこれは言います:

  • タグ内の任意のものと ap タグを一致させます
  • 終了 P タグが表示されるまで、何にでも一致します
  • 前 (2 行以上) の iff にのみ一致し、その後に続く
    • 空白文字 (おそらく) と a < p
    • 3回以上発生した場合

問題は、これが Javascript ではうまく機能するが、PHP では機能しないことです。PHP 言う

Compilation failed: nothing to repeat at offset 28

「何も繰り返さない」ようにするために、さまざまな括弧のラウンドを試しましたが、それは誤った正規表現を引き起こします。

はい、これはWebスクレイピング用ですが、いいえ、悪いことをしていない研究を行っています.

アイデアはありますか?ありがとう!

4

3 に答える 3

1

私には、ステート マシンの XML パーサー (SAX パーサー) が最も適しているように思えます。次に例を示します。

class StateHelper {

    function __construct($filename) {
        $this->p_count = 0;
        $this->p_elements = array();
        $this->in_p = FALSE;
        $this->minimum_in_succession = 2;
        $this->successive_element_data = array();
        $parser = xml_parser_create();
        xml_set_element_handler($parser, array($this, 'start_element'), NULL);
        xml_set_character_data_handler($parser, array($this, 'character_data'));

        $fp = fopen($filename, 'r')
            or die ("Cannot open $filename");

        while ($data = fread($fp, 4096)) {
            xml_parse($parser, $data, feof($fp)) or 
                die(sprintf('XML ERROR: %s at line %d',
                xml_error_string(xml_get_error_code($parser)),
                xml_get_current_line_number($parser)));
        }
        xml_parser_free($parser);
        $this->start_element(NULL, "end", NULL);
    }

    function start_element($parser, $element_name, $element_attrs) {
        if ($element_name == 'P') {
            $this->p_count += 1;
            $this->in_p = TRUE;
        } else {
            if ($this->p_count >= $this->minimum_in_succession) {
                $this->successive_element_data[] = $this->p_elements;
            }
            $this->p_elements = array();
            $this->p_count = 0;
            $this->in_p = FALSE;
        }
    }

    function character_data($parser, $data) {
        if ($this->in_p && strlen(trim($data))) {
            $this->p_elements[] = $data;
        }
    }
}

$parseState = new StateHelper("example.html");
print_r($parseState->successive_element_data);

example.html*

<html>
    <head>
    </head>
    <body>
        <p>Foo1</p>
        <p>Foo2</p>
        <p>Foo3</p>
        <div>
            <p>Bar1</p>
            <p>Bar2</p>
        </div>
        <ul>
            <li>
                <p>Baz1</p>
                <p>Baz2</p>
                <p>Baz3</p>
                <p>Baz4</p>
            </li>
        </ul>
    </body>
</html>

出力

Array
(
    [0] => Array
        (
            [0] => Foo1
            [1] => Foo2
            [2] => Foo3
        )

    [1] => Array
        (
            [0] => Baz1
            [1] => Baz2
            [2] => Baz3
            [3] => Baz4
        )

)
于 2012-08-28T05:00:40.920 に答える
0

ゼロ幅アサーションを繰り返すのは無意味であるため、PHPはおそらくそのエラーを出します。perlとjavascriptの両方がそのことを警告しません。

一度一致させると、実際には何も消費しないので、何度でも一致させることができます。

何をしようとしているのかによっては、正規表現で逃げることができるかもしれません。ただし、実際にHTMLについて何らかの方法で知る必要がある場合は、HTML解析ライブラリを使用するのが最善です。

あなたがしなければならないことは何ですか?

于 2012-08-28T05:11:24.353 に答える
0

代わりにXPathを使用しないのはなぜですか? 式は次のようになります。

//p[name(following-sibling::*[1]) = 'p' and name(following-sibling::*[2]) = 'p']

クエリはp、ドキュメント内の 2 つのp直後に続くすべての場所を検索します。

例 (デモ):

$html = <<< HTML
<div>
    <p>lore</p>
    <p>ipsum</p>
    <p>dolor</p>
    <br/>
    <p>sit</p>
    <p>amet</p> 
</div>
HTML;

このスニペットの最初の要素だけを見つけたいとします。コードは次のようになります。

$query = "//p[
    name(following-sibling::*[1]) = 'p' and 
    name(following-sibling::*[2]) = 'p'
]";

print_r(xpath_match_all($query, $html));

出力:

Array(
    [0] => Array(
        [0] => <p>lore</p>
    )
    [1] => Array(
        [0] => lore
    )
)

結果の配列には、そのクエリの outerHTML と innerHTML が含まれます。

もちろん、xpath_match_all関数を使用する必要はありません。それは単なる便利なユーティリティです。代替手段については、PHP で HTML/XML を解析および処理する方法を参照してください。

于 2012-08-28T07:25:34.240 に答える