解析Zend_Dom_Query的结果

时间:2014-07-23 14:00:58

标签: php zend-framework zend-dom-query

我正在尝试使用Zend_Dom_Query解析屏幕抓取的数据,但我正在努力如何正确地应用它来解决我的问题,而我在SO上看到的所有其他答案都做出了一些假设,这些假设非常坦率地用他们的天真吓唬我。

一个典型的例子是How to Pass Array from Zend Dom Query Results to table,其中通过使用对query()方法的单独调用从文档正文中提取数据点对。

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

如果基本假设是 AND 存在相同数量的$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”

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

我知道我可以硬编码每个元素的选择(我现在这样做),但这会产生依赖源数据稳定的脆弱代码。本周数据源又发生了变化,我的硬编码抓取工作无法正常工作。因此,我试图编写健壮的代码,可以找到我想要的东西,而不必关心/了解整体结构(Hmmm - 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()

1 个答案:

答案 0 :(得分:0)

好的..所以我咬紧牙关并为问题写了我自己的解决方案。此代码通过Zend_Dom_Query的结果进行递归,并查找匹配的css选择器。如上所示,代码适用于我,也帮助清理我的代码。对我来说,性能不是问题,但总是和Caveat 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);
    }