xpath: извлечь данные из узла, используя xpath - PullRequest
1 голос
/ 19 января 2012

Я хочу извлечь только рейтинг продаж (который в данном случае равен 5)

Рейтинг бестселлеров Amazon: # 5 в книгах (см. Top 100 в книгах)

С веб-страницы: http://www.amazon.com/Mockingjay-Hunger-Games-Book-3/dp/0439023513/ref=tmm_hrd_title_0

До сих пор я дошел до этого, который выбирает "AmazonРейтинг бестселлеров: ":

//li[@id='SalesRank']/b/text()

Я использую PHP DOMDocument и DOMXPath.

Ответы [ 2 ]

2 голосов
/ 19 января 2012

Вы можете использовать чистый XPath:

substring-before(normalize-space(/html/body//ul/li[@id="SalesRank"]/b[1]/following-sibling::text()[1])," ")

Однако, если ваш ввод немного беспорядочный, вы можете получить более надежные результаты, используя XPath для захвата текста родительского узла, а затем используя регулярное выражение для текста, чтобы получить нужную вещь.

Демонстрация обоих методов с использованием PHP с DOMDocument и DOMXPath:

// Method 1: XPath only
$xp_salesrank = 'substring-before(normalize-space(/html/body//li[@id="SalesRank"]/b[1]/following-sibling::text()[1])," ")';

// Method 2: XPath and Regex
$regex_ranktext = 'string(/html/body//li[@id="SalesRank"])';
$regex_salesrank = '/Best\s+Sellers\s+Rank:\s*(#\d+)\s+/ui';

// Test URLs
$urls = array(
    'http://rads.stackoverflow.com/amzn/click/0439023513',
    'http://www.amazon.com/Mockingjay-Final-Hunger-Games-ebook/dp/B003XF1XOQ/ref=tmm_kin_title_0?ie=UTF8&m=AG56TWVU5XWC2',
);

// Results
$ranks = array();
$ranks_regex = array();

foreach ($urls as $url) {
    $d = new DOMDocument();
    $d->loadHTMLFile($url);
    $xp = new DOMXPath($d);

    // Method 1: use pure xpath
    $ranks[] = $xp->evaluate($xp_salesrank);

    // Method 2: use xpath to get a section of text, then regex for more specific item
    // This method is probably more forgiving of bad HTML.
    $rank_regex = '';
    $ranktext = $xp->evaluate($regex_ranktext);
    if ($ranktext) {
        if (preg_match($regex_salesrank, $ranktext, $matches)) {
            $rank_regex = $matches[1];
        }
    }
    $ranks_regex[] = $rank_regex;

}

assert($ranks===$ranks_regex); // Both methods should be the same.
var_dump($ranks);
var_dump($ranks_regex);

Вывод, который я получаю:

array(2) {
  [0]=>
  string(2) "#4"
  [1]=>
  string(2) "#3"
}
array(2) {
  [0]=>
  string(2) "#4"
  [1]=>
  string(2) "#3"
}
0 голосов
/ 19 января 2012

Используйте :

substring-before(substring-after($expr, '#'), ' ')

, где $expr должно быть заменено вашим выражением :

   substring-before(substring-after(//li[@id='SalesRank']/b, '#'), ' ')

Или, еслиПравое выражение, которое выбирает текстовый узел, (согласно @FrancisAvila):

/html/body//ul/li[@id="SalesRank"]/b[1]/following-sibling::text()[1]

, тогда выше становится:

substring-before(
   substring-after(/html/body//ul/li[@id="SalesRank"]
                  /b[1]/following-sibling::text()[1], '#'), 
   ' ')
...