Невозможно заставить скрипт не печатать данные журнала ошибок в консоли

#python #python-3.x #selenium #web-scraping #seleniumwire

Вопрос:

Я пытаюсь создать сценарий с использованием python в сочетании с seleniumwire, реализующим в нем оспы. Скрипт иногда работает правильно, но в большинстве случаев выдает данные журнала ошибок, даже если код состояния равен 200. Я хочу избавиться от этих данных журнала. IP-адрес, жестко закодированный в скрипте, взят с бесплатного прокси-сайта, поэтому в данный момент он может оказаться бесполезным.

Это то, с чем я пытаюсь:

 from seleniumwire import webdriver

URL = 'https://www.zillow.com/Houston,-TX/houses/'

options = {
    'mitm_http2': False,
    'proxy': {'https': f'https://136.226.33.115:80'}
}

driver = webdriver.Chrome(seleniumwire_options=options)

driver.get(URL)
assert driver.requests[0].response.status_code==200
post_links = [i.get_attribute("href") for i in driver.find_elements_by_css_selector("article[role='presentation'] > .list-card-info > a.list-card-link")]
for individual_link in post_links:
    driver.get(individual_link)
    assert driver.requests[0].response.status_code==200
    post_title = driver.find_element_by_css_selector("h1").text
    print(post_title)
driver.quit()
 

Это тип сведений журнала ошибок, которые я могу видеть в консоли:

 127.0.0.1:55825: request
  -> HTTP protocol error in client request: Server disconnected
127.0.0.1:55967: request
  -> HTTP protocol error in client request: Server disconnected
127.0.0.1:64891: request
  -> HTTP protocol error in client request: Server disconnected
127.0.0.1:61466: request
  -> HTTP protocol error in client request: Server disconnected
127.0.0.1:51332: request
  -> HTTP protocol error in client request: Server disconnected
127.0.0.1:52783: request
  -> HTTP protocol error in client request: Server disconnected
 

Как я могу заставить скрипт не печатать эти данные журнала?

Комментарии:

1. Не уверен, правильно ли я понимаю, но если вы хотите избавиться только от строк журнала, почему бы не регистрировать эти случаи простым предложением if. В противном случае, если вы хотите поймать эти ошибки, вы можете добавить другое утверждение, чтобы post_title не содержал сообщение об ошибке: утверждение «Сервер отключен» не в post_title

2. Я не понимаю, какая часть вышеупомянутого поста вам непонятна @pafede2. Я выгнал assert и использовал if заявление, как вы и предлагали. Я также добавил строку, содержащую Server disconnected , но ошибка все еще там. Я просто не могу понять, откуда, черт возьми, взялся этот журнал ошибок. Я никогда не сталкивался с такой ошибкой, когда использовал selenium вместо seleniumwire .

3. Какова полная строка журнала, которую вы видите? Не только детали!

4. Проверьте это на @rfkortekaas. Скрипт отключается (что вы можете видеть в журнале) после одного успешного цикла, так как я использовал один прокси-сервер. Однако, когда прокси работают, эти вещи -> HTTP protocol error in client request: Server disconnected всегда всплывают.

5. Можете ли вы попробовать это, добавив chrome_options.add_experimental_option('excludeSwitches', ['enable-logging']) , chrome_options=chrome_options webdriver.Chrome() откуда chrome_options это Options() selenium.webdriver.chrome.options

Ответ №1:

ОРИГИНАЛЬНОЕ СООБЩЕНИЕ 06-22-2021 @12:00 UTC

Ошибка:

 HTTP protocol error in client request: Server disconnected
 

Выбрасывается mitmproxy. Я вытащил следующее из исходного кода mitmproxy.

 "clientconnect": "client_connected",
"clientdisconnect": "client_disconnected",
"serverconnect": "server_connect and server_connected",
"serverdisconnect": "server_disconnected",


class ServerDisconnectedHook(commands.StartHook):
    """
    A server connection has been closed (either by us or the server).
    """
    blocking = False
    data: ServerConnectionHookData

 

Я бы рекомендовал поместить ваш код в блок «Попробуйте, кроме», который позволит вам подавить ошибки, вызванные mitmproxy.

 from mitmproxy.exceptions import MitmproxyException
from mitmproxy.exceptions import HttpReadDisconnect

try:
  your driver code
except HttpReadDisconnect as e:
    pass
except MitmproxyException as e:
    """
    Base class for all exceptions thrown by mitmproxy.
    """
    pass
finally:
  driver.quit()
 

Если исключения, которые я предоставил, не устраняют вашу ошибку, я бы рекомендовал попробовать некоторые другие исключения в mitmproxy.

введите описание изображения здесь

ОБНОВЛЕНИЕ 06-22-2021 @15:28 UTC

В своем исследовании я отметил, что seleniumwire имеет код интеграции с mitmproxy. Частью этой интеграции является захват сообщения об ошибке, выданного *mitmproxy.»

 class SendToLogger:

    def log(self, entry):
        """Send a mitmproxy log message through our own logger."""
        getattr(logger, entry.level.replace('warn', 'warning'), logger.info)(entry.msg)
 

В моем тестировании трудно подавить рассматриваемую ошибку с помощью mitmproxy.exceptions. При тестировании следующих исключений единственным, который сработал, был HttpReadDisconnect. И этот запуск не был согласованным.

  • Исключение HttpException
  • HttpReadDisconnect
  • Исключение HttpProtocolException
  • Исключение Http2ProtocolException
  • Исключение MitmproxyException
  • Исключение сервера
  • Исключение TlsException

Я отметил, что если я добавлю стандартное исключение:

 except Exception as error:
    print('standard')
    print(''.join(traceback.format_tb(error.__traceback__)))
 

Что эта строка в вашем коде постоянно выдает ошибки:

  File "/Users/user_name/Python_Projects/scratch_pad/seleniumwire_test.py", line 18, in <module>
    assert driver.requests[0].response.status_code == 200
 

Когда я рассмотрел эту ошибку более подробно, я обнаружил, что она связана с кодом состояния.

 <class 'AttributeError'>
'NoneType' object has no attribute 'status_code'
 

ОБНОВЛЕНИЕ 06-23-2021 @15:04 UTC

Во время моего исследования я обнаружил, что у selenium был параметр service_log_path, который можно было добавить в webdriver.Хром().

 class WebDriver(ChromiumDriver):
   
    def __init__(self, executable_path="chromedriver", port=DEFAULT_PORT,
                 options: Options = None, service_args=None,
                 desired_capabilities=None, service_log_path=DEFAULT_SERVICE_LOG_PATH,
                 chrome_options=None, service: Service = None, keep_alive=DEFAULT_KEEP_ALIVE):
 

Согласно документации, этот параметр можно использовать следующим образом: service_log_path=/dev/null

К сожалению, в комментариях к классу WebDriver(ChromiumDriver) указано, что этот параметр устарел. Ему также не удалось подавить сообщения об ошибках sys.stdout.

service_log_path — Устаревший: Где регистрировать информацию от драйвера.

ТЕКУЩЕЕ СОСТОЯНИЕ

Я переработал ваш код и удалил строки status_code, которые выдавали ошибки. Я добавил несколько операторов implicitly_wait() и некоторые операторы WebDriverWait для обработки того, что вы пытались сделать с помощью оператора status_code. Я также добавил некоторую обработку ошибок для обнаружения определенных типов сообщений об ошибках. И я добавил некоторые опции chrome_options для подавления определенных вещей, таких как загрузка изображений веб-сайта, которые не нужны для очистки целевого веб-сайта.

Наконец, я добавил пользовательскую функцию ведения журнала, чтобы подавлять сообщения об ошибках, отправляемые в sys.stdout. Я много раз тестировал код и до сих пор не получил сообщение об ошибке в sys.stdout. Возможно, потребуется дополнительное тестирование, если вы снова получите сообщения.

Вот ссылка на код в действии.

 import sys
import logging
import traceback
from seleniumwire import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from mitmproxy.exceptions import HttpReadDisconnect, TcpDisconnect, TlsException


class DisableLogger():
    def __enter__(self):
       logging.disable(logging.WARNING)
    def __exit__(self, exit_type, exit_value, exit_traceback):
       logging.disable(logging.NOTSET)


options = {
    "backend": "mitmproxy",
    'mitm_http2': False,
    'disable_capture': True,
    'verify_ssl': True,
    'connection_keep_alive': False,
    'max_threads': 3,
    'connection_timeout': None,
    'proxy': {
        'https': 'https://209.40.237.43:8080',
    }
}

chrome_options = Options()
chrome_options.add_argument(
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36")
chrome_options.add_argument("--start-maximized")
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-logging')
chrome_options.add_argument("--disable-application-cache")
chrome_options.add_argument("--ignore-certificate-errors")
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)

webdriver.DesiredCapabilities.CHROME['acceptSslCerts'] = True

prefs = {
   "profile.managed_default_content_settings.images": 2,
   "profile.default_content_settings.images": 2
 }

capabilities = webdriver.DesiredCapabilities.CHROME
chrome_options.add_experimental_option("prefs", prefs)
capabilities.update(chrome_options.to_capabilities())

driver = webdriver.Chrome(executable_path='/usr/local/bin/chromedriver',
                          options=chrome_options, seleniumwire_options=options)

with DisableLogger():
    driver.implicitly_wait(60)
    try:
        driver.get('https://www.zillow.com/Houston,-TX/houses/')
        wait = WebDriverWait(driver, 240)
        page_title = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="search-page-react-content"]')))
        if page_title:
            post_links = [i.get_attribute("href") for i in driver.find_elements_by_css_selector("article[role='presentation'] > .list-card-info > a.list-card-link")]
            for individual_link in post_links:
                driver.implicitly_wait(60)
                driver.get(individual_link)
                post_title = driver.find_element_by_css_selector("h1").text
                print(post_title)

    except HttpReadDisconnect as error:
        print('A HttpReadDisconnect Exception has occurred')
        exc_type, exc_value, exc_tb = sys.exc_info()
        print(exc_type)
        print(exc_value)
        print(''.join(traceback.format_tb(error.__traceback__)))
        driver.quit()

    except TimeoutException as error:
        print('A TimeOut Exception has occurred')
        exc_type, exc_value, exc_tb = sys.exc_info()
        print(exc_type)
        print(exc_value)
        print(''.join(traceback.format_tb(error.__traceback__)))
        driver.quit()

    except TcpDisconnect as error:
        print('A TCP Disconnect Exception has occurred')
        exc_type, exc_value, exc_tb = sys.exc_info()
        print(exc_type)
        print(exc_value)
        print(''.join(traceback.format_tb(error.__traceback__)))
        driver.quit()

    except TlsException as error:
        print('A TLS Exception has occurred')
        exc_type, exc_value, exc_tb = sys.exc_info()
        print(exc_type)
        print(exc_value)
        print(''.join(traceback.format_tb(error.__traceback__)))
        driver.quit()

    except Exception as error:
        print('An exception has occurred')
        print(''.join(traceback.format_tb(error.__traceback__)))
        pass

    finally:
        driver.quit()
 

наблюдения

Я отметил, что вы используете бесплатные прокси вместо платного прокси-сервиса. Прокси-сервер в вашем коде hxxps://136.226.33.115:80, который я обнаружил, был стандартным HTTP-прокси, и у него также были проблемы с задержкой, что приводило к тайм-аутам при подключении к вашему целевому веб-сайту.

Еще одно наблюдение заключается в том, что на вашем целевом веб-сайте есть капча, которая срабатывает, когда вы отправляете слишком много запросов на подключение.

Я также отметил, что у вашего прокси-сервера также будут проблемы с подключением, что приведет к отправке сообщений об ошибках в sys.stdout. Это то, с чем вы, скорее всего, столкнулись.

ПРИМЕЧАНИЕ СБОКУ

Сеанс selenium в вашем коде иногда сталкивается с капчей «Я-человек» из Zillow.

введите описание изображения здесь

 ----------------------------------------
My system information
----------------------------------------

Platform: Mac 
Python Version: 3.9
Seleniumwire: 4.3.1
Selenium: 3.141.0
mitmproxy: 6.0.2
browserVersion: 91.0.4472.114
chromedriverVersion: 90.0.4430.24
IDE: PyCharm 2021.1.2

----------------------------------------
 

Комментарии:

1. Честно говоря, я решил seleniumwire использовать эту строку только для того driver.requests[0].response.status_code == 200 , чтобы получить статус. Чистый Селен не предлагает ничего подобного для проверки кода состояния.

2. Вы можете проверить код состояния с помощью selenium. При необходимости я могу предоставить вам этот код.

3. Да, конечно. Тогда Селениумвайр будет мне бесполезен.

4. @SMTH Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы о коде, который я опубликовал в разделе » Текущий статус » моего ответа.

5. Это то, что я испытываю, когда выполняю ваш сценарий. Я дважды прокрутил сценарий, чтобы убедиться, что то, что я видел, реально. Если быть точным, ошибка все еще существует, и я мог видеть, что одновременно открываются многочисленные окна. Спасибо.

Ответ №2:

если вы работаете в дистрибутиве Linux, вы можете перенаправлять вывод ошибок. для этого вам следует добавить 2>/dev/null в конец вашей команды. например, вы можете запустить свой скрипт следующим образом:

 python SCRIPT 2>/dev/null
 

Комментарии:

1. Я на Windows 7, 32-разрядная версия.

Ответ №3:

Вы можете определить желаемый уровень ошибок в журнале с помощью следующей строки кода:

 options .add_argument('--log-level=3')
 

Добавьте это к своему options .

атрибут уровня журнала задает минимальный уровень журнала.
Допустимые значения от 0 до 3:

ИНФОРМАЦИЯ = 0,
ПРЕДУПРЕЖДЕНИЕ = 1,
LOG_ERROR = 2,
LOG_FATAL = 3.

значение по умолчанию равно 0.

Комментарии:

1. Я попробовал ваше предложение, но оно не сработало. На самом деле это была моя первая попытка подавить эти ошибки, прежде чем задавать вопрос. Кстати, я перепробовал все предоставленные вами варианты.

2. может быть, это просто другой синтаксис? Я вижу, что вы использовали другой синтаксис для того, что вы определили options в своем коде. Так, может быть, вам следует просто сопоставить синтаксисы? В настоящее время я не вижу дополнительных способов сделать это.

3. Синтаксис здесь не является проблемой. Я использовал их правильно. К вашему сведению, это правильный синтаксис c_options.add_argument('--log-level=3') и, наконец, webdriver.Chrome(options=c_options,seleniumwire_options=options)

4. Я знаю, что я написал с правильным синтаксисом, но вы использовали какой-то другой синтаксис в своем коде options = { 'mitm_http2': False, 'proxy': {'https': f'https://136.226.33.115:80'} }

5. Вы забыли просмотреть мой последний комментарий. Вот как вы можете использовать там два типа опций webdriver.Chrome(options=c_options,seleniumwire_options=options)