Асинхронный анализатор HTML с Goutte

1

Я пытаюсь написать парсер HTML с помощью Goutte. Он работает очень хорошо. Однако Goutte использует запросы блокировки. Это хорошо работает, если вы имеете дело с одной услугой. Если я хочу запросить множество сервисов, которые независимы друг от друга, это вызывает проблему. Goutte использует BrowserKit и Guzzle. Я попытался изменить функцию doRequest, но она не удалась

Аргумент 1 передан в Symfony\Component\BrowserKit\CookieJar :: updateFromResponse() должен быть экземпляром Symfony\Component\BrowserKit\Response

 protected function doRequest($request)
    {
        $headers = array();
        foreach ($request->getServer() as $key => $val) {
            $key = strtolower(str_replace('_', '-', $key));
            $contentHeaders = array('content-length' => true, 'content-md5' => true, 'content-type' => true);
            if (0 === strpos($key, 'http-')) {
                $headers[substr($key, 5)] = $val;
            }
            // CONTENT_* are not prefixed with HTTP_
            elseif (isset($contentHeaders[$key])) {
                $headers[$key] = $val;
            }
        }

        $cookies = CookieJar::fromArray(
            $this->getCookieJar()->allRawValues($request->getUri()),
            parse_url($request->getUri(), PHP_URL_HOST)
        );

        $requestOptions = array(
            'cookies' => $cookies,
            'allow_redirects' => false,
            'auth' => $this->auth,
        );

        if (!in_array($request->getMethod(), array('GET', 'HEAD'))) {
            if (null !== $content = $request->getContent()) {
                $requestOptions['body'] = $content;
            } else {
                if ($files = $request->getFiles()) {
                    $requestOptions['multipart'] = [];

                    $this->addPostFields($request->getParameters(), $requestOptions['multipart']);
                    $this->addPostFiles($files, $requestOptions['multipart']);
                } else {
                    $requestOptions['form_params'] = $request->getParameters();
                }
            }
        }

        if (!empty($headers)) {
            $requestOptions['headers'] = $headers;
        }

        $method = $request->getMethod();
        $uri = $request->getUri();

        foreach ($this->headers as $name => $value) {
            $requestOptions['headers'][$name] = $value;
        }

        // Let BrowserKit handle redirects
            $promise = $this->getClient()->requestAsync($method,$uri,$requestOptions);
            $promise->then(
                function (ResponseInterface $response) {
                    return $this->createResponse($response);

                },
                function (RequestException $e) {
                    $response = $e->getResponse();
                    if (null === $response) {
                        throw $e;
                    }


                }



            );
        $promise->wait();

    }

Как я могу изменить Goutte\Client.php так, чтобы он выполнял асинхронные запросы? Разве это невозможно, как я могу запускать свои скребки, которые одновременно нацелены на разные конечные точки? благодаря

Теги:
guzzle
goutte

1 ответ

1

Goutte по сути является мостом между Guzzle и Symphony Browserkit и DomCrawler.

Самым большим недостатком использования Goutte является то, что все запросы выполняются синхронно

Чтобы выполнить все асинхронно, вам придется отказаться от использования Goutte и напрямую использовать Guzzle и DomCrawler.

Например:

$requests = [
    new GuzzleHttp\Psr7\Request('GET', $uri[0]),
    new GuzzleHttp\Psr7\Request('GET', $uri[1]),
    new GuzzleHttp\Psr7\Request('GET', $uri[2]),
    new GuzzleHttp\Psr7\Request('GET', $uri[3]),
    new GuzzleHttp\Psr7\Request('GET', $uri[4]),
    new GuzzleHttp\Psr7\Request('GET', $uri[5]),
    new GuzzleHttp\Psr7\Request('GET', $uri[6]),
];

$client = new GuzzleHttp\Client();

$pool = new GuzzleHttp\Pool($client, $requests, [
    'concurreny' => 5, //how many concurrent requests we want active at any given time
    'fulfilled' => function ($response, $index) {
        $crawler = new Symfony\Component\DomCrawler\Crawler(null, $uri[$index]);
        $crawler->addContent(
            $response->getBody()->__toString(),
            $response->getHeader['Content-Type'][0]
        );        
    },
    'rejected' => function ($response, $index) {
        // do something if the request failed.
    },
]);

$promise = $pool->promise();
$promise->wait();
  • 0
    Спасибо за ответ. Однако я пытаюсь запустить отдельные скребки, которые имеют больше, чем просто получить запросы. Каждый скребок имеет свой собственный класс для выполнения серии запросов, анализа DOM и т. Д. Вот почему я задаюсь вопросом, как я могу вызвать все эти скребки асинхронно. Если мои утилиты являются localhost / site1.php localhost / site2.php, то стоит ли вызывать site1.php, site2.php, скажем, cron.php с приведенным выше кодом? Что ты предлагаешь?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню