2014-12-18 3 views
2

Мне нужно сделать множество (тысяч) HTTP-запросов GET на большое количество веб-сайтов. Это довольно медленно, по причинам, по которым некоторые веб-сайты могут не отвечать (или занимать много времени, чтобы сделать это), в то время как другие тайм-аут. Поскольку мне нужно столько ответов, сколько я могу получить, установка небольшого тайм-аута (3-5 секунд) не в мою пользу.Многопроцессорные HTTP-запросы на получение в Python

Мне еще предстоит выполнить многопроцессорную или многопоточную обработку в Python, и я хорошо читал документацию. Вот что у меня есть до сих пор:

import requests 
from bs4 import BeautifulSoup 
from multiprocessing import Process, Pool 

errors = 0 

def get_site_content(site): 
    try : 
     # start = time.time() 
     response = requests.get(site, allow_redirects = True, timeout=5) 
     response.raise_for_status() 
     content = response.text 
    except Exception as e: 
     global errors 
     errors += 1 
     return '' 
    soup = BeautifulSoup(content) 
    for script in soup(["script", "style"]): 
     script.extract() 
    text = soup.get_text() 

    return text 

sites = ["http://www.example.net", ...] 

pool = Pool(processes=5) 
results = pool.map(get_site_content, sites) 
print results 

Теперь, я хочу, чтобы результаты были возвращены, чтобы как-то соединиться. Это позволяет два изменения:

  1. Каждого процесс имеет локальный список/очередь, которая содержит контент он накопленный соединяется с другими очередями, чтобы сформировать один результат, содержащий все содержимое для всех сайтов.

  2. Каждый процесс записывает в одну глобальную очередь по мере ее прохождения. Это повлечет за собой механизм блокировки для проверок параллелизма.

Может быть, лучший выбор здесь будет многопроцессорным или многопоточным? Как я мог бы выполнить вышеизложенное с помощью любого из подходов в Python?


Edit:

Я сделал попытку что-то вроде следующего:

# global 
queue = [] 
with Pool(processes = 5) as pool: 
    queue.append(pool.map(get_site_contents, sites)) 

print queue 

Однако, это дает мне следующую ошибку:

with Pool(processes = 4) as pool: 
AttributeError: __exit__ 

Что я не» t вполне понимаю. У меня есть небольшая проблема с пониманием , что именно pool.map делает, минуя применение функции на каждом объекте в итерируемом втором параметре. Он что-то возвращает? Если нет, добавьте в глобальную очередь из функции?

+0

Вы прочитали [введение в «многопроцессорную обработку»] (https://docs.python.org/3/library/multiprocessing.html#introduction)? В нем описывается, как это можно сделать. –

+0

@ LutzHorn Да, в частности [https://docs.python.org/3/library/multiprocessing.html#using-a-pool-of-workers] (этот раздел). Я уточнил свой вопрос с более конкретной информацией, относящейся к нему, поскольку, к сожалению, я счел, что документация несколько запутанна. – user991710

+0

Исключение происходит из-за того, что вы используете версию python, которая не поддерживает менеджеров контекста для пула. Поэтому просто не используйте инструкцию with с пулами или переключитесь на самую новую версию. Помимо этого, Pool.map возвращает список со всеми результатами, поэтому с вашим кодом вы создаете очередь списка, содержащую фактический список результатов. В противном случае ваш код кажется прекрасным. – phobic

ответ

2

У меня было аналогичное задание в университете (для реализации многопроцессорного веб-искателя) и использовалось многопроцессорно-безопасный класс Queue из библиотеки многопроцессорности python, которая будет выполнять всю магию с помощью блокировок и проверок параллелизма. Пример из документации гласит:

import multiprocessing as mp 

def foo(q): 
    q.put('hello') 

if __name__ == '__main__': 
    mp.set_start_method('spawn') 
    q = mp.Queue() 
    p = mp.Process(target=foo, args=(q,)) 
    p.start() 
    print(q.get()) 
    p.join() 

Однако, я должен был написать отдельный класс процесса для этого, чтобы работать, как я хотел работать. И я не использовал пул процессов. Вместо этого я попытался проверить использование памяти и вызвать процесс до тех пор, пока не будет достигнут порог заданной памяти.

+0

У меня есть AttributeError: объект 'module' не имеет атрибута 'set_start_method' –

3

pool.map запускает 'n' количество процессов, которые принимают функцию и запускают ее с элементом из итерабельного. Когда такой процесс заканчивается и возвращается, возвращаемое значение сохраняется в списке результатов в той же позиции, что и входной элемент во входной переменной.

например: если функция предназначена для вычисления квадрата числа, а затем используется pool.map, чтобы запустить эту функцию в списке чисел. Защиту square_this (х): квадрат = х ** 2 возвращение квадратный

input_iterable = [2, 3, 4] 
pool = Pool(processes=2) # Initalize a pool of 2 processes 
result = pool.map(square_this, input_iterable) # Use the pool to run the function on the items in the iterable 
pool.close() # this means that no more tasks will be added to the pool 
pool.join() # this blocks the program till function is run on all the items 
# print the result 
print result 

...>>[4, 9, 16] 

Pool.map метод не может быть идеальным в вашем случае, так как он будет блокировать, пока все процессы отделки. Если веб-сайт не отвечает или занимает слишком много времени, чтобы ответить, ваша программа будет застревать в ожидании. Вместо этого попробуйте подклассифицировать multiprocessing.Process в своем классе, который опросит эти веб-сайты и использует очереди для доступа к результатам. Когда у вас есть удовлетворительное количество ответов, вы можете остановить все процессы, не дожидаясь завершения оставшихся запросов.

Смежные вопросы