|
3 | 3 | # Generated by Glenn Jocher (glenn.jocher@ultralytics.com) for https://github.com/ultralytics |
4 | 4 |
|
5 | 5 | import argparse |
| 6 | +import os |
6 | 7 | import time |
7 | 8 | from pathlib import Path |
8 | 9 |
|
|
14 | 15 | secret = "" |
15 | 16 |
|
16 | 17 |
|
17 | | -def get_urls(search="honeybees on flowers", n=10, download=False): |
18 | | - """Fetch Flickr URLs for `search` term images, optionally downloading them; supports up to `n` images.""" |
19 | | - t = time.time() |
20 | | - flickr = FlickrAPI(key, secret) |
21 | | - license = () # https://www.flickr.com/services/api/explore/?method=flickr.photos.licenses.getInfo |
22 | | - photos = flickr.walk( |
23 | | - text=search, # http://www.flickr.com/services/api/flickr.photos.search.html |
24 | | - extras="url_o", |
25 | | - per_page=500, # 1-500 |
26 | | - license=license, |
27 | | - sort="relevance", |
| 18 | +def build_photo_url(photo): |
| 19 | + """Return the best available static image URL from a Flickr photo response.""" |
| 20 | + url = photo.get("url_o") # original size |
| 21 | + return url or ( |
| 22 | + f"https://farm{photo.get('farm')}.staticflickr.com/" |
| 23 | + f"{photo.get('server')}/{photo.get('id')}_{photo.get('secret')}_b.jpg" |
28 | 24 | ) |
29 | 25 |
|
| 26 | + |
| 27 | +def resolve_credentials(api_key=None, api_secret=None): |
| 28 | + """Resolve Flickr API credentials from arguments, environment variables, or source constants.""" |
| 29 | + api_key = api_key or os.getenv("FLICKR_API_KEY") or key |
| 30 | + api_secret = api_secret or os.getenv("FLICKR_API_SECRET") or secret |
| 31 | + if not api_key or not api_secret: |
| 32 | + help_url = "https://www.flickr.com/services/apps/create/apply" |
| 33 | + raise ValueError( |
| 34 | + "Flickr API key and secret are required. Pass --key/--secret, set " |
| 35 | + f"FLICKR_API_KEY/FLICKR_API_SECRET, or edit flickr_scraper.py. To apply visit {help_url}" |
| 36 | + ) |
| 37 | + return api_key, api_secret |
| 38 | + |
| 39 | + |
| 40 | +def get_urls(search="honeybees on flowers", n=10, download=False, api_key=None, api_secret=None, page=1): |
| 41 | + """Fetch Flickr URLs for a search term, optionally downloading up to n images.""" |
| 42 | + if n < 1: |
| 43 | + return [] |
| 44 | + |
| 45 | + t = time.time() |
| 46 | + api_key, api_secret = resolve_credentials(api_key, api_secret) |
| 47 | + flickr = FlickrAPI(api_key, api_secret, format="parsed-json") |
| 48 | + licenses = "" # https://www.flickr.com/services/api/explore/?method=flickr.photos.licenses.getInfo |
| 49 | + page = max(page, 1) |
| 50 | + |
30 | 51 | if download: |
31 | 52 | dir_path = Path.cwd() / "images" / search.replace(" ", "_") |
32 | 53 | dir_path.mkdir(parents=True, exist_ok=True) |
33 | 54 |
|
34 | 55 | urls = [] |
35 | | - for i, photo in enumerate(photos): |
36 | | - if i <= n: |
37 | | - try: |
38 | | - url = photo.get("url_o") # original size |
39 | | - if url is None: |
40 | | - url = f"https://farm{photo.get('farm')}.staticflickr.com/{photo.get('server')}/{photo.get('id')}_{photo.get('secret')}_b.jpg" |
41 | | - |
42 | | - if download: |
43 | | - download_uri(url, dir_path) |
44 | | - |
45 | | - urls.append(url) |
46 | | - print(f"{i}/{n} {url}") |
47 | | - except Exception: |
48 | | - print(f"{i}/{n} error...") |
49 | | - |
50 | | - else: |
51 | | - print(f"Done. ({time.time() - t:.1f}s)" + (f"\nAll images saved to {dir_path}" if download else "")) |
| 56 | + while len(urls) < n: |
| 57 | + try: |
| 58 | + response = flickr.photos.search( |
| 59 | + text=search, # https://www.flickr.com/services/api/flickr.photos.search.html |
| 60 | + extras="url_o", |
| 61 | + per_page=min(500, n - len(urls)), # 1-500 |
| 62 | + page=page, |
| 63 | + license=licenses, |
| 64 | + media="photos", |
| 65 | + sort="relevance", |
| 66 | + ) |
| 67 | + except Exception as e: |
| 68 | + raise RuntimeError(f"Flickr API request failed: {e}") from e |
| 69 | + |
| 70 | + if response.get("stat") == "fail": |
| 71 | + code = response.get("code", "unknown") |
| 72 | + message = response.get("message", "unknown Flickr API error") |
| 73 | + raise RuntimeError(f"Flickr API error {code}: {message}") |
| 74 | + |
| 75 | + photos = response.get("photos", {}) |
| 76 | + photo_list = photos.get("photo", []) |
| 77 | + if not photo_list: |
52 | 78 | break |
53 | 79 |
|
| 80 | + for photo in photo_list: |
| 81 | + url = build_photo_url(photo) |
| 82 | + if url in urls: |
| 83 | + continue |
| 84 | + |
| 85 | + if download: |
| 86 | + download_uri(url, dir_path) |
| 87 | + |
| 88 | + urls.append(url) |
| 89 | + print(f"{len(urls)}/{n} {url}") |
| 90 | + |
| 91 | + if len(urls) >= n: |
| 92 | + break |
| 93 | + |
| 94 | + if page >= int(photos.get("pages", page)): |
| 95 | + break |
| 96 | + page += 1 |
| 97 | + |
| 98 | + print(f"Done. ({time.time() - t:.1f}s)" + (f"\nAll images saved to {dir_path}" if download else "")) |
| 99 | + return urls |
| 100 | + |
54 | 101 |
|
55 | 102 | if __name__ == "__main__": |
56 | 103 | parser = argparse.ArgumentParser() |
57 | 104 | parser.add_argument("--search", nargs="+", default=["honeybees on flowers"], help="flickr search term") |
58 | 105 | parser.add_argument("--n", type=int, default=10, help="number of images") |
59 | 106 | parser.add_argument("--download", action="store_true", help="download images") |
| 107 | + parser.add_argument("--page", type=int, default=1, help="Flickr results page to start from") |
| 108 | + parser.add_argument("--key", default=None, help="Flickr API key, overrides FLICKR_API_KEY") |
| 109 | + parser.add_argument("--secret", default=None, help="Flickr API secret, overrides FLICKR_API_SECRET") |
60 | 110 | opt = parser.parse_args() |
61 | 111 |
|
62 | | - print(f"nargs {opt.search}") |
63 | | - help_url = "https://www.flickr.com/services/apps/create/apply" |
64 | | - assert key and secret, f"Flickr API key required in flickr_scraper.py L11-12. To apply visit {help_url}" |
65 | | - |
66 | | - for search in opt.search: |
67 | | - get_urls(search=search, n=opt.n, download=opt.download) |
| 112 | + try: |
| 113 | + for search in opt.search: |
| 114 | + get_urls( |
| 115 | + search=search, |
| 116 | + n=opt.n, |
| 117 | + download=opt.download, |
| 118 | + api_key=opt.key, |
| 119 | + api_secret=opt.secret, |
| 120 | + page=opt.page, |
| 121 | + ) |
| 122 | + except (RuntimeError, ValueError) as e: |
| 123 | + parser.exit(1, f"{e}\n") |
0 commit comments