Получите ответ прокси в промежуточном ПО - PullRequest
0 голосов
/ 08 января 2019

В промежуточном программном обеспечении у меня возникла следующая проблема со скрапом:

Я делаю запрос на сайт с https, а также использую прокси. При определении промежуточного программного обеспечения и использовании в нем process_response, response.headers имеет только заголовки с веб-сайта. Есть ли способ получить заголовки из запроса CONNECT, который устанавливает туннель прокси? Используемый нами прокси-сервер добавляет некоторую информацию в качестве заголовков в этот ответ, мы хотим использовать ее в промежуточном программном обеспечении. Я обнаружил, что в TunnelingTCP4ClientEndpoint.processProxyResponse параметр rcvd_bytes содержит всю необходимую мне информацию. Я не нашел способа получить rcvd_bytes в своем промежуточном программном обеспечении.

Также я нашел похожую (аналогичную) проблему год назад, которая не решена: Не получает заголовки Scrapy ProxyMesh

Вот пример с прокси-сайта:

Для HTTPS IP находится в заголовке ответа CONNECT. Пример x-hola-ip для IP-адреса прокси-сервера 5.6.7.8:

.
Request
CONNECT example.com:80 HTTP/1.1
Host: example.com:80
Accept: */*

Response:
HTTP/1.1 200 OK
Content-Type: text/html
x-hola-ip: 5.6.7.8

Я хочу получить x-hola-ip в этом примере.

При использовании curl типа curl --proxy mysuperproxy https://stackoverflow.com я получаю также правильные данные в ответе CONNECT.

Если это невозможно, моё возможное решение состоит в том, чтобы как-то до сих пор исправлять класс, или, возможно, вы знаете лучшее решение для этого в python.

Заранее спасибо за помощь.

Примечание: я также разместил этот вопрос на github проблем scrapy, я обновлю оба сайта, если найду какое-либо решение:)

Рабочий раствор с помощью Матфея:

from scrapy.core.downloader.handlers.http11 import (
    HTTP11DownloadHandler, ScrapyAgent, TunnelingTCP4ClientEndpoint, TunnelError, TunnelingAgent
)
from scrapy import twisted_version

class MyHTTPDownloader(HTTP11DownloadHandler):
    i = ''
    def download_request(self, request, spider):
        # we're just overriding here to monkey patch the attribute
        agent = ScrapyAgent(contextFactory=self._contextFactory, pool=self._pool,
            maxsize=getattr(spider, 'download_maxsize', self._default_maxsize),
            warnsize=getattr(spider, 'download_warnsize', self._default_warnsize),
            fail_on_dataloss=self._fail_on_dataloss)


        agent._TunnelingAgent = MyTunnelingAgent

        return agent.download_request(request)

class MyTunnelingAgent(TunnelingAgent):
    if twisted_version >= (15, 0, 0):
        def _getEndpoint(self, uri):
            return MyTunnelingTCP4ClientEndpoint(
                self._reactor, uri.host, uri.port, self._proxyConf,
                self._contextFactory, self._endpointFactory._connectTimeout,
                self._endpointFactory._bindAddress)
    else:
        def _getEndpoint(self, scheme, host, port):
            return MyTunnelingTCP4ClientEndpoint(
                self._reactor, host, port, self._proxyConf,
                self._contextFactory, self._connectTimeout,
                self._bindAddress)

class MyTunnelingTCP4ClientEndpoint(TunnelingTCP4ClientEndpoint):
    def processProxyResponse(self, rcvd_bytes):
        # log('hier rcvd_bytes')
        MyHTTPDownloader.i = rcvd_bytes
        return super(MyTunnelingTCP4ClientEndpoint, self).processProxyResponse(rcvd_bytes)

А в ваших настройках:

DOWNLOAD_HANDLERS = {
    'http': 'crawler.MyHTTPDownloader.MyHTTPDownloader',
    'https': 'crawler.MyHTTPDownloader.MyHTTPDownloader',
}

1 Ответ

0 голосов
/ 12 января 2019

Я видел в # 3329 , что кто-то из Scrapinghub сказал, что вряд ли они добавят эту функцию, и порекомендовал создать собственный подкласс, чтобы получить поведение, которое вы хотели. Итак, имея в виду:

Я полагаю, что после создания подкласса вы можете указать scrapy использовать его, установив клавиши http и https в DOWNLOAD_HANDLERS, чтобы они указывали на ваш подкласс.

Имейте в виду, что у меня нет локального http-прокси, который отправляет дополнительные заголовки для тестирования, так что это всего лишь "набросок салфетки" того, что, по моему мнению, должно произойти:

from scrapy.core.downloader.handlers.http11 import (
    HTTP11DownloadHandler, ScrapyAgent, TunnelingAgent,
)

class MyHTTPDownloader(HTTP11DownloadHandler):
    def download_request(self, request, spider):
        # we're just overriding here to monkey patch the attribute
        ScrapyAgent._TunnelingAgent = MyTunnelingAgent
        return super(MyHTTPDownloader, self).download_request(request, spider)

class MyTunnelingAgent(TunnelingAgent):
    # ... and here is where it would get weird

Этот последний бит машет руками, потому что я считаю, что у меня есть четкое понимание методов, которые необходимо переопределить для захвата байтов, которые вы хотите, но мне не хватает структуры Twisted в моей голове, чтобы знать, где положите их , чтобы выставить их на Response, который возвращается к пауку.

...