使用PHP DOM在表中进行刮擦

时间:2014-06-21 18:08:27

标签: php web-scraping

所以我一直在试验PHP Simple HTML Parser和用于PHP 5的内置PHP DOM解析器来搜索网站。

我们以此为例:http://www.ammunitiondepot.com/12-Gauge-Shotgun-Ammo-s/1922.htm

我正在尝试抓取v65-productDisplay表中的所有产品图片。我能够抓住页面上的所有图像,但是我很难尝试只抓取表格中的图像。

这是我用来抓取所有图片的代码:

$html = file_get_contents('http://www.ammunitiondepot.com/12-Gauge-Shotgun-Ammo-s/1922.htm');
$dom = new domDocument;
$dom->loadHTML($html);
$dom->preserveWhiteSpace = false;
$images = $dom->getElementsByTagName('img');
foreach ($images as $image) {
    echo $image->getAttribute('src');
}

1 个答案:

答案 0 :(得分:1)

您获得所有<img> src - 属性的问题基本上是因为您运行

$images = $dom->getElementsByTagName('img');

这表示您希望从整个文档中获取所有<img>个元素。但实际上,您只想获得特定表中的图像。至少你现在这么认为,但让我们暂时这样做。您要查找的表是文档顺序中的第13个表。那么你现在要解决的问题是你先得到<table>,然后从中获取所有<img>元素:

$tables = $dom->getElementsByTagName('table');
$table  = $tables->item(12); # 13th table in the document
$images = $table->getElementsByTagName('img');

然后,这将为您提供您在问题中要求的图像(src属性的摘录):

//cdn3.volusion.com/sfvhn.cpdkd/v/vspfiles/photos/SPL12-00BK-1.jpg?1381182668
/v/vspfiles/templates/ammunition/images/clear1x1.gif
/v/vspfiles/templates/ammunition/images/clear1x1.gif
/v/vspfiles/templates/ammunition/images/clear1x1.gif
/v/vspfiles/templates/ammunition/images/clear1x1.gif
/v/vspfiles/templates/ammunition/images/buttons/btn_addtocart_small.gif
/v/vspfiles/templates/ammunition/images/clear1x1.gif
...

显然这会导致一些进一步的问题:

  1. 图片列表包含大量您不感兴趣的图片。您需要.jpg个文件,而不是所有那些1像素的GIF或购物车按钮。
  2. 表的编号是硬编码的。这不是很稳定,最好让我们去寻找class="v65-productDisplay"属性(你甚至已经在你的问题中写了它)。
  3. 图片网址是相对于文档的,因此需要解决。
  4. 我现在先说明如何解决前两个问题。

    似乎getElementsByTagName即使有用也不能灵活满足您的拼抢需求。还有一种更好的方法来查询文档中的元素,称为xpathref)。它是一种查询语言,您可以在其中表达所需的元素。所以我们希望特定表中的图像src属性是jpegs。 xpath查询看起来像这样:

    //table[@class="v65-productDisplay"]/tr/td/a/img/@src[contains(., ".jpg")]
    

    这是在DOMXPath的帮助下运行的,如下所示:

    $xpath = new DOMXPath($dom);
    $srcQuery = '//table[@class="v65-productDisplay"]/tr/td/a/img/@src[contains(., ".jpg")]';
    /** @var DOMAttr $src */
    foreach ($xpath->query($srcQuery) as $src) {
        echo $src->nodeValue, "\n";
    }
    

    现在已经将列表大大减少到你正在寻找的内容,而不是那么冗长:

    //cdn3.volusion.com/sfvhn.cpdkd/v/vspfiles/photos/SPL12-00BK-1.jpg?1381182668
    //cdn3.volusion.com/sfvhn.cpdkd/v/vspfiles/photos/GTL1275-1.jpg?1380206953
    //cdn3.volusion.com/sfvhn.cpdkd/v/vspfiles/photos/SS12L8-1.jpg?1390326206
    //cdn3.volusion.com/sfvhn.cpdkd/v/vspfiles/photos/LEF127RS-1.jpg?1368458526
    //cdn3.volusion.com/sfvhn.cpdkd/v/vspfiles/photos/LE13300-1.jpg?1368458467
    //cdn3.volusion.com/sfvhn.cpdkd/v/vspfiles/photos/ADLE13300AC-1.jpg?1393516003
    ...
    

    所以现在只有问题留给了URI的清理,即文档URI的解析(因为文档中没有其他基URI)并且可能清理查询字符串。我是在Net_URL2的帮助下完成的,这里只有src处理:

    /** @var DOMAttr $src */
    foreach ($xpath->query($srcQuery) as $src) {
        $href = $uri->resolve($src->nodeValue);
        $href->setQuery(false);
        echo $href, "\n";
    }
    

    这是一个完整的例子:

    <?php
    /*
     * @link http://stackoverflow.com/questions/24344420/scraping-within-a-table-using-php-dom
     * @auhtor hakre <http://hakre.wordpress.com>
     */
    
    # uses Net_URL2 -- http://pear.php.net/package/Net_URL2/ -- https://packagist.org/packages/pear/net_url2
    require __DIR__ . '/vendor/autoload.php';
    
    $uri   = new Net_URL2('http://www.ammunitiondepot.com/12-Gauge-Shotgun-Ammo-s/1922.htm');
    $cache = '12-Gauge-Shotgun-Ammo-s-1922.htm';
    
    if (is_readable($cache)) {
        $html = file_get_contents($cache);
    } else {
        $options = [
            'http' => [
                'user_agent'    => "Godzilla/42.4 (Gabba Gandalf Client 7.3; C128; Z80) Lord of the Table Weed Edition (KHTML, like Gold Dust Day Gecko) Chrome/97.0.43043.0 Safari/1337.42",
                'max_redirects' => 1, # do not follow redirects
            ]
        ];
        $context = stream_context_create($options);
        $html    = file_get_contents($uri, null, $context);
        file_put_contents($cache, $html);
    }
    
    $dom                     = new domDocument;
    $dom->preserveWhiteSpace = false;
    
    $save = libxml_use_internal_errors(true);
    $dom->loadHTML($html);
    libxml_use_internal_errors($save);
    $dom->documentURI = $uri;
    
    $xpath    = new DOMXPath($dom);
    $srcQuery = '//table[@class="v65-productDisplay"]/tr/td/a/img/@src[contains(., ".jpg")]';
    /** @var DOMAttr $src */
    foreach ($xpath->query($srcQuery) as $src) {
        $href = $uri->resolve($src->nodeValue);
        $href->setQuery(false);
        echo $href, "\n";
    }
    

    以下是供将来参考的HTML:http://pastebin.com/HCTTRm9E