├── flickrDownloader ├── __init__.py ├── utils.py └── flickrDownloader.py ├── flickr.apikey ├── .idea ├── vcs.xml ├── misc.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── modules.xml └── flickr-image-downloader.iml ├── setup.py ├── LICENSE ├── .gitignore ├── main_usage_examples.py └── README.md /flickrDownloader/__init__.py: -------------------------------------------------------------------------------- 1 | from flickrDownloader import * -------------------------------------------------------------------------------- /flickr.apikey: -------------------------------------------------------------------------------- 1 | INSERT_HERE_YOUR_API_KEY 2 | (request an api key here: https://www.flickr.com/services/api/misc.api_keys.html) 3 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/flickr-image-downloader.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from setuptools import find_packages 3 | 4 | 5 | setup(name='flickrDownloader', 6 | version='0.1.3', 7 | description='Helper for Flickr photos.search API', 8 | author='Riccardo Del Chiaro', 9 | author_email='riccardo.delchiaro@gmail.com', 10 | url='https://github.com/nagash91/python-flickr-image-downloader', 11 | license='MIT', 12 | packages=find_packages(), 13 | package_dir={'flickrDownloader':'flickrDownloader'}, 14 | long_description=open('README.md').read(), 15 | requires=['requests'] 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Riccardo Del Chiaro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment next line to not share the flickr.apikey: 2 | # flickr.apikey 3 | 4 | # Ignore building files (generated by setuptools, setup.py) 5 | build 6 | dist 7 | *.egg-info 8 | 9 | # Byte-compiled / optimized / DLL files 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | 14 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 15 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 16 | 17 | # User-specific stuff: 18 | .idea/**/workspace.xml 19 | .idea/**/tasks.xml 20 | 21 | # Sensitive or high-churn files: 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.xml 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | 30 | # Gradle: 31 | .idea/**/gradle.xml 32 | .idea/**/libraries 33 | 34 | # Mongo Explorer plugin: 35 | .idea/**/mongoSettings.xml 36 | 37 | ## File-based project format: 38 | *.iws 39 | 40 | ## Plugin-specific files: 41 | 42 | # IntelliJ 43 | /out/ 44 | 45 | # mpeltonen/sbt-idea plugin 46 | .idea_modules/ 47 | 48 | # JIRA plugin 49 | atlassian-ide-plugin.xml 50 | 51 | # Crashlytics plugin (for Android Studio and IntelliJ) 52 | com_crashlytics_export_strings.xml 53 | crashlytics.properties 54 | crashlytics-build.properties 55 | fabric.properties -------------------------------------------------------------------------------- /flickrDownloader/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import urllib2 3 | from urllib2 import Request, urlopen 4 | from urllib2 import URLError, HTTPError 5 | 6 | def string_or_path(string_or_file): 7 | if os.path.isfile(string_or_file): 8 | ret = open(string_or_file, 'r').read() 9 | else: 10 | ret = string_or_file 11 | return ret 12 | 13 | 14 | def web_downloader(link_list, download_path, save_filename_prefix="", save_filename_postfix="_", forced_extension=None, 15 | verbose=False, ignore_errors=False): 16 | # type: (list(str), str, str, str, bool, bool) -> None 17 | 18 | client_header = { 19 | "User-Agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17"} 20 | 21 | if download_path != "" and not os.path.isdir(download_path): 22 | os.mkdir(download_path) 23 | 24 | error_count = 0 25 | for k, link in enumerate(link_list): 26 | 27 | try: 28 | req = Request(link, headers=client_header) 29 | response = urlopen(req) 30 | if forced_extension is not None: 31 | extension = forced_extension 32 | else: 33 | extension = os.path.splitext(link)[1].lower() 34 | output_file = open(os.path.join(download_path, save_filename_prefix + save_filename_postfix + str(k) 35 | + extension), 'wb') 36 | data = response.read() 37 | output_file.write(data) 38 | response.close() 39 | if verbose: 40 | print("completed ====> " + str(k)) 41 | 42 | # IN this saving process we are just skipping the URL if there is any error 43 | except IOError: # If there is any IOError 44 | error_count += 1 45 | if not ignore_errors: 46 | print("IOError on image " + str(k)) 47 | 48 | except HTTPError: # If there is any HTTPError 49 | error_count += 1 50 | if not ignore_errors: 51 | print("HTTPError" + str(k)) 52 | 53 | except URLError: 54 | error_count += 1 55 | if not ignore_errors: 56 | print("URLError " + str(k)) 57 | 58 | except ValueError: 59 | error_count += 1 60 | if not ignore_errors: 61 | print("ValueERROR " + str(k)) 62 | 63 | if verbose: 64 | print("\nAll are downloaded") 65 | if verbose or not ignore_errors: 66 | print("Total Errors ----> " + str(error_count) ) 67 | 68 | return error_count 69 | 70 | 71 | 72 | import re, urlparse 73 | def urlEncodeNonAscii(b): 74 | return re.sub('[\x80-\xFF]', lambda c: '%%%02x' % ord(c.group(0)), b) 75 | 76 | 77 | # USE urllib2.quote instead 78 | # def iriToUri(iri): 79 | # parts= urlparse.urlparse(iri) 80 | # return urlparse.urlunparse( 81 | # part.encode('idna') if parti==1 else urlEncodeNonAscii(part.encode('utf-8')) 82 | # for parti, part in enumerate(parts) 83 | # ) -------------------------------------------------------------------------------- /main_usage_examples.py: -------------------------------------------------------------------------------- 1 | from flickrDownloader import * 2 | 3 | 4 | api_key = "INSERT_HERE_YOUR_API_KEY" # if you want to insert your apikey in source code 5 | api_key = "flickr.apikey" # if you want to read apikey from file 6 | 7 | # If you want to share your code in git, you may want not to share your api key too! 8 | # In that case, insert your api key in the flickr.apikey file and add flickr.apikey in your .gitignore 9 | 10 | 11 | # Available licenses: (from: https://www.flickr.com/services/api/explore/flickr.photos.licenses.getInfo) 12 | # 13 | # {"id": 0, "name": "All Rights Reserved", "url": ""}, 14 | # {"id": 4, "name": "Attribution License", "url": "https:\/\/creativecommons.org\/licenses\/by\/2.0\/"}, 15 | # {"id": 6, "name": "Attribution-NoDerivs License", "url": "https:\/\/creativecommons.org\/licenses\/by-nd\/2.0\/"}, 16 | # {"id": 3, "name": "Attribution-NonCommercial-NoDerivs License", "url": "https:\/\/creativecommons.org\/licenses\/by-nc-nd\/2.0\/"}, 17 | # {"id": 2, "name": "Attribution-NonCommercial License", "url": "https:\/\/creativecommons.org\/licenses\/by-nc\/2.0\/"}, 18 | # {"id": 1, "name": "Attribution-NonCommercial-ShareAlike License", "url": "https:\/\/creativecommons.org\/licenses\/by-nc-sa\/2.0\/"}, 19 | # {"id": 5, "name": "Attribution-ShareAlike License", "url": "https:\/\/creativecommons.org\/licenses\/by-sa\/2.0\/"}, 20 | # {"id": 7, "name": "No known copyright restrictions", "url": "https:\/\/www.flickr.com\/commons\/usage\/"}, 21 | # {"id": 8, "name": "United States Government Work", "url": "http:\/\/www.usa.gov\/copyright.shtml"}, 22 | # {"id": 9, "name": "Public Domain Dedication (CC0)", "url": "https:\/\/creativecommons.org\/publicdomain\/zero\/1.0\/"}, 23 | # {"id": 10, "name": "Public Domain Mark", "url": "https:\/\/creativecommons.org\/publicdomain\/mark\/1.0\/"} 24 | license_id = 10 # "using public domain mark" license 25 | 26 | 27 | link_list = flickr_photos_downloader(api_key, 28 | query_text="david michelangelo", 29 | # tags="", 30 | tag_mode=FlickrTagMode.all, 31 | download_path="michelangelo_download", 32 | image_size=FlickrImageSize.square_150x150, 33 | n_images=100, 34 | verbose=True, 35 | license_id=license_id) 36 | 37 | flickr_photos_downloader(api_key, 38 | n_images=10, 39 | query_text="Firenze", 40 | tags="Art", 41 | tag_mode=FlickrTagMode.any, 42 | image_size=FlickrImageSize.longedge_1600, 43 | content_type=FlickrContentType.photos, 44 | media=FlickrMedia.photos, 45 | download_path="img_downloads", 46 | save_filename_prefix="flickr_downloaded_", 47 | forced_extension=None, 48 | verbose=True, 49 | ignore_errors=False, 50 | license_id=license_id) 51 | 52 | 53 | only_link = flickr_photos_links(api_key, 54 | n_images=1500, 55 | query_text="Firenze", 56 | tags="Art", 57 | tag_mode=FlickrTagMode.any, 58 | image_size=FlickrImageSize.longedge_1600, 59 | content_type=FlickrContentType.photos, 60 | media=FlickrMedia.photos, 61 | verbose=True, 62 | ignore_errors=False, 63 | license_id=license_id) 64 | 65 | for i, link in enumerate(only_link): 66 | print str(i) + "\t-\t" + link 67 | 68 | responsesJson = flickr_photos_search(api_key, 69 | n_images=1500, 70 | query_text="Firenze", 71 | tags="Art", 72 | tag_mode=FlickrTagMode.any, 73 | content_type=FlickrContentType.photos, 74 | media=FlickrMedia.photos, 75 | response_format=FlickrResponseFormat.JSON, 76 | license_id=license_id) 77 | 78 | 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Flickr Images Downloader 2 | Python functions for searching and downloading images/links from Flickr.
3 | Use the flickr.photos.search API to search image (using tags/text):
4 | https://www.flickr.com/services/api/explore/flickr.photos.search
5 | 6 | ## Compatability 7 | Tested only on python 2.7 8 | 9 | ## Limitations 10 | Not all the possible search arguments are implemented (for example place, geolocalization, and others). 11 | 12 | 13 | ## Disclaimer 14 | This program lets you download tons of images from Flickr.
15 | Please do not violate its copyright or license terms.
16 |
17 | Flickr photos licenses API:
18 | https://www.flickr.com/services/api/flickr.photos.licenses.getInfo.html 19 | https://www.flickr.com/services/api/explore/flickr.photos.licenses.getInfo 20 | 21 | ## Install 22 | git clone https://github.com/nagash91/python-flickr-image-downloader.git
23 | cd python-flickr-image-downloader
24 | python setup.py install
25 |
26 | 27 | ## Usage 28 | ```python 29 | from flickrDownloader import * 30 | 31 | api_key = "INSERT_HERE_YOUR_API_KEY" # if you want to insert your apikey in source code 32 | api_key = "flickr.apikey" # if you want to read apikey from file 33 | 34 | license_id = 10 # "using public domain mark" license 35 | 36 | # Get links and download photos: 37 | flickr_photos_downloader(api_key, 38 | n_images=10, 39 | query_text="Firenze", 40 | tags="Art", 41 | tag_mode=FlickrTagMode.any, 42 | image_size=FlickrImageSize.longedge_1600, 43 | content_type=FlickrContentType.photos, 44 | media=FlickrMedia.photos, 45 | download_path="img_downloads", 46 | save_filename_prefix="flickr_downloaded_", 47 | forced_extension=None, 48 | verbose=True, 49 | ignore_errors=False, 50 | license_id=license_id) 51 | 52 | # Get links only: 53 | only_link = flickr_photos_links(api_key, 54 | n_images=1500, 55 | query_text="Firenze", 56 | tags="Art", 57 | tag_mode=FlickrTagMode.any, 58 | image_size=FlickrImageSize.longedge_1600, 59 | content_type=FlickrContentType.photos, 60 | media=FlickrMedia.photos, 61 | verbose=True, 62 | ignore_errors=False, 63 | license_id=license_id) 64 | # Print links: 65 | for i, link in enumerate(only_link): 66 | print str(i) + "\t-\t" + link 67 | ``` 68 | 69 |
70 |
71 | Available options: 72 | 73 | ```python 74 | # ENUM DEFINED FOR OPTIONS: 75 | 76 | class FlickrContentType(Enum): 77 | default = "" 78 | photos = "&content_type=1" 79 | screenshoots = "&content_type=2" 80 | other = "&content_type=3" 81 | photos_screenshots = "&content_type=4" 82 | screenshoots_other = "&content_type=5" 83 | photos_other = "&content_type=6" 84 | photos_screenshots_other = "&content_type=7" 85 | 86 | 87 | class FlickrTagMode(Enum): 88 | default = "" 89 | any = '&tag_mode=any' # logic OR 90 | all = '&tag_mode=all' # logic AND 91 | 92 | 93 | class FlickrMedia(Enum): 94 | default = "" 95 | photos = "&media=photos" 96 | videos = "&media=videos" 97 | 98 | 99 | class FlickrResponseFormat(Enum): 100 | JSON = "&format=json&nojsoncallback=1" 101 | JSONP = "&format=json" 102 | XML = "&format=rest" 103 | PHP_SERIAL = "&format=php_serial" 104 | default = JSON 105 | 106 | class FlickrImageSize(Enum): 107 | default = "" 108 | square_75x75 = "_s" # 75 x 75 109 | square_150x150 = "_q" # 150 x 150 110 | longedge_100 = "_t" # 100 on the longest edge 111 | longedge_240 = "_m" # 240 on the longest edge 112 | longedge_320 = "_n" # 320 on the longest edge 113 | longedge_500 = "_-" # 500 on the longest edge 114 | longedge_640 = "_z" # 640 on the longest edge 115 | longedge_800 = "_c" # 800 on the longest edge (flickr new feature from 01/03/2012) 116 | longedge_1024 = "_b" # 1024 on the longest edge 117 | longedge_1600 = "_h" # 1600 on the longest edge (flickr new feature from 01/03/2012) 118 | longedge_2048 = "_k" # 2048 on the longest edge (flickr new feature from 01/03/2012) 119 | ``` 120 | -------------------------------------------------------------------------------- /flickrDownloader/flickrDownloader.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib2 3 | from enum import Enum 4 | import requests 5 | from requests import Response 6 | 7 | from utils import string_or_path 8 | from utils import web_downloader 9 | 10 | # TODO: always using public for now 11 | class FlickrPrivacyFilter(Enum): 12 | default = u"" 13 | public = u"&privacy_filter=1" 14 | friends = u"&privacy_filter=2" 15 | family = u"&privacy_filter=3" 16 | friends_family = u"&privacy_filter=4" 17 | private = u"&privacy_filter=5" 18 | 19 | 20 | class FlickrContentType(Enum): 21 | default = u"" 22 | photos = u"&content_type=1" 23 | screenshoots = u"&content_type=2" 24 | other = u"&content_type=3" 25 | photos_screenshots = u"&content_type=4" 26 | screenshoots_other = u"&content_type=5" 27 | photos_other = u"&content_type=6" 28 | photos_screenshots_other = u"&content_type=7" 29 | 30 | 31 | class FlickrTagMode(Enum): 32 | default = u"" 33 | any = u'&tag_mode=any' # logic OR 34 | all = u'&tag_mode=all' # logic AND 35 | 36 | 37 | class FlickrMedia(Enum): 38 | default = u"" 39 | photos = u"&media=photos" 40 | videos = u"&media=videos" 41 | 42 | 43 | class FlickrResponseFormat(Enum): 44 | JSON = u"&format=json&nojsoncallback=1" 45 | JSONP = u"&format=json" 46 | XML = u"&format=rest" 47 | PHP_SERIAL = u"&format=php_serial" 48 | default = JSON 49 | 50 | class FlickrImageSize(Enum): 51 | default = u"" 52 | square_75x75 = u"_s" # 75 x 75 53 | square_150x150 = u"_q" # 150 x 150 54 | longedge_100 = u"_t" # 100 on the longest edge 55 | longedge_240 = u"_m" # 240 on the longest edge 56 | longedge_320 = u"_n" # 320 on the longest edge 57 | longedge_500 = u"_-" # 500 on the longest edge 58 | longedge_640 = u"_z" # 640 on the longest edge 59 | longedge_800 = u"_c" # 800 on the longest edge (flickr new feature from 01/03/2012) 60 | longedge_1024 = u"_b" # 1024 on the longest edge 61 | longedge_1600 = u"_h" # 1600 on the longest edge (flickr new feature from 01/03/2012) 62 | longedge_2048 = u"_k" # 2048 on the longest edge (flickr new feature from 01/03/2012) 63 | 64 | 65 | # TODO: original image: jpg, gif or png according to the source format, not supported yet 66 | # original = "_o" 67 | # Require original secret (o-secret) but in responses I can't see that entry: 68 | # https://farm{farm-id}.staticflickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png) 69 | # class FlickrImageExtension(Enum): 70 | # jpg = "jpg" 71 | # png = "png" 72 | # gif = "gif" 73 | # default = jpg 74 | 75 | # TODO: add localizations 76 | def flickr_photos_search(api_key_or_file_path, # type: unicode 77 | n_images=100, # type: int 78 | query_text=None, # type: unicode 79 | tags=None, # type: unicode 80 | tag_mode=FlickrTagMode.default, # type: FlickrTagMode 81 | content_type=FlickrContentType.default, # type: FlickrContentType 82 | media=FlickrMedia.default, # type: FlickrMedia 83 | response_format=FlickrResponseFormat.JSON, # type: FlickrResponseFormat 84 | license_id=None 85 | ): 86 | # type: (unicode, int, unicode, unicode, FlickrTagMode, FlickrContentType, FlickrMedia, FlickrResponseFormat) -> list(Response) 87 | """ 88 | 89 | :rtype: list(Response) 90 | """ 91 | MAX_IMAGES_PER_PAGE = 500 # fixed by flickr api 92 | if isinstance(api_key_or_file_path, str): 93 | api_key_or_file_path = unicode(api_key_or_file_path, "UTF-8") 94 | if not isinstance(api_key_or_file_path, unicode): 95 | raise ValueError(u"api_key_or_file_path must be a unicode (flickr api key or path to text file containing key).") 96 | api_key = string_or_path(api_key_or_file_path).split(" ")[0].split("\n")[0] 97 | 98 | 99 | query = u"https://api.flickr.com/services/rest/?method=flickr.photos.search" 100 | query += u"&api_key=" + api_key 101 | 102 | if isinstance(query_text, str): 103 | query_text = unicode(query_text, "UTF-8") 104 | if isinstance(tags, str): 105 | tags = unicode(tags, "UTF-8") 106 | #if isinstance(query_text, unicode): 107 | if query_text is not None: 108 | query += u"&text=" + urllib2.quote(query_text.encode('utf-8')) 109 | 110 | #if isinstance(tags, unicode): 111 | if tags is not None: 112 | query += u"&tags=" + urllib2.quote(tags.encode('utf-8')) + tag_mode.value 113 | # query.replace(" ", "%20") 114 | query += content_type.value + media.value + response_format.value + FlickrPrivacyFilter.public.value 115 | 116 | if license_id is not None: 117 | query += u"&license_id=" + unicode(license_id) 118 | 119 | rest = n_images % MAX_IMAGES_PER_PAGE 120 | n_queries = n_images/MAX_IMAGES_PER_PAGE 121 | query_len_list = [MAX_IMAGES_PER_PAGE] * n_queries 122 | if rest > 0: 123 | query_len_list.append(rest) 124 | 125 | responses = [] 126 | for i, query_len in enumerate(query_len_list): 127 | page_query = query + u"&per_page=" + unicode(query_len) + u"&page=" + unicode(i+1) 128 | responses.append(requests.get(page_query)) 129 | 130 | return responses 131 | 132 | 133 | def flickr_photos_links(api_key_or_file_path, # type: unicode 134 | n_images=100, # type: int 135 | query_text=None, # type: unicode 136 | tags=None, # type: unicode 137 | image_size=FlickrImageSize.default, # type: FlickrImageSize 138 | tag_mode=FlickrTagMode.default, # type: FlickrTagMode 139 | content_type=FlickrContentType.default, # type: FlickrContentType 140 | media=FlickrMedia.default, # type: FlickrMedia 141 | license_id=None, 142 | verbose=False, 143 | ignore_errors=False, 144 | max_error_retries=3 145 | ): 146 | # type: (...) -> list(unicode) 147 | 148 | retry = 0 149 | links = [] 150 | 151 | while retry < max_error_retries: 152 | links = [] 153 | 154 | try: 155 | responses = flickr_photos_search(api_key_or_file_path=api_key_or_file_path, n_images=n_images, 156 | query_text=query_text, tags=tags, tag_mode=tag_mode, 157 | content_type=content_type, media=media, 158 | response_format=FlickrResponseFormat.JSON, license_id=license_id) 159 | 160 | for response in responses: 161 | if response.ok: 162 | content = response.content 163 | data = json.loads(content) 164 | 165 | 166 | if 'photos' in data.keys(): 167 | data = data['photos']['photo'] 168 | for d in data: 169 | # https://farm{farm-id}.staticflickr.com/{server-id}/{id}_{secret}_[mstzb].jpg 170 | lnk = u"https://farm{}.staticflickr.com/{}/{}_{}{}.jpg"\ 171 | .format(d['farm'], d['server'], d['id'], d['secret'], image_size.value) 172 | links.append(lnk) 173 | else: 174 | if not ignore_errors: 175 | print(u"Format error in received json (can't find key 'photos').") 176 | if 'message' in data.keys(): 177 | print(u"Received Message: {}".format(data['message'].encode("utf-8"))) 178 | 179 | else: 180 | if not ignore_errors: 181 | print(u"Flickr response not ok.") 182 | if verbose: 183 | print(u"Links retrived from flickr responses: {}".format(len(links))) 184 | return links 185 | 186 | except ValueError as v: 187 | retry-=1 188 | if verbose or not ignore_errors: 189 | print(u"Value Error in flickr_photos_links process:") 190 | print(v.message) 191 | if retry list(unicode) 219 | 220 | links = flickr_photos_links(api_key_or_file_path=api_key_or_file_path, query_text=query_text, tags=tags, n_images=n_images, 221 | image_size=image_size, tag_mode=tag_mode, content_type=content_type, media=media, 222 | verbose=verbose, ignore_errors=ignore_errors, license_id=license_id, max_error_retries=max_error_retries) 223 | web_downloader(link_list=links, download_path=download_path, save_filename_prefix=save_filename_prefix, 224 | forced_extension=forced_extension, verbose=verbose, ignore_errors=ignore_errors) 225 | return links 226 | 227 | 228 | 229 | 230 | 231 | 232 | # Flickr Documentations: 233 | # For search: 234 | # https://www.flickr.com/services/api/explore/flickr.photos.search 235 | # (use public = 1 in the query!) 236 | # (log in in flickr to automatically insert my api key). 237 | # 238 | # 239 | # To download images, look at here: https://www.flickr.com/services/api/misc.urls.html 240 | # example: http://farm3.staticflickr.com/2636/32179988483_cd41d8fca9_b.jpg 241 | # 242 | # Otherwise you can use getSize query and use the source response to get the direct link: 243 | # http://www.flickr.com/services/api/flickr.photos.getSizes.html 244 | # 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | # * * * * * * * * * * * * * * * * * * * SEARCH API * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 258 | # SEARCH API ( https://www.flickr.com/services/api/flickr.photos.search.html ) 259 | # EXAMPLE OF QUERY: 260 | # https://api.flickr.com/services/rest/?method=flickr.photos.search 261 | # &api_key=API_KEY 262 | # &tags=art 263 | # &text=david 264 | # &per_page=100 265 | # &page=1 266 | # &format=json 267 | 268 | # tags (Facoltativo) 269 | # A comma-delimited list of tags. Photos with one or more of the tags listed will be returned. You can exclude results 270 | # that match a term by prepending it with a - character. 271 | 272 | # tag_mode (Facoltativo) 273 | # Either 'any' for an OR combination of tags, or 'all' for an AND combination. Defaults to 'any' if not specified. 274 | 275 | # text (Facoltativo) 276 | # A free text search. Photos who's title, description or tags contain the text will be returned. You can exclude results 277 | # that match a term by prepending it with a - character. 278 | 279 | # sort (Facoltativo) 280 | # The order in which to sort returned photos. Deafults to date-posted-desc (unless you are doing a radial geo query, in 281 | # which case the default sorting is by ascending distance from the point specified). The possible values are: 282 | # date-posted-asc, 283 | # date-posted-desc, 284 | # date-taken-asc, 285 | # date-taken-desc, 286 | # interestingness-desc, 287 | # interestingness-asc, 288 | # relevance. 289 | 290 | 291 | # privacy_filter (Facoltativo) 292 | # Return photos only matching a certain privacy level. 293 | # This only applies when making an authenticated call to view photos you own. 294 | # Valid values are: 295 | # 1 public photos 296 | # 2 private photos visible to friends 297 | # 3 private photos visible to family 298 | # 4 private photos visible to friends & family 299 | # 5 completely private photos 300 | 301 | 302 | # content_type (Facoltativo) 303 | # Content Type setting: 304 | # 1 for photos only. 305 | # 2 for screenshots only. 306 | # 3 for 'other' only. 307 | # 4 for photos and screenshots. 308 | # 5 for screenshots and 'other'. 309 | # 6 for photos and 'other'. 310 | # 7 for photos, screenshots, and 'other' (all). 311 | 312 | 313 | # media (Facoltativo) 314 | # Filter results by media type. Possible values are all (default), photos or videos 315 | 316 | 317 | 318 | # per_page (Facoltativo) 319 | # Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500. 320 | 321 | # page (Facoltativo) 322 | # The page of results to return. If this argument is omitted, it defaults to 1. 323 | 324 | 325 | 326 | 327 | # FOR LOCALIZATION: 328 | 329 | # geo_context (Facoltativo) 330 | # Geo context is a numeric value representing the photo's geotagginess beyond latitude and longitude. 331 | # For example, you may wish to search for photos that were taken "indoors" or "outdoors". 332 | # The current list of context IDs is : 333 | # 0, not defined. 334 | # 1, indoors. 335 | # 2, outdoors. 336 | # Geo queries require some sort of limiting agent in order to prevent the database from crying. 337 | # This is basically like the check against "parameterless searches" for queries without a geo component. 338 | # 339 | 340 | # A tag, for instance, is considered a limiting agent as are user defined min_date_taken and min_date_upload parameters 341 | # If no limiting factor is passed we return only photos added in the last 12 hours (though we may extend the limit in the future). 342 | # lat (Facoltativo) 343 | # A valid latitude, in decimal format, for doing radial geo queries. 344 | # Geo queries require some sort of limiting agent in order to prevent the database from crying. 345 | # This is basically like the check against "parameterless searches" for queries without a geo component. 346 | # A tag, for instance, is considered a limiting agent as are user defined min_date_taken and min_date_upload parameters 347 | # If no limiting factor is passed we return only photos added in the last 12 hours 348 | # (though we may extend the limit in the future). 349 | 350 | # lon (Facoltativo) 351 | # A valid longitude, in decimal format, for doing radial geo queries. 352 | # Geo queries require some sort of limiting agent in order to prevent the database from crying. 353 | # This is basically like the check against "parameterless searches" for queries without a geo component. 354 | # A tag, for instance, is considered a limiting agent as are user defined min_date_taken and min_date_upload parameters 355 | # If no limiting factor is passed we return only photos added in the last 12 hours 356 | # (though we may extend the limit in the future). 357 | 358 | 359 | # radius (Facoltativo) 360 | # A valid radius used for geo queries, greater than zero and less than 20 miles (or 32 kilometers), for use with point-based geo queries. The default value is 5 (km). 361 | 362 | # radius_units (Facoltativo) 363 | # The unit of measure when doing radial geo queries. Valid options are "mi" (miles) and "km" (kilometers). The default is "km". 364 | 365 | 366 | 367 | # bbox (Facoltativo) 368 | # A comma-delimited list of 4 values defining the Bounding Box of the area that will be searched. 369 | # The 4 values represent the bottom-left corner of the box and the top-right corner, minimum_longitude, 370 | # minimum_latitude, maximum_longitude, maximum_latitude. 371 | # Longitude has a range of -180 to 180 , latitude of -90 to 90. Defaults to -180, -90, 180, 90 if not specified. 372 | # Unlike standard photo queries, geo (or bounding box) queries will only return 250 results per page. 373 | # Geo queries require some sort of limiting agent in order to prevent the database from crying. 374 | # This is basically like the check against "parameterless searches" for queries without a geo component. 375 | # A tag, for instance, is considered a limiting agent as are user defined min_date_taken and min_date_upload parameters 376 | # If no limiting factor is passed we return only photos added in the last 12 hours (though we may extend the limit in the future). 377 | 378 | # accuracy (Facoltativo) 379 | # Recorded accuracy level of the location information. Current range is 1-16 : 380 | # World level is 1 381 | # Country is ~3 382 | # Region is ~6 383 | # City is ~11 384 | # Street is ~16 385 | # Defaults to maximum value if not specified. 386 | # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 387 | --------------------------------------------------------------------------------