Иногда бывает нужно набрать картинок по определённой тематике, чтобы иметь возможность выбрать из существующего набора нужную и т.д. Текущие поисковики дают такую возможность, но надо открывать браузер, переходить по страницам, работать мышкой и, вообщем, заниматься этим. Хотелось бы иметь консольную утилиту «запустил и забыл» для набора нужных картинок. Рассматривается Bing API, начало работы на Python и их связка для поиска изображений.
Это моя первая более-менее большая программа на Python'е, который я стал изучать недавно (кстати, огромное спасибо kossmak за его переводы статей). Примеры использования в конце статьи.
Консольная программа, принимающая на вход поисковую строку и требуемое количество картинок. На выходе — поддиректория в текущей с результатами поиска.
Некоторое время назад нужно было протестировать асинхронный загрузчик под ActionScript. Для нагрузки был выбран Google, однако, в результате оказалось, что Google выдаёт не больше 64 результатов по запросам через API. Этого (на тот момент) хватило, но осадок остался. После поисков было найдено: Yahoo (с комментариями, что многие выдаваемые им данные устарели) и Bing (который на своей странице обещает до 1000 результатов). Был выбран Bing, т.к., помимо самого запроса, позволяет накладывать на него фильтры (см. ниже)
Разработка под Bing начинается со страницы Bing Developer Center. Там необходимо получить APP_ID для подписывания каждого запроса, регистрация минутная. Я не очень разбирался с накладываемыми ограничениями (может быть их просто нет) так что публикую вместе с примерами свой тестовый APP_ID (если намерены использовать, то рекомендую завести и вбить свой APP_ID в код).
API существует для VB/C#/C++/F#/JS, но в данном примере используется конечный http request. Описание API для поиска изображений здесь
Итак, минимальный запрос для поиска картинок и ответа в формате JSON выглядит так:
api.search.live.net/json.aspx?appid=APP_ID&sources=image&query=SEARCH_QUERY
Пример запроса (поиск по слову apple):
http://api.search.live.net/json.aspx?appid=4EFC2F2CA1F9547B3C048B40C33A6A4FEF1FAF3B&sources=image&query=apple
Тут всё просто и кросс-платформерно. Сам питон (версии 2.6.x) ставится отсюда. В качестве среды разработки мне очень понравился PyDev. Ставим Eclipse(если ещё нет) и из под него ставим PyDev
Не буду комментировать поблочно, в коде много комментариев, к тому же он не так велик, чтобы не поместить его одним блоком. Коротко:
Для уточнения запроса можно использовать фильтры Bing API, разделённые пробелом.
Для этого понадобится py2exe, установить можно отсюда. Далее, в папке с программой создаётся файл setup.py со следующим содержимым (программа в файле bing.py):
И запускается на выполнение командой «python setup.py». В результате выполнения в папке ./dist оказывается «скомпилированная» программа (файл w9xpopen.exe можно стереть)
Далее её можно пожать UPX'ом (с 5182Kb ужалось до 4061Кb)
Странный хабра-глюк.
Не выводит ничего.
Также ссылки вида
Выводятся без http://
Скомпированный exe под Win32 тут.
Введение
Это моя первая более-менее большая программа на Python'е, который я стал изучать недавно (кстати, огромное спасибо kossmak за его переводы статей). Примеры использования в конце статьи.
ТЗ
Консольная программа, принимающая на вход поисковую строку и требуемое количество картинок. На выходе — поддиректория в текущей с результатами поиска.
Почему Bing
Некоторое время назад нужно было протестировать асинхронный загрузчик под ActionScript. Для нагрузки был выбран Google, однако, в результате оказалось, что Google выдаёт не больше 64 результатов по запросам через API. Этого (на тот момент) хватило, но осадок остался. После поисков было найдено: Yahoo (с комментариями, что многие выдаваемые им данные устарели) и Bing (который на своей странице обещает до 1000 результатов). Был выбран Bing, т.к., помимо самого запроса, позволяет накладывать на него фильтры (см. ниже)
Bing
Разработка под Bing начинается со страницы Bing Developer Center. Там необходимо получить APP_ID для подписывания каждого запроса, регистрация минутная. Я не очень разбирался с накладываемыми ограничениями (может быть их просто нет) так что публикую вместе с примерами свой тестовый APP_ID (если намерены использовать, то рекомендую завести и вбить свой APP_ID в код).
Bing API
API существует для VB/C#/C++/F#/JS, но в данном примере используется конечный http request. Описание API для поиска изображений здесь
Итак, минимальный запрос для поиска картинок и ответа в формате JSON выглядит так:
api.search.live.net/json.aspx?appid=APP_ID&sources=image&query=SEARCH_QUERY
Пример запроса (поиск по слову apple):
http://api.search.live.net/json.aspx?appid=4EFC2F2CA1F9547B3C048B40C33A6A4FEF1FAF3B&sources=image&query=apple
Python
Тут всё просто и кросс-платформерно. Сам питон (версии 2.6.x) ставится отсюда. В качестве среды разработки мне очень понравился PyDev. Ставим Eclipse(если ещё нет) и из под него ставим PyDev
Алгоритм
Не буду комментировать поблочно, в коде много комментариев, к тому же он не так велик, чтобы не поместить его одним блоком. Коротко:
- В главном цикла отсылается запрос на Bing API и увеличивается параметр image.offset пока либо не наберётся требуемое количество изображений, либо Bing API не выдаст, что мол результаты кончились.
- Каждый запрос запрашивает по 8 картинок (остановился на таком размере, 4 слишком мало, для 16 иногда долго ждать ответа, максимум 50).
- Для каждой найденной картинки извлекается URL, и заводится нитка, которая скачивает картинку в память и сохраняет на диск. Здесь столкнулся с проблемой — картинки очень часто называются одинаково. Так что функция сохранения «блокирует» остальные нити, и добавляет к имени файла "_" спереди, пока не окажется что такого файла ещё нет. Далее сохранение и раблокировка.
Код
# import used libraries
import urllib, json, sys, os, threading
def load_url(url, filename, filesystem_lock):
try:
# open connection to URL
socket = urllib.urlopen(url)
# read data
data = socket.read()
# close connection
socket.close()
# on all exceptions
except:
print "error loading", url
# if no exceptions
else:
# save loaded data
save_to_file(data, filename, filesystem_lock)
def save_to_file(data, filename, filesystem_lock):
# wait for file system and block it
filesystem_lock.acquire()
try:
# while already have file with this name
while os.path.isfile(filename):
# append '_' to the beginning of file name
filename = os.path.dirname(filename) + "/_" + os.path.basename(filename)
# open for binary writing
with open(filename, 'wb') as f:
# and save data
f.write(data)
f.close()
print filename
except:
print "error saving", filename
# release file system
filesystem_lock.release()
def main():
# Bing search URL
SERVICE_URL = "http://api.search.live.net/json.aspx"
# request parameters dictionary (will append to SERVICE_URL)
params = {}
params["appid"] = "4EFC2F2CA1F9547B3C048B40C33A6A4FEF1FAF3B"
params["sources"] = "image"
params["image.count"] = 8
params["image.offset"] = 00
# try to read command line parameters
try:
params["query"] = sys.argv[1]
images_count = int(sys.argv[2])
if len(sys.argv) > 3:
params["image.filters"] = sys.argv[3]
# if have less than 2 parameters (IndexError) or
# if second parameter cannot be cast to int (ValueError)
except (IndexError, ValueError):
# print usage string
print "Bing image search tool"
print "Usage: bing.py search_str images_count [filters]"
# end exit
return 1
# make directory at current path
dir_name = "./" + params["query"] + "/"
if not os.path.isdir(dir_name):
os.mkdir(dir_name)
# list to store loading threads
loaders = []
# file system lock object
filesystem_lock = threading.Lock()
try:
# loop for images count
while(params["image.offset"] < images_count):
# combine URL string, open it and parse with JSON
response = json.load(urllib.urlopen(SERVICE_URL + "?%s" % urllib.urlencode(params)))
# extract image section
images_section = response["SearchResponse"]["Image"]
# if current search offset greater or equal to returned total files
if "Total" not in images_section or params["image.offset"] >= images_section["Total"]:
# then break search loop
break
# extract image results section
results = images_section["Results"]
# loop for results
for result in results:
# extract image URL
image_url = result["MediaUrl"]
# create new loading thread
loader = threading.Thread(\
target = load_url,\
args=(\
image_url,\
dir_name + os.path.basename(str(image_url)),\
filesystem_lock))
# start loading thread
loader.start()
# and add it to loaders list
loaders.append(loader)
# advance search offset
params["image.offset"] += 1
# break if no more images needed
if params["image.offset"] >= images_count:
break;
# on all exceptions
except:
print "error occured"
return 1
# wait for all loading threads to complete
for loader in loaders:
loader.join()
# all done
print "done"
return 0;
if __name__ == '__main__':
status = main()
sys.exit(status)
Примеры запросов
Для уточнения запроса можно использовать фильтры Bing API, разделённые пробелом.
bing.py apple 1000
— найти 1000 картинок по запросу «apple».bing.py "obama" 16 "size:large style:graphics face:face"
— найти 16 портретов Обамы, большого размера в стиле иллюстрации.bing.py "warhammer wallpaper" 16 "size:width:1280 size:height:1024"
— найти 16 обоев по теме «warhammer», размерами 1280x1024
Создание single-exe под win32
Для этого понадобится py2exe, установить можно отсюда. Далее, в папке с программой создаётся файл setup.py со следующим содержимым (программа в файле bing.py):
from distutils.core import setup
import py2exe, sys, os
sys.argv.append('py2exe')
setup(
console=['bing.py'],
options = {'py2exe': {'bundle_files': 1}},
zipfile = None,
)
И запускается на выполнение командой «python setup.py». В результате выполнения в папке ./dist оказывается «скомпилированная» программа (файл w9xpopen.exe можно стереть)
Далее её можно пожать UPX'ом (с 5182Kb ужалось до 4061Кb)
Что хотелось бы улучшить
- Запросы на русском
- Общий индикатор прогресса по всем файлам
- Прогресс загрузки по каждого файла
- Использование time-out при попытке загрузки изображения (он по-умолчанию вроде бы минутный)
- Нормальная обработка ошибок
P.S.
Странный хабра-глюк.
<code><font color="#666666">0</font></code>
Не выводит ничего.
Также ссылки вида
<code>http://api.google.com</code>
Выводятся без http://
P.P.S.
Скомпированный exe под Win32 тут.