#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)