Bing + Python, поиск изображений (original) (raw)

Bing+PythonИногда бывает нужно набрать картинок по определённой тематике, чтобы иметь возможность выбрать из существующего набора нужную и т.д. Текущие поисковики дают такую возможность, но надо открывать браузер, переходить по страницам, работать мышкой и, вообщем, заниматься этим. Хотелось бы иметь консольную утилиту «запустил и забыл» для набора нужных картинок. Рассматривается Bing API, начало работы на Python и их связка для поиска изображений.

Введение

Это моя первая более-менее большая программа на 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

Алгоритм

Не буду комментировать поблочно, в коде много комментариев, к тому же он не так велик, чтобы не поместить его одним блоком. Коротко:

Код

_# 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 SERVICEURL)_ 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, разделённые пробелом.

Создание 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)

Что хотелось бы улучшить

P.S.

Странный хабра-глюк.
<code><font color="#666666">0</font></code>
Не выводит ничего.
Также ссылки вида
<code>http://api.google.com</code>
Выводятся без http://

P.P.S.

Скомпированный exe под Win32 тут.