【问题标题】:Web Scrape Symfony2 - Impossible Challenge - Crawler ParsingWeb Scrape Symfony2 - 不可能的挑战 - 爬虫解析
【发布时间】:2015-03-26 15:41:11
【问题描述】:

(编辑:我仍然没有找到解决这个问题的方法。$crawler 对象似乎很荒谬,我只想解析它以获取特定的 <td> 文本,这有多难?我也不能serialize()整个爬虫对象并将网页的整个源代码变成一个字符串,否则我只能用困难的方式解析那个字符串。请帮忙。我觉得我已经很好地描述了这个问题,下面。)

下面我使用 Symfony、Goutte 和 DomCrawler 来抓取网页。我一直试图通过其他问题来解决这个问题,但没有成功,但现在我将发布我所有的代码,以使其尽可能直截了当。

我能够获取该页面并获取我正在寻找的第一个数据。第一个是从 javascript 打印的 url,带有一个带有 onclicka 标签,并且是一个长字符串,所以我使用 preg_match 来筛选并得到我需要的东西。

我需要的下一点数据是<td> 标记中的一些文本。问题是,这个网页有 10-20 个不同的 <table> 标签,并且没有 id=""class="" 标签,所以很难隔离。所以我要做的是搜索单词 "Event Title" 然后转到下一个兄弟<td> 标签并提取它的innerHtml,这将是实际的标题。

问题在于,对于第二部分,我似乎无法正确解析 $crawler 对象。我不明白,我之前在serialize() 版本的$crawler 对象上做了一个preg_match,但是对于下半部分,我似乎无法正确解析。

$crawler = $client->request('GET', 'https://movies.randomjunk.com/events/EventServlet?ab=mov&eventId=154367');



$aurl = 'http://movies.randomjunk.com/r.htm?e=154367'; // event url beginning string
$gas = $overview->filter('a[onclick*="' . $aurl . '"]');

$string1 = serialize($gas->filter('a')->attr('onclick')); //TEST
$string1M = preg_match("/(?<=\')(.*?)(?=\')/", $string1, $finalURL); 
$aString = $finalURL[0];
echo "<br><br>" . $aString . "<br><br>";
// IT WORKS UP TO HERE


// $title = $crawler->filterXPath('//td[. = "Event Title"]/following-sibling::td[1]')->each(funtion (Crawler $crawler, $i) {
//     return $node->text();
// }); // No clue why, but this doesn't work. 

$html = $overview->getNode(0)->ownerDocument->saveHTML();


$re = "/>Event\sTitle.*?<\\/td>.*?<td>\\K.*?(?=<\\/td>)/s";
$str = serialize($html);
print_r($str);
preg_match_all($re, $str, $matches);
$gas2 = $matches[0];


echo "<pre>";
    print_r($gas2);
echo "</pre>";

我的preg_match 只返回一个空数组。我认为搜索$crawler 对象存在问题,因为它由许多节点组成。我一直在尝试将其全部转换为 html,然后转换为 preg_match,但它只是拒绝工作。我做了一些print_r 语句,它只返回整个网页。

以下是爬虫对象中的一些 html 示例:

{lots of other html and tables}
<table> 
    <tr>
        <td>Title</td>
        <td>The Harsh Face of Mother Nature</td>
        <td>The Harsh Face of Mother Nature</td>
    </tr>
    .
    .
</table>
{lots of other html and tables} 

而目标是解析整个 page/$crawler 对象并得到标题“大自然的严酷面孔”

我知道这一定是可能的,但任何人都想提供的唯一答案是指向 domcrawler 页面的链接,此时我已经阅读了大约一千次。请帮忙。

【问题讨论】:

  • 您究竟需要从这一切中获得哪些数据?标题?
  • 在底部我列出了目标,从&lt;td&gt; 大自然母亲的严酷面孔中获得标题。这将是动态的并且一直在变化,但之前的&lt;td&gt; 将保持不变,&lt;td&gt;Title&lt;/td&gt;。所以我必须找到&lt;td&gt;,然后去找它的下一个兄弟,然后就会有我的答案。

标签: php regex symfony web-scraping domcrawler


【解决方案1】:

鉴于上面的 html 片段,我能够想出以下的 XPath:

//table/tr/td[.='Title']/following-sibling::td[1]

您可以在 Here 处使用您提供的 html 片段测试 XPath

$html = '<table><tr><td>Event Title</td><td>The Harsh Face of Mother Nature</td><td>The Harsh Face of Mother Nature</td></tr><tr><td>Event Title</td><td>The Harsh Face of Mother Nature</td><td>The Harsh Face of Mother Nature</td></tr><tr><td>Event Title</td><td>The Harsh Face of Mother Nature</td><td>The Harsh Face of Mother Nature</td></tr></table>';
$crawler = new Symfony\Component\DomCrawler\Crawler($html);

$query = "//table/tr/td[.='Event Title']/following-sibling::td[1]";
$crawler->filterXPath($query)->each(function($crawler, $i) {
echo $crawler->text() . PHP_EOL;

});

哪些输出:

The Harsh Face of Mother Nature
The Harsh Face of Mother Nature
The Harsh Face of Mother Nature

更新:测试成功:

$html = '<html><table><tr><td>Event Title</td><td>The Harsh Face of Mother Nature</td><td>The Harsh Face of Mother Nature</td></tr></table><table><tr><td>Event Title</td><td>The Harsh Face of Mother Nature</td><td>The Harsh Face of Mother Nature</td></tr></table><table><tr><td>Event Title</td><td>The Harsh Face of Mother Nature</td><td>The Harsh Face of Mother Nature</td></tr></table></html>';

更新:从网站获得示例 html 后,我能够使用以下 XPath 解析内容:

//td[normalize-space(text()) = 'Event Title']/following-sibling::td[1]

真正的问题是“事件标题”周围的前导和尾随空格。

【讨论】:

  • 现在我收到了The current node list is empty.,所以我必须弄清楚原因。
  • 您能否提供实际页面源代码的复制/粘贴?此外,如果节点列表为空,则问题可能出在 XPath 表达式的 .='Title' 部分。
  • 您的新代码删除了所有错误,但现在我什么也没得到。我将文本放入 echo 中,似乎 each() 函数甚至没有被访问。我的文字是“事件标题”。我不需要在 Xpath 查询中为空格添加 %20 或 \s,对吗?
  • 我的意思是,我输入了“事件标题”,但仍然无法访问 each() 函数。我认为这与$crawler 对象有关。看,我刮了一个巨大的网页,里面装满了很多废话。我在帖子中提到,这组 html 中有超过 10 个表,所以我不仅要访问正确的表,还要访问其中的正确 td。我不确定节点在 Symfony 中是如何工作的,因为文档并没有深入,但我认为它只是访问第一个 &lt;table&gt; 然后不会过去。因此,也许如果我有一个 each() 函数循环遍历所有表,然后检查每个 td。
【解决方案2】:

好吧,你可以做的是在你的 :

中使用一个类

&lt;td class="mytitle"&gt;The Harsh Face of Mother Nature&lt;/td&gt;

您将使用它来过滤您的爬虫,以将您的所有标题放在一个数组中,如下所示:

$titles = $crawler->filter('td.mytitle')->extract(array('_text'));

其中 td.mytitle 是一个 css 选择器,选择带有 mytitle 类的 td 并提取引用节点内文本的 _text。

比正则表达式更简单、更高效...

未测试此代码,但它应该可以工作,您可以在此处获得有关爬虫的更多帮助和更多信息:

http://symfony.com/fr/doc/current/components/dom_crawler.html

【讨论】:

  • 我正在抓取一个我无法控制的网页,因此无法添加类,否则这将非常容易。
  • 你试过 $crawler->filter('html:contains("Title")')->each(function ($node) { $node->siblings()->first() ->extract(array('_text')); });
  • 您好,感谢您的回复。 $crawler-&gt;filter('html:contains("Title")');我先试了这行代码,它返回了整个页面的html。当我print_r 时,我会得到整个页面。我也试过你给我的代码,它返回一个空数组。
【解决方案3】:

这是这个问题的另一个答案。

use Weidner\Goutte\GoutteFacade;
use Symfony\Component\DomCrawler\Crawler;


$crawler = GoutteFacade::request('GET','http://localhost/php_notes.php');

// find the parent table 
$table = $crawler->filter('table')->each(function($table){

    $tdText = $table->filter('td')->each(function ($node){


        $alike = $node->previousAll(); // calculate the elements of the same level above this element :Will return array containing the tags above this tag.

        $elementTag = $alike->eq(0); // find the tag above this <td> tag. 

        if($elementTag->nodeName()=='td'){

            if($elementTag->text()=='Title')
            {
                dump("Title Heading => ".$elementTag->text()); // Title
                dd("Title Value => ".$node->text()); // The Harsh Face of Mother Nature
            }
        }


    });
});

您需要对 Symfony\dom-crawler\Crawler.php 文件的 567 行进行一些更改。

public function nodeName()
    {
        if (!$this->nodes) {
            return null;
            // throw new \InvalidArgumentException('The current node list is empty.');
        }

        return $this->getNode(0)->nodeName;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-07
    • 1970-01-01
    • 2018-01-03
    • 1970-01-01
    • 2012-09-12
    • 1970-01-01
    • 2012-07-14
    相关资源
    最近更新 更多