Comments 18
Если принимаются сторонние модули, то я бы ещё посоветовал посмотреть на pycurl.
В нём, например, поддерживается keep-alive, который при скачивании нескольких картинок с одного сервера может положительно способствовать в плане скорости. Например, urllib.urlopen это не поддерживает когда я последний раз это проверял.
А если будет скачиваться текстовая информация, то pycurl так же может прозрачно это сжимать (если сервер поддерживает, конечно).
В нём, например, поддерживается keep-alive, который при скачивании нескольких картинок с одного сервера может положительно способствовать в плане скорости. Например, urllib.urlopen это не поддерживает когда я последний раз это проверял.
А если будет скачиваться текстовая информация, то pycurl так же может прозрачно это сжимать (если сервер поддерживает, конечно).
Весь пост — это один из ответов на SO, который я недавно встречал.
И что? Значит он абсолютно бесполезен? Я вот думаю над одним проектом, где надо парсить и сохранять странички. Мне этот пост попался вовремя.
Спасибо автору!
Спасибо автору!
Я ничего против автора не имею, но хабр тем и ценен, что статьи в нем отличаются от вопросов на stackoverflow. Формат хабра скорее: «Как я написал парсер», или «Используем requests + lxml для парсинга».
Не хватает sock.connect(...) ....write('GET /img.jpg HTTP/1.0\r\nHost: ...') ...read()
Я около полугода назад начал использовать python для аналогичных целей — массового парсинга страниц, поэтому мне тоже было интересно, какой способ работает быстрее. Для этого я набросал небольшой тест: pastebin.com/mH2ASEGX. Скрипт в 100 итераций получает главную страницу vk.com и ищет на ней наличие паттерна — типичные действия при парсинге. Резульаты следующие:
Из выводов: видно, что urllib-функции и httplib работают приблизительно в два раза медленнее, чем популярная библиотека Requests. Это вызвано тем, что urllib* не поддерживают keep-alive и на каждый запрос открывают-закрывают новый сокет (в третьей версии питона это исправили). Нужно скзаать, что с httplib кипэлайвы использовать, в принципе, можно, но контролировать их нужно вручную, через хедеры, тогда скорость работы будет приблизительно в 2 раза выше. Pycurl по скорости тоже ничем не отличается от других высокоуровневых библиотек, не знаю, правда, поддерживает ли он keep-alive.
Ну а сокеты, как самый низкоуровневый доступ к сети, рвут все библиотеки с огромным отрывом.
Поэтому если стоит вопрос максимальной производительности и нет сложных http-запросов, то лучше все оформить в виде какой-нибудь своей обертки над сокетами.
('testUrllib()', 19.59859853472629)
('testUrllib2()', 22.586007300934412)
('testHttplib()', 16.670537860489773)
('testSocket()', 1.5129479809538537)
('testRequests()', 9.380710576092)
('testPycurl()', 17.76420596649031)
Из выводов: видно, что urllib-функции и httplib работают приблизительно в два раза медленнее, чем популярная библиотека Requests. Это вызвано тем, что urllib* не поддерживают keep-alive и на каждый запрос открывают-закрывают новый сокет (в третьей версии питона это исправили). Нужно скзаать, что с httplib кипэлайвы использовать, в принципе, можно, но контролировать их нужно вручную, через хедеры, тогда скорость работы будет приблизительно в 2 раза выше. Pycurl по скорости тоже ничем не отличается от других высокоуровневых библиотек, не знаю, правда, поддерживает ли он keep-alive.
Ну а сокеты, как самый низкоуровневый доступ к сети, рвут все библиотеки с огромным отрывом.
Поэтому если стоит вопрос максимальной производительности и нет сложных http-запросов, то лучше все оформить в виде какой-нибудь своей обертки над сокетами.
На самом деле curl достаточно эффективная библиотека, просто вы не используйте multicurl, который на больших объёмах отлично себя показывает.
Однопоточные парсеры прошлый век, количество данных с каждым годом только растёт.
Однопоточные парсеры прошлый век, количество данных с каждым годом только растёт.
Ну если уже говорить о действительно больших масштабах, то pyCurl в многопоточных приложениях себя плохо ведет, так как использует блокирующие функции, тот же getaddrinfo для резолвинга домена в IP. Поэтому лучший вариант — это gevent и подобные асинхронные решения.
Только что запустил этот тест у себя локально на libcurl 7.34.0 и получил, что testPycurl примерно на 40% быстрее, чем testUrllib.
Видимо, действительно, зависит от сборки как выше уже заметили.
Кстати, если добавить сжатие, то testPycurl будет ещё вдобавок где-то в два раза быстрее (у меня в результате получилось примерно в три раза быстрее testUrllib'a):
Видимо, действительно, зависит от сборки как выше уже заметили.
Кстати, если добавить сжатие, то testPycurl будет ещё вдобавок где-то в два раза быстрее (у меня в результате получилось примерно в три раза быстрее testUrllib'a):
curlHandler.setopt(pycurl.ENCODING, 'gzip')
Я в urllib2 добавлял поддержку gzip так:
С keep-alive у urllib к сожалению всё печально. Рецепты есть, но старые и не поддерживаемые.
class GZipProcessor(urllib2.BaseHandler):
"""A handler to add gzip capabilities to urllib2 requests
http://techknack.net/python-urllib2-handlers/
"""
def http_request(self, req):
req.add_header("Accept-Encoding", "gzip")
return req
https_request = http_request
def http_response(self, req, resp):
if resp.headers.get("content-encoding") == "gzip":
gz = GzipFile(
fileobj=StringIO(resp.read()),
mode="r"
)
old_resp = resp
resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url,
old_resp.code)
resp.msg = old_resp.msg
return resp
https_response = http_response
opener = urllib2.build_opener()
opener.add_handler(GZipProcessor())
opener.open("http://example.com/")
С keep-alive у urllib к сожалению всё печально. Рецепты есть, но старые и не поддерживаемые.
Не понятно на что тут смотреть, даже проверки валидности изображения нет. Отдаст сервер 404 и будет битое изображение где-то потом выдаваться.
Сравнивать Grab не имеет смысла, граб это удобный фрэймворк поверх pycurl, данная операция может быть выполнена как синхронным грабом from grab import Grab так и асинхронным Spider — from grab.spider import Spider. Смысла особого нет замерять время, в конечном итоге все упирается в ширину канала и нестабильный пинг до цели.
Здесь явно используется кэширование. Без кэширования (h = httplib2.Http()) метод работает в 6-9 раза медленнее предыдущих аналогов.
Метод 4, с (без кэширования, с)
0.089 (7.625)
Сперва не заметил «с» перед скобочкой… И подумал, что с кешированием медленнее.
Для Python3 надо написать первые 2 способа так:
Способ 1
Способ 2
Способ 1
from urllib.request import urlopen
resource = urlopen(img)
out = open("...\img.jpg", 'wb')
out.write(resource.read())
out.close()
Способ 2
from urllib.request import urlretrieve
urlretrieve(img, "...\img.jpg")
Может я чего-то не знаю, но я действительно не понимаю, почему эта новость находится на главной странице. Скоро, наверное, будут выкладывать на главную способы вывести «хеллоу ворлд», и пост длиной в абзац.
Sign up to leave a comment.
Четыре метода загрузки изображений с веб-сайта с помощью Python