Одновременно загружать несколько страниц?
Я хотел бы написать script в Python, который может захватить URL-адрес из базы данных и одновременно загружать веб-страницы, чтобы ускорить работу, а не ждать, пока каждая страница будет загружаться одна за другой.
Согласно этой теме, Python не позволяет этого из-за чего-то называемого Global Interpreter Lock, который предотвращает многократное повторение одного и того же script.
Прежде чем инвестировать время в изучение структуры Twisted, я хотел бы убедиться, что нет более простого способа сделать то, что мне нужно сделать выше.
Спасибо за любой совет.
Ответы
Ответ 1
Не беспокойтесь о GIL. В вашем случае это не имеет значения.
Самый простой способ сделать то, что вы хотите, - создать пул потоков, используя модуль потоковой передачи и одну из реализаций пула потоков из ASPN. Каждый поток из этого пула может использовать httplib для загрузки ваших веб-страниц.
Другим вариантом является использование PyCURL module - он поддерживает параллельные downlaods изначально, поэтому вам не нужно его реализовывать самостоятельно.
Ответ 2
GIL не позволяет эффективно выполнять балансировку нагрузки процессора с помощью потоков. Поскольку это не балансировка нагрузки процессора, но предотвращение одного ожидания ввода-вывода от остановки всей загрузки, GIL здесь не имеет отношения. *)
Итак, все, что вам нужно сделать, это создать несколько процессов, загружаемых одновременно. Вы можете сделать это с помощью модуля потоковой передачи или модуля многопроцессорности.
*) Хорошо... если у вас нет Gigabit-соединений, и ваша проблема в том, что ваш процессор перегружен до вашей сети. Но это явно не так.
Ответ 3
Недавно я решил эту же проблему. Следует учитывать, что некоторые люди не любезно относятся к тому, что их серверы увязли и блокируют IP-адрес, который делает это. Стандартная любезность, которую я слышал, составляет около 3 секунд между запросами страниц, но это гибко.
Если вы загружаете с нескольких веб-сайтов, вы можете группировать свои URL-адреса по домену и создавать по одному потоку. Затем в вашем потоке вы можете сделать что-то вроде этого:
for url in urls:
timer = time.time()
# ... get your content ...
# perhaps put content in a queue to be written back to
# your database if it doesn't allow concurrent writes.
while time.time() - timer < 3.0:
time.sleep(0.5)
Иногда просто получение вашего ответа займет полные 3 секунды, и вам не нужно беспокоиться об этом.
Конечно, это не поможет вам, если вы загружаете только с одного сайта, но это может помешать вам заблокироваться.
Моя машина обрабатывает около 200 потоков, прежде чем накладные расходы на управление ими замедляют процесс. Я закончил что-то вроде 40-50 страниц в секунду.
Ответ 4
urllib и threading (или multiprocessing) в пакетах есть все, что вам нужно для "паука", в котором вы нуждаетесь.
Что вам нужно сделать, так это получить URL-адреса от БД, и для каждого URL-адреса запустите поток или процесс, который
захватывает URL.
так же, как пример (пропущенные поисковые запросы базы данных):
#!/usr/bin/env python
import Queue
import threading
import urllib2
import time
hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com",
"http://ibm.com", "http://apple.com"]
queue = Queue.Queue()
class ThreadUrl(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
while True:
#grabs host from queue
host = self.queue.get()
#grabs urls of hosts and prints first 1024 bytes of page
url = urllib2.urlopen(host)
print url.read(1024)
#signals to queue job is done
self.queue.task_done()
start = time.time()
def main():
#spawn a pool of threads, and pass them queue instance
for i in range(5):
t = ThreadUrl(queue)
t.setDaemon(True)
t.start()
#populate queue with data
for host in hosts:
queue.put(host)
#wait on the queue until everything has been processed
queue.join()
main()
print "Elapsed Time: %s" % (time.time() - start)
Ответ 5
В настоящее время есть отличные библиотеки Python, которые делают это за вас - urllib3 и requests
Ответ 6
Вы можете посмотреть пакет многопроцессорности, который предотвращает проблему блокировки GIL.
Ответ 7
Загрузка - это IO, которая может выполняться асинхронно с использованием неблокирующих сокетов или скрученных. Оба этих решения будут намного эффективнее, чем потоки или многопроцессорность.
Ответ 8
Я получаю помощь от какого-то эксперта сегодня, поскольку он публикует код python 2.X для ссылки.
http://paste.ubuntu.com/24529360/
from multiprocessing.dummy import Pool
import urllib
def download(_id):
success = False
fail_count = 0
url = 'fuck_url/%s'%_id
while not success:
if fail_count>10:
print url,'download faild'
return
try:
urllib.urlretrieve(url,'%s.html'%_id)
success = True
except Exception:
fail_count+=1
pass
if __name__ == '__main__':
pool = Pool(processes=100) # 100 thread
pool.map(download,range(30000))
pool.close()
pool.join()
Я предпочитаю использовать requests
, поскольку он имеет header
и proxy
может быть легко добавлен.
import requests
hdrs = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
r = requests.get(url, headers=hdrs, verify=False)#, proxies=proxyDict)
with open(fullFileName, 'wb') as code:
code.write(r.content)