跳到内容

BrowserKit 组件

编辑此页

BrowserKit 组件模拟 Web 浏览器的行为,允许你以编程方式发送请求、点击链接和提交表单。

安装

1
$ composer require symfony/browser-kit

注意

如果在 Symfony 应用程序之外安装此组件,则必须在代码中引入 vendor/autoload.php 文件,以启用 Composer 提供的类自动加载机制。阅读 这篇文章了解更多详情。

基本用法

另请参阅

本文介绍了如何在任何 PHP 应用程序中将 BrowserKit 功能用作独立组件。阅读 Symfony 功能测试 文章,了解如何在 Symfony 应用程序中使用它。

创建客户端

该组件仅提供了一个抽象客户端,没有提供任何可用于 HTTP 层的后端。要创建你自己的客户端,你必须扩展 AbstractBrowser 类并实现 doRequest() 方法。此方法接受一个请求,并应返回一个响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Acme;

use Symfony\Component\BrowserKit\AbstractBrowser;
use Symfony\Component\BrowserKit\Response;

class Client extends AbstractBrowser
{
    protected function doRequest($request): Response
    {
        // ... convert request into a response

        return new Response($content, $status, $headers);
    }
}

有关基于 HTTP 层的浏览器的简单实现,请查看 HttpBrowser,它由 此组件 提供。对于基于 HttpKernelInterface 的实现,请查看 HttpClientKernel,它由 HttpKernel 组件 提供。

发送请求

使用 request() 方法发送 HTTP 请求。前两个参数是 HTTP 方法和请求的 URL。

1
2
3
4
use Acme\Client;

$client = new Client();
$crawler = $client->request('GET', '/');

request() 方法返回的值是 Crawler 类的实例,它由 DomCrawler 组件 提供,允许以编程方式访问和遍历 HTML 元素。

jsonRequest() 方法定义了与 request() 方法相同的参数,是一个将请求参数转换为 JSON 字符串并设置所需 HTTP 标头的快捷方式。

1
2
3
4
5
use Acme\Client;

$client = new Client();
// this encodes parameters as JSON and sets the required CONTENT_TYPE and HTTP_ACCEPT headers
$crawler = $client->jsonRequest('GET', '/', ['some_parameter' => 'some_value']);

xmlHttpRequest() 方法定义了与 request() 方法相同的参数,是一个发送 AJAX 请求的快捷方式。

1
2
3
4
5
use Acme\Client;

$client = new Client();
// the required HTTP_X_REQUESTED_WITH header is added automatically
$crawler = $client->xmlHttpRequest('GET', '/');

AbstractBrowser 能够模拟链接点击。传递链接的文本内容,客户端将执行所需的 HTTP GET 请求来模拟链接点击。

1
2
3
4
5
6
use Acme\Client;

$client = new Client();
$client->request('GET', '/product/123');

$crawler = $client->clickLink('Go elsewhere...');

如果你需要 Link 对象,它可以访问链接属性(例如 $link->getMethod(), $link->getUri()),请使用此其他方法。

1
2
3
4
// ...
$crawler = $client->request('GET', '/product/123');
$link = $crawler->selectLink('Go elsewhere...')->link();
$client->click($link);

click()clickLink() 方法可以接受一个可选的 serverParameters 参数。此参数允许在点击链接时发送附加信息,例如 headers。

1
2
3
4
5
6
7
8
9
10
11
use Acme\Client;

$client = new Client();
$client->request('GET', '/product/123');

// works both with `click()`...
$link = $crawler->selectLink('Go elsewhere...')->link();
$client->click($link, ['X-Custom-Header' => 'Some data']);

// ... and `clickLink()`
$crawler = $client->clickLink('Go elsewhere...', ['X-Custom-Header' => 'Some data']);

提交表单

AbstractBrowser 也能够提交表单。首先,使用表单的任何按钮选择表单,然后在提交之前覆盖其任何属性(方法、字段值等)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use Acme\Client;

$client = new Client();
$crawler = $client->request('GET', 'https://github.com/login');

// find the form with the 'Log in' button and submit it
// 'Log in' can be the text content, id, value or name of a <button> or <input type="submit">
$client->submitForm('Log in');

// the second optional argument lets you override the default form field values
$client->submitForm('Log in', [
    'login' => 'my_user',
    'password' => 'my_pass',
    // to upload a file, the value must be the absolute file path
    'file' => __FILE__,
]);

// you can override other form options too
$client->submitForm(
    'Log in',
    ['login' => 'my_user', 'password' => 'my_pass'],
    // override the default form HTTP method
    'PUT',
    // override some $_SERVER parameters (e.g. HTTP headers)
    ['HTTP_ACCEPT_LANGUAGE' => 'es']
);

如果你需要 Form 对象,它可以访问表单属性(例如 $form->getUri(), $form->getValues(), $form->getFields()),请使用此其他方法。

1
2
3
4
5
6
7
8
9
// ...

// select the form and fill in some values
$form = $crawler->selectButton('Log in')->form();
$form['login'] = 'symfonyfan';
$form['password'] = 'anypass';

// submit that form
$crawler = $client->submit($form);

自定义 Header 处理

传递给 request() 方法的可选 HTTP headers 遵循 FastCGI 请求格式(大写,下划线代替短划线,并以 HTTP_ 为前缀)。在将这些 headers 保存到请求之前,它们会被转换为小写,HTTP_ 被剥离,下划线转换为短划线。

如果你正在向对 header 大小写或标点符号有特殊规则的应用程序发送请求,请覆盖 getHeaders() 方法,该方法必须返回 headers 的关联数组。

1
2
3
4
5
6
7
8
9
protected function getHeaders(Request $request): array
{
    $headers = parent::getHeaders($request);
    if (isset($request->getServer()['api_key'])) {
        $headers['api_key'] = $request->getServer()['api_key'];
    }

    return $headers;
}

Cookies

检索 Cookies

AbstractBrowser 实现通过 CookieJar 公开 cookies(如果有),它允许你在使用客户端发送请求时存储和检索任何 cookie。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use Acme\Client;

// Make a request
$client = new Client();
$crawler = $client->request('GET', '/');

// Get the cookie Jar
$cookieJar = $client->getCookieJar();

// Get a cookie by name
$cookie = $cookieJar->get('name_of_the_cookie');

// Get cookie data
$name       = $cookie->getName();
$value      = $cookie->getValue();
$rawValue   = $cookie->getRawValue();
$isSecure   = $cookie->isSecure();
$isHttpOnly = $cookie->isHttpOnly();
$isExpired  = $cookie->isExpired();
$expires    = $cookie->getExpiresTime();
$path       = $cookie->getPath();
$domain     = $cookie->getDomain();
$sameSite   = $cookie->getSameSite();

注意

这些方法仅返回尚未过期的 cookies。

循环遍历 Cookies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
use Acme\Client;

// Make a request
$client = new Client();
$crawler = $client->request('GET', '/');

// Get the cookie Jar
$cookieJar = $client->getCookieJar();

// Get array with all cookies
$cookies = $cookieJar->all();
foreach ($cookies as $cookie) {
    // ...
}

// Get all values
$values = $cookieJar->allValues('http://symfony.ac.cn');
foreach ($values as $value) {
    // ...
}

// Get all raw values
$rawValues = $cookieJar->allRawValues('http://symfony.ac.cn');
foreach ($rawValues as $rawValue) {
    // ...
}

设置 Cookies

你还可以创建 cookies 并将其添加到 cookie jar 中,该 cookie jar 可以注入到客户端构造函数中。

1
2
3
4
5
6
7
8
9
10
use Acme\Client;

// create cookies and add to cookie jar
$cookie = new Cookie('flavor', 'chocolate', strtotime('+1 day'));
$cookieJar = new CookieJar();
$cookieJar->set($cookie);

// create a client and set the cookies
$client = new Client([], null, $cookieJar);
// ...

发送 Cookies

请求可以包含 cookies。为此,请使用 request() 方法的 serverParameters 参数来设置 Cookie header 值。

1
2
3
4
5
6
$client->request('GET', '/', [], [], [
    'HTTP_COOKIE' => new Cookie('flavor', 'chocolate', strtotime('+1 day')),

    // you can also pass the cookie contents as a string
    'HTTP_COOKIE' => 'flavor=chocolate; expires=Sat, 11 Feb 2023 12:18:13 GMT; Max-Age=86400; path=/'
]);

注意

使用 serverParameters 参数设置的所有 HTTP headers 都必须以 HTTP_ 为前缀。

历史记录

客户端存储你的所有请求,允许你在历史记录中前进和后退。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use Acme\Client;

$client = new Client();
$client->request('GET', '/');

// select and click on a link
$link = $crawler->selectLink('Documentation')->link();
$client->click($link);

// go back to home page
$crawler = $client->back();

// go forward to documentation page
$crawler = $client->forward();

你可以使用 restart() 方法删除客户端的历史记录。这也将删除所有 cookies。

1
2
3
4
5
6
7
use Acme\Client;

$client = new Client();
$client->request('GET', '/');

// reset the client (history and cookies are cleared too)
$client->restart();

发送外部 HTTP 请求

到目前为止,本文中的所有示例都假设你正在向自己的应用程序发送内部请求。但是,当向外部网站和应用程序发送 HTTP 请求时,你可以运行完全相同的示例。

首先,安装和配置 HttpClient 组件。然后,使用 HttpBrowser 创建将发送外部 HTTP 请求的客户端。

1
2
3
4
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;

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

你现在可以使用本文中显示的任何方法来提取信息、点击链接、提交表单等。这意味着你不再需要使用专用的网络爬虫或抓取工具,例如 Goutte

1
2
3
4
5
6
7
8
$browser = new HttpBrowser(HttpClient::create());

$browser->request('GET', 'https://github.com');
$browser->clickLink('Sign in');
$browser->submitForm('Sign in', ['login' => '...', 'password' => '...']);
$openPullRequests = trim($browser->clickLink('Pull requests')->filter(
    '.table-list-header-toggle a:nth-child(1)'
)->text());

提示

你还可以使用 HTTP 客户端选项,如 ciphersauth_basicquery。它们必须作为默认选项参数传递给 HTTP 浏览器使用的客户端。

处理 HTTP 响应

当使用 BrowserKit 组件时,你可能需要处理你发送的请求的响应。为此,调用 HttpBrowser 对象的 getResponse() 方法。此方法返回浏览器收到的最后一个响应。

1
2
3
4
$browser = new HttpBrowser(HttpClient::create());

$browser->request('GET', 'https://foo.com');
$response = $browser->getResponse();

如果你正在发送导致 JSON 响应的请求,你可以使用 toArray() 方法将 JSON 文档转换为 PHP 数组,而无需显式调用 json_decode()

1
2
3
4
5
$browser = new HttpBrowser(HttpClient::create());

$browser->request('GET', 'https://api.foo.com');
$response = $browser->getResponse()->toArray();
// $response is a PHP array of the decoded JSON contents
这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可证获得许可。
TOC
    版本