使用 PHP 进行网页抓取 – 如何使用开源工具抓取网页

作者:曼森·库瓦尔

Web 抓取可让您从 Internet 上的网页收集数据。它也称为网络爬行或网络数据提取。

PHP 是一种广泛使用的后端脚本语言,用于创建动态网站和 Web 应用程序。并且您可以使用普通的 PHP 代码实现一个网络爬虫。

但由于我们不想重新发明轮子,我们可以利用一些现成的开源 PHP 网页抓取库来帮助我们收集数据。

在本教程中,我们将讨论您可以使用 PHP 来抓取网页的各种工具和服务。我们将讨论的工具是 Guzzle、Goutte、Simple HTML DOM 和无头浏览器 Symfony Panther。

注意:在抓取网站之前,您应该仔细阅读他们的服务条款,以确保它们可以被抓取。抓取数据——即使它是可公开访问的——也可能使网站的服务器过载。(谁知道——如果你礼貌地询问,他们甚至可能会给你一个 API 密钥,这样你就不必刮擦了。😉)

如何设置项目

在我们开始之前,如果您想跟随并尝试代码,这里是您的开发环境的一些先决条件:

  • 确保您已安装最新版本的 PHP。
  • 转到此链接Composer以设置一个Composer,我们将使用它来安装 Web 抓取库的各种 PHP 依赖项。
  • 您选择的编辑器。

完成所有这些后,创建一个项目目录并导航到该目录:

mkdir php_scraper

cd php_scraper

在终端中运行以下两个命令来初始化composer.json文件:

composer init — require=”php >=7.4" — no-interaction

composer update

让我们开始吧。

使用 Guzzle、XML 和 XPath 使用 PHP 进行网页抓取

Guzzle是一个 PHP HTTP 客户端,可让您快速轻松地发送 HTTP 请求。它有一个用于构建查询字符串的简单界面。

XML是一种标记语言,它对文档进行编码,使它们既是人类可读的又是机器可读的。

XPath的是一种查询语言,用于导航和选择XML节点。

让我们看看我们如何一起使用这三个工具来抓取网站。

首先通过在终端中执行以下命令,通过 Composer 安装 Guzzle:

composer require guzzlehttp/guzzle

安装 Guzzle 后,让我们创建一个新的 PHP 文件,我们将向其中添加代码。我们将其命名为 guzzle_requests.php

在本演示中,我们将抓取Books to Scrape网站。您应该能够按照我们在此处定义的相同步骤来抓取您选择的任何网站。

Books to Scrape 网站如下所示:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

我们想提取书籍的标题并将它们显示在终端上。

抓取网站的第一步是了解其 HTML 布局。在这种情况下,您可以通过右键单击列表中第一个产品上方的页面并选择Inspect来查看此页面的 HTML 布局。

这是显示页面源代码片段的屏幕截图:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

您可以看到该列表包含在<ol class=”row”>元素中。下一个直接子元素是<li>元素。

我们想要的是书名。它在<a> 内,而<a>又在<h3> 内,后者在<article> 内,最后在<li>元素内。

要初始化 Guzzle、XML 和 Xpath,请将以下代码添加到guzzle_requests.php文件中:

<?php
# scraping books to scrape: https://books.toscrape.com/
require 'vendor/autoload.php';
$httpClient = new \GuzzleHttp\Client();
$response = $httpClient->get('https://books.toscrape.com/');
$htmlString = (string) $response->getBody();
//add this line to suppress any warnings
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML($htmlString);
$xpath = new DOMXPath($doc);

上面的代码片段会将网页加载到一个字符串中。然后我们使用 XML 解析字符串并将其分配给$xpath变量。

您接下来要做的是定位<a>标签内的文本内容。将以下代码添加到文件中:

$titles = $xpath->evaluate('//ol[@class="row"]//li//article//h3/a');
$extractedTitles = [];
foreach ($titles as $title) {
$extractedTitles[] = $title->textContent.PHP_EOL;
echo $title->textContent.PHP_EOL;
}

在上面的代码片段中,//ol[@class=”row”]获取整个列表。

列表中的每一项都有一个<a>标记,我们的目标是提取该书的实际标题。我们只有一个包含 <a> 的 <h3> 标签,这使得直接定位它变得更容易。

我们使用foreach循环提取文本内容并将它们回显到终端。

在此步骤中,您可以选择对提取的数据执行某些操作,可以将数据分配给数组变量、写入文件或将其存储在数据库中。

您可以通过运行以下命令在终端上使用 PHP 执行该文件。请记住,突出显示的部分是我们命名文件的方式:

php guzzle_requests.php

这应该显示如下内容:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

那很顺利。

现在,如果我们还想获得这本书的价格怎么办?

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

价格恰好在<p>标签内,在 <div> 标签内。如您所见,有多个 <p> 标签和多个 <div> 标签。

为了找到正确的目标,我们将使用 CSS 类选择器,幸运的是,它对每个标签都是唯一的。这是获取价格标签并将其连接到标题字符串的代码片段:

$titles = $xpath->evaluate('//ol[@class="row"]//li//article//h3/a');
$prices = $xpath->evaluate('//ol[@class="row"]//li//article//div[@class="product_price"]//p[@class="price_color"]');
foreach ($titles as $key => $title) {
echo $title->textContent . ' @ '. $prices[$key]->textContent.PHP_EOL;
}

如果您在终端上执行代码,您应该会看到如下内容:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

你的整个代码应该是这样的:

<?php
# scraping books to scrape: https://books.toscrape.com/
require 'vendor/autoload.php';
$httpClient = new \GuzzleHttp\Client();
$response = $httpClient->get('https://books.toscrape.com/');
$htmlString = (string) $response->getBody();
//add this line to suppress any warnings
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML($htmlString);
$xpath = new DOMXPath($doc);
$titles = $xpath->evaluate('//ol[@class="row"]//li//article//h3/a');
$prices = $xpath->evaluate('//ol[@class="row"]//li//article//div[@class="product_price"]//p[@class="price_color"]');
foreach ($titles as $key => $title) {
echo $title->textContent . ' @ '. $prices[$key]->textContent.PHP_EOL;
}

当然,这是一个基本的网络爬虫,你当然可以做得更好。让我们搬到下一个图书馆。

使用 Goutte 使用 PHP 进行网页抓取

Goutte是另一个出色的 PHP HTTP 客户端,专门用于网页抓取。它由Symfony 框架的创建者开发,并提供了一个很好的 API 来从网站的 HTML/XML 响应中抓取数据。

以下是它包含的一些组件,使网络爬行变得简单:

通过在终端上执行以下命令,通过 composer 安装 Goutte:

composer require fabpot/goutte

安装 Goutte 包后,为我们的代码创建一个新的 PHP 文件——我们称之为goutte_requests.php

在本节中,我们将讨论我们在第一节中对 Guzzle 库做了什么。

我们将使用 Goutte从Books to Scrape网站抓取书名。然后我们将看到如何将价格添加到数组变量中并在代码中使用该变量。

在 goutte_requests.php 文件中添加以下代码:

<?php
# scraping books to scrape: https://books.toscrape.com/
require 'vendor/autoload.php';
$httpClient = new \Goutte\Client();
$response = $httpClient->request('GET', 'https://books.toscrape.com/');
$titles = $response->evaluate('//ol[@class="row"]//li//article//h3/a');
$prices = $response->evaluate('//ol[@class="row"]//li//article//div[@class="product_price"]//p[@class="price_color"]');
// we can store the prices into an array
$priceArray = [];
foreach ($prices as $key => $price) {
$priceArray[] = $price->textContent;
}
// we extract the titles and display to the terminal together with the prices
foreach ($titles as $key => $title) {
echo $title->textContent . ' @ '. $priceArray[$key] . PHP_EOL;
}

通过在终端中运行以下命令来执行代码:

php goutte_requests.php

这是输出:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

这是使用 Goutte 进行网页抓取的一种方式。

让我们讨论另一种使用Goutte 附带的CSS Selector组件的方法。CSS 选择器比使用前面方法中显示的 XPath 更直接。

创建另一个 PHP 文件,我们称之为goutte_css_requests.php。将以下代码添加到文件中:

<?php
# scraping books to scrape: https://books.toscrape.com/
require 'vendor/autoload.php';
$httpClient = new \Goutte\Client();
$response = $httpClient->request('GET', 'https://books.toscrape.com/');
// get prices into an array
$prices = [];
$response->filter('.row li article div.product_price p.price_color')->each(function ($node) use (&$prices) {
$prices[] = $node->text();
});
// echo titles and prices
$priceIndex = 0;
$response->filter('.row li article h3 a')->each(function ($node) use ($prices, &$priceIndex) {
echo $node->text() . ' @ ' . $prices[$priceIndex] .PHP_EOL;
$priceIndex++;
});

如您所见,使用 CSS Selector 组件会产生更清晰、更易读的代码。

您可能已经注意到我们使用了&运算符。这确保我们将变量的引用带入“每个”循环,而不仅仅是变量的值。如果&$prices在循环内修改了 ,循环外的实际值也会被修改。

您可以通过来自官方 PHP 文档的参考阅读更多关于赋值的信息

通过运行以下命令在终端中执行该文件:

php goutte_css_requests.php

您应该会看到类似于之前屏幕截图中的输出:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

到目前为止,我们使用 PHP 和 Goutte 的网络抓取工具进展顺利。让我们更深入一点,看看我们是否可以单击链接并导航到不同的页面。

在我们的演示网站Books to Scrape 上,如果您单击一本书的标题,将加载一个页面,显示该书的详细信息,例如:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

我们想看看您是否点击了图书列表中的链接,导航到图书详细信息页面,然后提取描述。检查页面以查看我们将定位的目标:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

我们的目标流将来自<div class=”content”>元素,然后是<div id=”content_inner”>,然后是只出现一次的<article>标签,最后是<p>标签。

我们有几个<p>标签——带有描述的标签是<div class=”content”>级中的第四个。由于数组从 0 开始,我们将在第3 个索引处获取节点。

现在我们知道我们的目标是什么,让我们编写代码。

首先,添加以下 composer 包以帮助进行 HTML5 解析:

composer require masterminds/html5

接下来修改goutte_css_requests.php文件如下:

<?php
# scraping books to scrape: https://books.toscrape.com/
require 'vendor/autoload.php';
$httpClient = new \Goutte\Client();
$response = $httpClient->request('GET', 'https://books.toscrape.com/');
// get prices into an array
$prices = [];
$response->filter('.row li article div.product_price p.price_color')
->each(function ($node) use (&$prices) {
$prices[] = $node->text();
});
// echo title, price, and description
$priceIndex = 0;
$response->filter('.row li article h3 a')
->each(function ($node) use ($prices, &$priceIndex, $httpClient) {
$title = $node->text();
$price = $prices[$priceIndex];
//getting the description
$description = $httpClient->click($node->link())
->filter('.content #content_inner article p')->eq(3)->text();
// display the result
echo "{$title} @ {$price} : {$description}\n\n";
$priceIndex++;
});

如果您在终端中执行该文件,您应该会看到显示的标题、价格和描述:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

使用 Goutte CSS Selector组件和单击页面的选项,您可以轻松地抓取包含多个页面的整个网站,并根据需要提取尽可能多的数据。

使用简单的 HTML DOM 在 PHP 中抓取网页

Simple HTML DOM是另一个简约的 PHP 网页抓取库,可用于抓取网站。让我们讨论如何使用这个库来抓取网站。就像在前面的示例中一样,我们将抓取 Books to Scrape 网站。

在安装包之前,请修改您的 composer.json 文件并在require:{}块下方添加以下代码行以避免出现版本控制错误:

"minimum-stability": "dev",
"prefer-stable": true

现在,您可以使用以下命令安装库:

composer require simplehtmldom/simplehtmldom

安装库后,创建一个名为simplehtmldom_requests.php的新 PHP 文件。

我们已经在前面的部分讨论了我们正在抓取的网页的布局。所以,我们将直接进入代码。将以下代码添加到simplehtmldom_requests.php文件中:

<?php
# scraping books to scrape: https://books.toscrape.com/
require 'vendor/autoload.php';
$httpClient = new \simplehtmldom\HtmlWeb();
$response = $httpClient->load('https://books.toscrape.com/');
// echo the title
echo $response->find('title', 0)->plaintext . PHP_EOL . PHP_EOL;
// get the prices into an array
$prices = [];
foreach ($response->find('.row li article div.product_price p.price_color') as $price) {
$prices[] = $price->plaintext;
}
// echo titles and prices
foreach ($response->find('.row li article h3 a') as $key => $title) {
echo "{$title->plaintext} @ {$prices[$key]} \n";
}

如果您在终端中执行代码,它应该显示结果:

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

您可以从官方 API 文档中找到更多使用Simple HTML DOM 库抓取网页的方法。

使用无头浏览器(Symfony Panther)在 PHP 中进行网页抓取

无头浏览器是没有图形用户界面的浏览器。无头浏览器允许您使用终端在类似于 Web 浏览器的环境中加载网页。这允许您编写代码来控制浏览,就像我们在前面的步骤中所做的那样。

那么为什么这是必要的呢?

在现代 Web 开发中,大多数开发人员使用 JavaScript Web 框架。这些框架在浏览器中生成 HTML 代码。在其他情况下,AJAX 会动态加载内容。

在前面的示例中,我们使用了静态 HTML 页面,因此输出是一致的。

在动态情况下,您使用 JavaScript 和 AJAX 生成 HTML,DOM 树的输出可能会有很大不同。这会导致我们的刮板失败。无头浏览器出现在现代网站中以处理此类问题。

Symfony的豹PHP库无头的浏览器效果很好。您可以使用该库来抓取网站并使用真实浏览器运行测试。

此外,它提供了与 Goutte 库相同的方法,因此您可以使用它代替 Goutte。

与我们在本教程中讨论的以前的网络抓取库不同,Panther 可以执行以下操作:

  • 在网页上执行 JavaScript 代码
  • 支持远程浏览器测试
  • 通过在执行一行代码之前等待其他元素加载来支持元素的异步加载
  • 支持 Firefox 的 Chrome 的所有实现
  • 可以截图
  • 允许在加载页面的上下文中运行您的自定义 JS 代码或 XPath 查询。

我们已经做了很多抓取,所以让我们尝试一些不同的东西。我们将加载一个 HTML 页面并截取该页面的屏幕截图。

使用以下命令安装Symfony Panther

composer require symfony/panther

创建一个新的 php 文件,我们称之为panther_requests.php。将以下代码添加到文件中:

<?php
# scraping books to scrape: https://books.toscrape.com/
require 'vendor/autoload.php';
$httpClient = \Symfony\Component\Panther\Client::createChromeClient();
// for a Firefox client use the line below instead
//$httpClient = \Symfony\Component\Panther\Client::createFirefoxClient();
// get response
$response = $httpClient->get('https://books.toscrape.com/');
// take screenshot and store in current directory
$response->takeScreenshot($saveAs = 'books_scrape_homepage.jpg');
// let's display some book titles
$response->getCrawler()->filter('.row li article h3 a')
->each(function ($node) {
echo $node->text() . PHP_EOL;
});

要在您的系统上运行此代码,您必须安装适用于 Chrome 或 Firefox 的驱动程序,具体取决于您在代码中使用的客户端。

幸运的是,Composer 可以自动为您执行此操作。在终端中执行以下命令来安装和检测驱动程序:

composer require - dev dbrekelmans/bdi && vendor/bin/bdi detect drivers

现在您可以在终端中执行 PHP 文件,它将截取网页的屏幕截图并将其存储在当前目录中。然后它将显示来自网站的标题列表。

使用 PHP 进行网页抓取 - 如何使用开源工具抓取网页

 

点击阅读原文

本文来自投稿,不代表微擎百科立场,如若转载,请注明出处:https://www.w7.wiki/develop/5248.html

发表评论

登录后才能评论