使用 Laravel 进行网络爬虫:一步步指南

学习如何使用 Laravel 进行网络爬虫,从设置爬虫 API 到利用强大的库,同时遵循道德爬虫的最佳实践。
1 min read
使用Laravel进行网页爬取

在本教程中,您将探索如何在 Laravel 中进行网络爬虫,并学习:

  • 为什么 Laravel 是进行网络爬虫的绝佳技术
  • 最佳的 Laravel 爬虫库有哪些
  • 如何从头开始构建一个 Laravel 网络爬虫 API

让我们开始吧!

可以在 Laravel 中进行网络爬虫吗?

简而言之:是的,Laravel 是进行网络爬虫的可行技术。

Laravel 是一个以其优雅和富有表现力的语法而闻名的强大 PHP 框架。特别是,它使您能够动态创建用于从网络抓取数据的 API。这得益于许多爬虫库的支持,它们简化了从页面获取数据的过程。欲了解更多指导,请查看我们的文章《在 PHP 中进行网络爬虫》

Laravel 因其可扩展性、易于与其他工具集成以及广泛的社区支持,而成为网络爬虫的绝佳选择。其强大的 MVC 架构有助于保持您的爬虫逻辑良好组织和可维护。这在构建复杂或大型的爬虫项目时非常有用。

最佳的 Laravel 网络爬虫库

以下是使用 Laravel 进行网络爬虫的最佳库:

  • BrowserKit:Symfony 框架的一部分,它模拟了 Web 浏览器的 API,用于与 HTML 文档交互。它依赖于 DomCrawler 来导航和抓取 HTML 文档。该库非常适合从 PHP 中的静态页面提取数据。
  • HttpClient:Symfony 的一个组件,用于发送 HTTP 请求。它可以与 BrowserKit 无缝集成。
  • Guzzle:一个强大的 HTTP 客户端,用于向服务器发送 Web 请求并高效处理响应。它有助于检索与网页关联的 HTML 文档。了解如何在 Guzzle 中设置代理
  • Panther:Symfony 的一个组件,为网络爬虫提供了一个无头浏览器。它允许您与需要 JavaScript 进行渲染或交互的动态网站交互。

先决条件

要跟随本教程在 Laravel 中进行网络爬虫,您需要满足以下先决条件:

还推荐使用用于编写 PHP 代码的 IDE。带有 PHP 扩展的 Visual Studio CodeWebStorm 都是不错的选择。

如何在 Laravel 中构建一个网络爬虫 API

在这个逐步指南中,您将学习如何构建一个 Laravel 网络爬虫 API。目标站点是 Quotes 爬虫沙盒站点,爬虫端点将:

  1. 从页面中选择引用的 HTML 元素
  2. 从中提取数据
  3. 以 JSON 格式返回抓取的数据

目标站点如下所示:

用于爬取页面的名言
用于爬取页面的名言

按照以下说明,学习如何在 Laravel 中进行网络爬虫!

步骤 1:设置 Laravel 项目

打开终端,然后运行以下 Composer create-project 命令来初始化您的 Laravel 网络爬虫应用程序:

composer create-project laravel/laravel laravel-scraper

laravel-scraper 文件夹现在将包含一个空白的 Laravel 项目。在您喜欢的 PHP IDE 中加载它。

这是您当前后端的文件结构:

后端的文件结构

太好了!您现在已经有了一个 Laravel 项目。

步骤 2:初始化您的爬虫 API

在项目目录中运行以下 Artisan 命令 来添加一个新的 Laravel 控制器:

php artisan make:controller ScrapingController

这将在 /app/Http/Controllers 目录下创建以下 ScrapingController.php 文件:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ScrapingController extends Controller

{

//

}

ScrapingController 文件中,添加以下 scrapeQuotes() 方法:

public function scrapeQuotes(): JsonResponse

{

// scraping logic...

return response()->json('Hello, World!');

}

目前,该方法返回一个占位符 'Hello, World!' 的 JSON 消息。很快,它将包含一些 Laravel 的爬虫逻辑。

别忘了添加以下导入:

use Illuminate\Http\JsonResponse;

通过在 routes/api.php 中添加以下行,将 scrapeQuotes() 方法关联到一个专用的端点:

use App\Http\Controllers\ScrapingController;

Route::get('/v1/scraping/scrape-quotes', [ScrapingController::class, 'scrapeQuotes']);

太好了!现在是验证 Laravel 爬虫 API 是否按预期工作的时间。请记住,Laravel 的 API 位于 /api 路径下。因此,完整的 API 端点是 /api/v1/scraping/scrape-quotes

使用以下命令启动您的 Laravel 应用程序:

php artisan serve

您的服务器现在应在本地监听端口 8000

使用 cURL 向 /api/v1/scraping/scrape-quotes 端点发出 GET 请求:

curl -X GET 'http://localhost:8000/api/v1/scraping/scrape-quotes'

注意:在 Windows 上,将 curl 替换为 curl.exe。在我们的cURL 网络爬虫指南中了解更多信息。

您应该获得以下响应:

"Hello, World!"

太棒了!示例爬虫 API 工作正常。现在是使用 Laravel 定义一些爬虫逻辑的时候了。

步骤 3:安装爬虫库

在安装任何软件包之前,您需要确定哪些 Laravel 网络爬虫库最适合您的需求。为此,在浏览器中打开目标站点。右键点击页面并选择“检查”以打开开发者工具。然后,转到“网络”选项卡,重新加载页面,并访问“Fetch/XHR”部分:

访问 'FetchXHR' 部分

如您所见,网页未执行任何AJAX 请求。这意味着它未在客户端动态加载数据。因此,它是一个静态页面,所有数据都嵌入在 HTML 文档中。

由于页面是静态的,您无需使用无头浏览器库来抓取它。虽然您仍然可以使用浏览器自动化工具,但这只会引入不必要的开销。推荐的方法是使用 Symfony 的 BrowserKitHttpClient 组件。

使用以下命令将 symfony/browser-kitsymfony/http-client 组件添加到您的项目依赖项中:

composer require symfony/browser-kit symfony/http-client

做得好!您现在拥有在 Laravel 中进行数据爬取所需的一切。

步骤 4:下载目标页面

ScrapingController 中导入 BrowserKitHttpClient

use Symfony\Component\BrowserKit\HttpBrowser;

use Symfony\Component\HttpClient\HttpClient;

scrapeQuotes() 中,初始化一个新的 HttpBrowser 对象:

$browser = new HttpBrowser(HttpClient::create());

这使您能够通过模拟浏览器行为来发出 HTTP 请求。同时,请记住,它并未在真实浏览器中执行请求。HttpBrowser 只是提供类似浏览器的功能,例如 Cookie 和会话处理。

使用 request() 方法对目标页面的 URL 执行 HTTP GET 请求:

$crawler = $browser->request('GET', 'https://quotes.toscrape.com/');

结果将是一个 Crawler 对象,它会自动解析服务器返回的 HTML 文档。该类还提供节点选择和数据提取功能。

您可以通过从爬虫中提取页面的 HTML 来验证上述逻辑是否有效:

$html = $crawler->outerHtml();

为了测试,让您的 API 返回这些数据。

您的 scrapeQuotes() 函数现在将如下所示:

public function scrapeQuotes(): JsonResponse

{

// initialize a browser-like HTTP client

$browser = new HttpBrowser(HttpClient::create());

// download and parse the HTML of the target page

$crawler = $browser->request('GET', 'https://quotes.toscrape.com/');

// get the page outer HTML and return it

$html = $crawler->outerHtml();

return response()->json($html);

}

太棒了!您的 API 现在将返回:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>Quotes to Scrape</title>

<link rel="stylesheet" href="/static/bootstrap.min.css">

<link rel="stylesheet" href="/static/main.css">

</head>

<!-- omitted for brevity ... -->

步骤 5:检查页面内容

要定义数据提取逻辑,检查目标页面的 HTML 结构至关重要。

因此,在浏览器中打开 Quotes To Scrape。然后,右键点击一个引用的 HTML 元素并选择“检查”选项。在浏览器的开发者工具中,展开 HTML 并开始研究它:

检查引用元素

在这里,注意每个引用卡片都是一个 .quote HTML 节点,包含:

  1. 包含引用文本的 .text 元素
  2. 带有作者姓名的 .author 节点
  3. 多个 .tag 元素,每个显示一个标签

使用上述 CSS 选择器,您拥有了在 Laravel 中执行网络爬虫所需的一切。使用这些选择器来定位感兴趣的 DOM 元素,并在下一步中从中提取数据!

步骤 6:准备进行网络爬虫

由于目标页面包含多个引用,创建一个数据结构来存储抓取的数据是必要的。数组将是理想的选择:

quotes = []

然后,使用 Crawler 类的 filter() 方法选择所有引用元素:

$quote_html_elements = $crawler->filter('.quote');

这将返回页面上与指定的 .quote CSS 选择器匹配的所有 DOM 节点。

接下来,迭代它们,并准备在每个节点上应用数据提取逻辑:

foreach ($quote_html_elements as $quote_html_element) {

// create a new quote crawler

$quote_crawler = new Crawler($quote_html_element);

// scraping logic...

}

请注意,filter() 返回的 DOMNode 对象不提供节点选择的方法。因此,您需要创建一个仅限于特定 HTML 引用元素的本地 Crawler 实例。

为了使上述代码正常工作,添加以下导入:

use Symfony\Component\DomCrawler\Crawler;

您无需手动安装 DomCrawler 包。这是因为它是 BrowserKit 组件的直接依赖项。

太棒了!您离 Laravel 网络爬虫的目标又近了一步。

步骤 7:实现数据爬取

foreach 循环中:

  1. .text.author.tag 元素中提取感兴趣的数据
  2. 使用它们填充一个新的 $quote 对象
  3. 将新的 $quote 对象添加到 $quotes

首先,选择 HTML 引用元素内的 .text 元素。然后,使用 text() 方法从中提取内部文本:

$text_html_element = $quote_crawler->filter('.text');

$raw_text = $text_html_element->text();

请注意,每个引用都被包含在 \u201c\u201d 特殊字符中。您可以使用 PHP 的 str_replace() 函数将其删除,如下所示:

$text = str_replace(["\u{201c}", "\u{201d}"], '', $raw_text);

同样,使用以下代码抓取作者信息:

$author_html_element = $quote_crawler->filter('.author');

$author = $author_html_element->text();

抓取标签可能有点挑战性。由于单个引用可以有多个标签,您需要定义一个数组并逐个抓取每个标签:

$tag_html_elements = $quote_crawler->filter('.tag');

$tags = [];

foreach ($tag_html_elements as $tag_html_element) {

$tag = $tag_html_element->textContent;

$tags[] = $tag;

}

请注意,filter() 返回的 DOMNode 元素不提供 text() 方法。相应地,它们提供了 textContent 属性。

整个 Laravel 数据爬取逻辑如下所示:

// create a new quote crawler

$quote_crawler = new Crawler($quote_html_element);

// perform the data extraction logic

$text_html_element = $quote_crawler->filter('.text');

$raw_text = $text_html_element->text();

// remove special characters from the raw text information

$text = str_replace(["\u{201c}", "\u{201d}"], '', $raw_text);

$author_html_element = $quote_crawler->filter('.author');

$author = $author_html_element->text();

$tag_html_elements = $quote_crawler->filter('.tag');

$tags = [];

foreach ($tag_html_elements as $tag_html_element) {

$tag = $tag_html_element->textContent;

$tags[] = $tag;

}

好了!您离最终目标又近了一步。

步骤 8:返回抓取的数据

使用抓取的数据创建一个 $quote 对象,并将其添加到 $quotes

$quote = [

'text' => $text,

'author' => $author,

'tags' => $tags

];

$quotes[] = $quote;

接下来,使用 $quotes 列表更新 API 响应数据:

return response()->json(['quotes' => $quotes]);

在爬虫循环结束时,$quotes 将包含:

array(10) {

[0]=>

array(3) {

["text"]=>

string(113) "The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking."

["author"]=>

string(15) "Albert Einstein"

["tags"]=>

array(4) {

[0]=>

string(6) "change"

[1]=>

string(13) "deep-thoughts"

[2]=>

string(8) "thinking"

[3]=>

string(5) "world"

}

}

// omitted for brevity...

[9]=>

array(3) {

["text"]=>

string(48) "A day without sunshine is like, you know, night."

["author"]=>

string(12) "Steve Martin"

["tags"]=>

array(3) {

[0]=>

string(5) "humor"

[1]=>

string(7) "obvious"

[2]=>

string(6) "simile"

}

}

}

非常好!这些数据将被序列化为 JSON,并由 Laravel 爬虫 API 返回。

步骤 9:整合所有内容

以下是 Laravel 中 ScrapingController 文件的完整代码:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Illuminate\Http\JsonResponse;

use Symfony\Component\BrowserKit\HttpBrowser;

use Symfony\Component\HttpClient\HttpClient;

use Symfony\Component\DomCrawler\Crawler;

class ScrapingController extends Controller

{

public function scrapeQuotes(): JsonResponse

{

// initialize a browser-like HTTP client

$browser = new HttpBrowser(HttpClient::create());

// download and parse the HTML of the target page

$crawler = $browser->request('GET', 'https://quotes.toscrape.com/');

// where to store the scraped data

$quotes = [];

// select all quote HTML elements on the page

$quote_html_elements = $crawler->filter('.quote');

// iterate over each quote HTML element and apply

// the scraping logic

foreach ($quote_html_elements as $quote_html_element) {

// create a new quote crawler

$quote_crawler = new Crawler($quote_html_element);

// perform the data extraction logic

$text_html_element = $quote_crawler->filter('.text');

$raw_text = $text_html_element->text();

// remove special characters from the raw text information

$text = str_replace(["\u{201c}", "\u{201d}"], '', $raw_text);

$author_html_element = $quote_crawler->filter('.author');

$author = $author_html_element->text();

$tag_html_elements = $quote_crawler->filter('.tag');

$tags = [];

foreach ($tag_html_elements as $tag_html_element) {

$tag = $tag_html_element->textContent;

$tags[] = $tag;

}

// create a new quote object

// with the scraped data

$quote = [

'text' => $text,

'author' => $author,

'tags' => $tags

];

// add the quote object to the quotes array

$quotes[] = $quote;

}

var_dump($quotes);

return response()->json(['quotes' => $quotes]);

}

}

是时候测试了!

启动您的 Laravel 服务器:

php artisan serve

然后,向 /api/v1/scraping/scrape-quotes 端点发出 GET 请求:

curl -X GET 'http://localhost:8000/api/v1/scraping/scrape-quotes'

您将获得以下结果:

{

"quotes": [

{

"text": "The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.",

"author": "Albert Einstein",

"tags": [

"change",

"deep-thoughts",

"thinking",

"world"

]

},

// omitted for brevity...

{

"text": "A day without sunshine is like, you know, night.",

"author": "Steve Martin",

"tags": [

"humor",

"obvious",

"simile"

]

}

]

}

瞧!不到 100 行代码,您就在 Laravel 中完成了网络爬虫。

下一步

您在此构建的 API 只是 Laravel 在网络爬虫方面所能实现的基本示例。要将您的项目提升到下一个水平,请考虑以下改进:

  • 实现网络爬行:目标站点包含分布在多个页面上的多个引用。这是一个常见的情况,需要通过网络爬行来完整地检索数据。阅读我们的文章,了解什么是网络爬虫
  • 调度您的爬虫任务:添加一个调度程序,定期调用您的 API,将数据存储在数据库中,确保您始终拥有新鲜数据。
  • 集成代理:从同一 IP 发出多个请求可能会被反爬虫措施阻止。为避免这种情况,请考虑在您的 PHP 爬虫中集成住宅代理

保持您的 Laravel 网络爬虫操作道德且尊重他人

网络爬虫是为各种目的收集有价值数据的有效方法。然而,目标是负责任地检索数据,而不是损害目标站点。因此,以正确的预防措施进行爬虫非常重要。

请遵循以下提示,以确保负责任的 Laravel 网络爬虫:

  • 检查并遵守站点的服务条款:在爬取站点之前,查看其服务条款。这些通常包含有关版权、知识产权和使用其数据的指南。
  • 尊重 robots.txt 文件:站点的 robots.txt 文件定义了自动爬虫应如何访问其页面的规则。为了保持道德实践,请遵守这些指南。在我们的robots.txt 网络爬虫指南中了解更多信息。
  • 仅针对公开可用的信息:专注于公开可访问的数据。避免抓取受登录凭据或其他形式授权保护的页面。未经适当许可,针对私人或敏感数据是不道德的,可能会导致法律后果。
  • 限制请求的频率:在短时间内发出过多请求可能会使服务器过载,影响所有用户的站点性能。这也可能触发限速措施并导致您被阻止。通过在请求之间添加随机延迟,避免对目标服务器造成负担。
  • 依赖可靠且最新的爬虫工具:偏好信誉良好的提供商,并选择维护良好且定期更新的工具。这可确保它们与最新的道德 Laravel 爬虫实践保持一致。如果您不确定,请查看我们的文章如何选择最佳的网络爬虫服务

结论

在本指南中,您了解了为什么 Laravel 是构建网络爬虫 API 的良好框架。您还探索了一些最佳的爬虫库。然后,您学习了如何创建一个 Laravel 网络爬虫 API,以动态从目标页面提取数据。正如您所见,使用 Laravel 进行网络爬虫非常简单,只需几行代码。

问题是,大多数网站使用反机器人和反爬虫解决方案来保护其数据。这些技术可以检测并阻止您的自动请求。幸运的是,Bright Data 提供了一套使爬虫变得容易的解决方案:

  • 抓取浏览器:一个基于云的可控浏览器,提供 JavaScript 渲染功能,同时为您处理 CAPTCHA、浏览器指纹、自动重试等。它可与最流行的自动化浏览器库(如 Playwright 和 Puppeteer)集成。
  • 网络解锁器:一个解锁 API,可以无缝返回任何页面的干净 HTML,绕过任何反爬虫措施。
  • 网络抓取API:用于以编程方式访问数十个流行域的结构化网络数据的端点。

不想处理网络爬虫但仍对在线数据感兴趣?探索 Bright Data 的现成数据集!

立即注册并开始您的免费试用。