2

我正在尝试使用 Zend_Dom_Query 解析屏幕抓取的数据,但我正在努力如何为我的案例正确应用它,并且我在 SO 上看到的所有其他答案都做出假设,坦率地说,他们的天真吓到了我。

一个典型的例子是如何将数组从 Zend Dom 查询结果传递到表,其中通过使用对query()方法的单独调用从文档正文中提取数据点对。

$year = $dom->query('.secondaryInfo');
$rating = $dom->query('.ratingColumn');

基本假设是存在相同数量的$year$rating结果并且它们在文档中彼此正确对齐。如果这些假设中的任何一个是错误的,那么提取的数据就不是毫无价值的——事实上它变成了谎言。

在我的情况下,我试图从一个站点中提取多个数据块,其中每个块名义上是以下形式:

 <p class="main" atrb1="value1">
    <a href="#1" >href text 1</a>
    <span class="sub1"> 
        <span class="span1"></span>
        <span class="sub2">
            <span class="span2">data span2</span>
            <a href="#2">href text 2</a>
        </span>
        <span class="sub3">
            <span class="span3">
                <p>Some other data</p>
                <span class="sub4">
                    <span class="sub5">More data</span>
                </span>
            </span>
        </span>
    </span>
 </p>

对于每个块,我需要从各个部分获取数据:

  • “。主要的”
  • “.main a”
  • “.main.span2”
  • “.main .sub2 a”
  • “.main .span3 p”
  • ETC

然后将数据集作为一个不同的单元处理,而不是作为不同数据的多个集合。

我知道我可以对每个元素的选择进行硬编码(我目前正在这样做),但这会产生依赖于稳定源数据的脆弱代码。本周数据源再次发生变化,我被硬编码的抓取失败所困扰。因此,我正在尝试编写健壮的代码来定位我想要的东西,而无需关心/了解整体结构(嗯 - Linq for php?)

所以在我看来,我希望代码看起来像

$dom = new Zend_Dom_Query($body);
$results = $dom->query('.main');
foreach ($results as $result) 
{
  $data1 = $result->query(".main a");
  $data2 = $result->query(".main .span2");
  $data3 = $result->query(".main .sub a");
  etc

  if ($data1 && $data2 && $data3) {
    Do something
  } else {
    Do something else
  }
}

是否可以使用现有的 Zend/PHP 函数调用来做我想做的事情?还是我需要编写某种自定义函数来实现$result->query()

4

1 回答 1

0

好的..所以我硬着头皮写了我自己的解决方案。此代码通过来自 的结果进行递归Zend_Dom_Query并查找匹配的 css 选择器。如前所述,代码对我有用,也有助于清理我的代码。性能对我来说不是问题,但一如既往地告诫 Emptor。我还留下了一些注释掉的代码,这些代码可以使搜索引导的位置可视化。该代码也是类的一部分,因此$this->在地方使用。

该代码用作:

$dom = new Zend_Dom_Query($body);
$results = $dom->query('.main');
foreach ($results as $result) 
{
  $data1 = $this->domQuery($result, ".sub2 a");
  if (!is_null($data1))
  {
    Do Something
  }
}

<a href="#2">href text 2</a>它在元素下找到<span class="sub2">元素。

    // Function that recurses through a Zend_Dom_Query_Result, looking for css selectors 
    private function recurseDomQueryResult($dom, $depth, $targets, $index, $count)
    {
        // Gross checking
        if ($index<0 || $index >= $count) return NULL;

        // Document where we are
        $element = $dom->nodeName;
        $class = NULL;
        $id = NULL;
//      $href = NULL;

        // Skip unwanted elements
        if ($element == '#text') return NULL;

        if ($dom->hasAttributes()) {
            if ($dom->hasAttribute('class'))
            {
                $class  =  trim($dom->getAttribute('class'));
            }

            if ($dom->hasAttribute('id'))
            {
                $id  =  trim($dom->getAttribute('id'));
            }

//          if ($element == 'a')
//          {
//              if ($dom->hasAttribute('href'))
//              {
//                  $href  =  trim($dom->getAttribute('href'));
//              }
//          }
        }

//      $padding = str_repeat('==', $depth);

//      echo "$padding&lt;$element";
//      if (!($class === NULL)) echo ' class="'.$class.'"';
//      if (!($href === NULL)) echo ' href="'.$href.'"';
//      echo '&gt;<br />'. "\n";

        // See if we have a match for the current element
        $target = $targets[$index];
        $sliced = substr($target,1);
        switch($target[0])
        {
            case '.':
                if ($sliced === $class) {
                    $index++;
                }
                break;

            case '#':
                if ($sliced === $id) {
                    $index++;
                }
                break;

            default:
                if ($target === $element) {
                    $index++;
                }
                break;
        }

        // Check for having matched all
        if ($index == $count) return $dom;

        // We didn't have a match at this level
        // So recursively look at all the children
        $children = $dom->childNodes;
        if ($children) {
            foreach($children as $child)
            {
                if (!is_null(($result = $this->recurseDomQueryResult($child, $depth+1, $targets, $index, $count)))) return $result;
            }
        }

        // Did not find anything
//      echo "$padding&lt;/$element&gt;<br />\n";
        return NULL;
    }


    // User function that you call to find a single element in a Zend_Dom_Query_Result
    // $dom is the Zend_Dom_Query_Result object
    // $path is a path of css selectors, e.g. ".sub2 a"
    private function domQuery($dom, $path)
    {
        $depth = 0;
        $index = 0;
        $targets = explode(' ', $path);
        $count = count($targets);

        return $this->recurseDomQueryResult($dom, $depth, $targets, $index, $count);
    }
于 2014-07-24T13:44:01.977 回答