Я собираю сообщения с Wykop.pl («Польский Reddit»), просматривая несколько страниц, которые были возвращены, когда я искал на сайте интересующее меня ключевое слово. Я написал цикл для итерации моего целевого контента для каждой страницы; однако цикл завершается на определенных страницах (постоянно) с ошибкой «Ошибка HTTP 413: объект запроса слишком велик».
Я пытался очистить проблемные страницы по отдельности, но одно и то же сообщение об ошибке повторялось. Чтобы обойти это, мне пришлось установить свои диапазоны вручную для сбора данных, но за счет пропуска большого количества данных, и я хотел бы знать, есть ли решение Pythonic для решения этой ошибки. Я также пробовал более длительные паузы, так как, возможно, я рисковал отправить слишком много запросов, но, похоже, это не так.
from time import sleep
from random import randint
import requests
from requests import get
from bs4 import BeautifulSoup
from mtranslate import translate
from IPython.core.display import clear_output
from mtranslate import translate
posts = []
votes = []
dates = []
images = []
users = []
start_time = time()
requests = 0
pages = [str(i) for i in range(1,10)]
for page in pages:
url = "https://www.wykop.pl/szukaj/wpisy/smog/strona/" + page + "/"
response = get(url)
# Pause the loop
sleep(randint(8,15))
# Monitor the requests
requests += 1
elapsed_time = time() - start_time
print('Request:{}; Frequency: {} requests/s'.format(requests, requests/elapsed_time))
clear_output(wait = True)
# Throw a warning for non-200 status codes
if response.status_code != 200:
warn('Request: {}; Status code: {}'.format(requests, response.status_code))
# Break the loop if the number of requests is greater than expected
if requests > 10:
warn('Number of requests was greater than expected.')
break
soup = BeautifulSoup(response.text, 'html.parser')
results = soup.find_all('li', class_ = "entry iC")
for result in results:
# Error handling
try:
post = result.find('div', class_ = "text").text
post = translate(post,'en','auto')
posts.append(post)
date = result.time['title']
dates.append(date)
vote = result.p.b.span.text
vote = int(vote)
votes.append(vote)
user = result.div.b.text
users.append(user)
image = result.find('img',class_='block lazy')
images.append(image)
except AttributeError as e:
print(e)
Если бы я мог запустить сценарий сразу, я бы установил диапазон от 1 до 163 (поскольку у меня есть 163 страницы результатов сообщений, в которых упоминается интересующее меня ключевое слово). Таким образом, мне пришлось установить меньшие диапазоны для постепенного сбора данных, но опять же за счет пропуска страниц данных.
Альтернативой, которую я использую на случай непредвиденных обстоятельств, является очистка от обозначенных проблемных страниц в виде загруженных html-документов на моем рабочем столе.
Также! утечка памяти — очень распространенная проблема с библиотекой запросов. Вместо этого вам следует изучить асинхронные запросы, чтобы справиться с этим. Еще лучше, начните использовать scrapy!
Возможно, вы натолкнулись на какое-то ограничение IP-адреса. При запуске скрипт у меня работает нормально без ограничения скорости (на данный момент). Тем не менее, я бы порекомендовал вам использовать requests.Session()
(вам нужно изменить переменную requests
, иначе она переопределит импорт). Это может помочь уменьшить возможные проблемы с утечкой памяти.
Так, например:
from bs4 import BeautifulSoup
from time import sleep
from time import time
from random import randint
import requests
posts = []
votes = []
dates = []
images = []
users = []
start_time = time()
request_count = 0
req_sess = requests.Session()
for page_num in range(1, 100):
response = req_sess.get(f"https://www.wykop.pl/szukaj/wpisy/smog/strona/{page_num}/")
# Pause the loop
#sleep(randint(1,3))
# Monitor the requests
request_count += 1
elapsed_time = time() - start_time
print('Page {}; Request:{}; Frequency: {} requests/s'.format(page_num, request_count, request_count/elapsed_time))
#clear_output(wait = True)
# Throw a warning for non-200 status codes
if response.status_code != 200:
print('Request: {}; Status code: {}'.format(requests, response.status_code))
print(response.headers)
# Break the loop if the number of requests is greater than expected
#if requests > 10:
# print('Number of requests was greater than expected.')
# break
soup = BeautifulSoup(response.text, 'html.parser')
results = soup.find_all('li', class_ = "entry iC")
for result in results:
# Error handling
try:
post = result.find('div', class_ = "text").text
#post = translate(post,'en','auto')
posts.append(post)
date = result.time['title']
dates.append(date)
vote = result.p.b.span.text
vote = int(vote)
votes.append(vote)
user = result.div.b.text
users.append(user)
image = result.find('img',class_='block lazy')
images.append(image)
except AttributeError as e:
print(e)
Дал следующий вывод:
Page 1; Request:1; Frequency: 1.246137372973911 requests/s
Page 2; Request:2; Frequency: 1.3021880233774552 requests/s
Page 3; Request:3; Frequency: 1.2663757427416629 requests/s
Page 4; Request:4; Frequency: 1.1807827876080845 requests/s
.
.
.
Page 96; Request:96; Frequency: 0.8888853607003809 requests/s
Page 97; Request:97; Frequency: 0.8891876183362001 requests/s
Page 98; Request:98; Frequency: 0.888801819672809 requests/s
Page 99; Request:99; Frequency: 0.8900784741536467 requests/s
Это также отлично работало, когда я начинал с гораздо большего количества страниц. Теоретически, когда вы получаете код состояния ошибки 413, теперь он должен отображать заголовки ответа. Согласно RFC 7231, сервер должен возвращать поле заголовка Retry-After
, которое вы можете использовать, чтобы определить, как долго ждать до вашего следующего запроса.
Привет Мартин; работал как шарм! Вот что интересно: я думаю, что это был модуль перевода ('post = translate(post,'en','auto')'), который сбрасывал весь скрипт. Я попробовал ваш скрипт с отключенным и активным переводом, я предполагал, что последний замедлит процесс очистки, но нет! Он вернул ту же ошибку 413 на странице 13. Похоже, в этом и была суть проблемы.
Итак, вот загвоздка:
Ошибка 413 связана не с Wykop, веб-сайтом, подлежащим очистке, а с пакетом mtranslate, который использует API Google Translate. В моем исходном коде произошло то, что когда Wykop был очищен, он затем перевел сообщения с польского на английский. Однако Google Translation API имеет ограничение примерно в 100 000 символов за 100 секунд на пользователя. Поэтому, когда код достиг 13-й страницы, mtranslate достиг лимита запросов для переводчик Google. Следовательно, решение Мартина отлично работает при очистке данных с отключенной функцией перевода.
Я пришел к такому выводу, когда использовал модуль для перевода сообщений, хранящихся в фрейме данных, так как я столкнулся с той же ошибкой примерно на отметке 8% моего цикла перевода.
Отсутствует импорт BS4, друг! Возможно, вы просто ошиблись при оклейке.