1 00:00:07,880 --> 00:00:12,780 Welcome back. Let's try querying another rest API, 2 00:00:12,780 --> 00:00:14,190 this one from Flickr, 3 00:00:14,190 --> 00:00:16,875 one of the first popular photo sharing sites. 4 00:00:16,875 --> 00:00:19,110 It's still used by a lot of excellent photographers. 5 00:00:19,110 --> 00:00:20,940 It's run by Yahoo now. 6 00:00:20,940 --> 00:00:26,055 Here's the overview, documentation page for the Flickr API. 7 00:00:26,055 --> 00:00:28,455 We're going to use the photo searching endpoint. 8 00:00:28,455 --> 00:00:30,180 There are lots of other things you can do with 9 00:00:30,180 --> 00:00:33,855 the Flickr rest API including posting photos and lots of other things. 10 00:00:33,855 --> 00:00:37,590 We'll just look at searching because you can do that even if you only 11 00:00:37,590 --> 00:00:39,150 like looking at photographs and you don't have 12 00:00:39,150 --> 00:00:41,775 a Flickr account where you've uploaded photos. 13 00:00:41,775 --> 00:00:44,685 So first, at the bottom of this page, 14 00:00:44,685 --> 00:00:48,035 notice that they give what they call an endpoint. 15 00:00:48,035 --> 00:00:51,950 That's what we will call the base URL. 16 00:00:51,950 --> 00:00:54,950 Everything after that is going to have question mark, 17 00:00:54,950 --> 00:00:57,780 key equals value pairs. 18 00:01:01,180 --> 00:01:09,405 The next notice that they have a required parameter called the api_key. 19 00:01:09,405 --> 00:01:17,130 So one of these is going to have to be api_key equals something. 20 00:01:17,240 --> 00:01:25,170 That's a developer specific key that you get from Flickr by signing up for it. 21 00:01:27,220 --> 00:01:30,800 If you access one of our few cached queries, 22 00:01:30,800 --> 00:01:32,705 you actually won't need an api_key. 23 00:01:32,705 --> 00:01:36,875 But if you want to run any other queries that we haven't put into a cache for you, 24 00:01:36,875 --> 00:01:39,815 you will need to get an api_key from Flickr. 25 00:01:39,815 --> 00:01:43,190 A third thing to notice on this page is that they have 26 00:01:43,190 --> 00:01:47,345 another required query parameter called method. 27 00:01:47,345 --> 00:01:51,890 So we're going to have to have something that says method equals something. 28 00:01:51,890 --> 00:01:54,560 In our case, it's going to be something about 29 00:01:54,560 --> 00:01:59,570 search and we'll find that at a more specific documentation page. 30 00:01:59,570 --> 00:02:04,170 So let's look at the documentation page for searching. 31 00:02:07,330 --> 00:02:10,620 I already have it loaded here. 32 00:02:11,420 --> 00:02:19,065 We're going to have to say method equals flickr.photos.search. 33 00:02:19,065 --> 00:02:22,400 There are also a bunch of other keys that we can provide, 34 00:02:22,400 --> 00:02:24,520 what they call arguments. 35 00:02:24,520 --> 00:02:27,205 I'm just going to clear my marking here, 36 00:02:27,205 --> 00:02:34,530 and scroll down to show you that there's a lot of options or arguments, 37 00:02:34,810 --> 00:02:38,070 and it just keeps going. 38 00:02:38,320 --> 00:02:43,625 These are all things that are going to affect what gets retrieved. 39 00:02:43,625 --> 00:02:47,510 So we can ask for only things that 40 00:02:47,510 --> 00:02:51,785 are photos that are taken indoors or only photos that are taken outdoors. 41 00:02:51,785 --> 00:02:53,930 We can provide a latitude and longitude, 42 00:02:53,930 --> 00:03:01,400 and it'll bring us back photos that are within some radius of that location on Earth. 43 00:03:01,400 --> 00:03:03,230 So there's a bunch of them here. 44 00:03:03,230 --> 00:03:05,970 We're not going to use them all in the examples that I show you, 45 00:03:05,970 --> 00:03:08,795 but we can come back to them whenever we need. 46 00:03:08,795 --> 00:03:12,905 It's worth noting that the api_key is required. 47 00:03:12,905 --> 00:03:15,605 I talked about that already. 48 00:03:15,605 --> 00:03:18,144 Then, we're going to search for tags. 49 00:03:18,144 --> 00:03:21,270 So photos, Flickr, the photographer, 50 00:03:21,270 --> 00:03:25,000 when they upload them, they can give them tags like this is indoors, 51 00:03:25,000 --> 00:03:27,830 or this is still life or this has mountains in it, 52 00:03:27,830 --> 00:03:30,215 or they can put a city name, 53 00:03:30,215 --> 00:03:31,760 any tag that they choose. 54 00:03:31,760 --> 00:03:36,650 So we're going to use that to search for photos of mountains and rivers. 55 00:03:36,650 --> 00:03:38,780 Then, there's this tag_mode. 56 00:03:38,780 --> 00:03:45,260 If you provide more than one tag that can either be treated as you want to 57 00:03:45,260 --> 00:03:47,510 search for photos that have one of those tags as 58 00:03:47,510 --> 00:03:51,755 the or combination or that you want to search for photos that have all of those tags, 59 00:03:51,755 --> 00:03:54,030 that's the and combination. 60 00:03:54,790 --> 00:03:57,670 Now, if I scroll all the way down, 61 00:03:57,670 --> 00:04:01,110 we'll see something about what the results look like. 62 00:04:03,200 --> 00:04:05,985 Here's an example response. 63 00:04:05,985 --> 00:04:10,570 You'll notice that it doesn't look much like our favorite JSON format. 64 00:04:10,570 --> 00:04:13,385 This is actually XML format. 65 00:04:13,385 --> 00:04:18,195 So by default, Flickr returns data not in the JSON format, but in XML. 66 00:04:18,195 --> 00:04:21,480 It's possible to parse XML in Python. 67 00:04:21,480 --> 00:04:25,410 It's not that difficult, but we're not going to go into that in this specialization. 68 00:04:25,410 --> 00:04:30,880 Most sites that provide XML also have some way to ask for JSON instead. 69 00:04:30,880 --> 00:04:34,390 Flickr does have weighed asked for JSON instead. 70 00:04:34,390 --> 00:04:37,150 They don't make it that easy to find out how. 71 00:04:37,150 --> 00:04:40,765 I didn't find it anywhere on this page. 72 00:04:40,765 --> 00:04:44,560 I did do another Google search. 73 00:04:47,210 --> 00:04:54,160 If I just search for Flickr API JSON, 74 00:04:54,380 --> 00:04:57,700 the first response that comes up is 75 00:04:57,700 --> 00:05:03,380 a documentation page about how to get things in JSON format. 76 00:05:03,380 --> 00:05:08,115 Now, the trick is, I'm going to show you somewhere here, 77 00:05:08,115 --> 00:05:13,185 and it's right here, send a parameter called format with a value of JSON. 78 00:05:13,185 --> 00:05:19,730 So we're going to have in our URL format equals JSON. 79 00:05:20,130 --> 00:05:23,035 There's one other little tricky thing. 80 00:05:23,035 --> 00:05:25,420 I'm just going to get rid of those markings, 81 00:05:25,420 --> 00:05:27,910 and we'll scroll down to the bottom of the page, 82 00:05:27,910 --> 00:05:32,875 they have a little thing that says something about callback functions. 83 00:05:32,875 --> 00:05:37,390 It turns out we have to send nojsoncallback equals one. 84 00:05:37,390 --> 00:05:40,090 Now, it's a little more technical than we're ready for, 85 00:05:40,090 --> 00:05:41,290 but here's the basic idea. 86 00:05:41,290 --> 00:05:44,545 Without that nojsoncallback equals one, 87 00:05:44,545 --> 00:05:47,380 you would get JSON results that are wrapped 88 00:05:47,380 --> 00:05:52,880 in basically a JavaScript function invocation called the callback. 89 00:05:52,880 --> 00:05:56,845 So that wrapping is part of a standard called JSONP. 90 00:05:56,845 --> 00:06:01,960 In any case, what we need to do is we need to include nojsoncallback 91 00:06:01,960 --> 00:06:08,270 equals one in order to just get pure JSON without any extra characters around it. 92 00:06:08,490 --> 00:06:11,695 So with that basics from the documentation, 93 00:06:11,695 --> 00:06:15,245 let's look at some code for making API requests. 94 00:06:15,245 --> 00:06:17,710 As with the other APIs, 95 00:06:17,710 --> 00:06:19,920 we're going to use requests with caching, 96 00:06:19,920 --> 00:06:24,455 so that we can avoid you having to actually get an api_key from Flickr. 97 00:06:24,455 --> 00:06:29,520 You'll just get data from our cache where we won't check for needing an api_key. 98 00:06:29,870 --> 00:06:33,530 Just like we did for the data muse API, 99 00:06:33,530 --> 00:06:37,175 I've defined a function, get_flickr_data. 100 00:06:37,175 --> 00:06:39,590 We'll pass in as a parameter as 101 00:06:39,590 --> 00:06:43,525 tags_string that's saying what tags we want to search for. 102 00:06:43,525 --> 00:06:46,395 So when I call it on line 26, 103 00:06:46,395 --> 00:06:51,860 I'm going to pass in this comma separated list of tags, river, mountains. 104 00:06:51,860 --> 00:06:55,350 Inside this get_flickr_data function, 105 00:06:55,480 --> 00:07:03,170 you can see that I'm going to get set up to make a call to the request with caching.get. 106 00:07:03,170 --> 00:07:06,065 In order to set up for that, I set my base URL, 107 00:07:06,065 --> 00:07:08,390 and make an empty parameters dictionary, 108 00:07:08,390 --> 00:07:11,130 and then I set a bunch of key value pairs. 109 00:07:11,130 --> 00:07:17,575 I'm going to add API key equals whatever I have for my Flickr_key, 110 00:07:17,575 --> 00:07:20,720 which is just a variable up here. 111 00:07:21,430 --> 00:07:23,615 For the tags key, 112 00:07:23,615 --> 00:07:24,980 I'm going to set it to be river, 113 00:07:24,980 --> 00:07:29,420 mountains or whatever the current value of tags string is, 114 00:07:29,420 --> 00:07:32,240 whatever has been passed in to this function. 115 00:07:32,240 --> 00:07:34,955 For the tag mode, I'm going to say all. 116 00:07:34,955 --> 00:07:39,950 I want photos that have both river and mountains as a tag. 117 00:07:39,950 --> 00:07:43,055 The method is flickr.photos.search. 118 00:07:43,055 --> 00:07:46,310 I only want to get five results back. 119 00:07:46,310 --> 00:07:50,390 I would like only photos not videos and I want 120 00:07:50,390 --> 00:07:54,755 the format to be JSON and no JSON callback equals one. 121 00:07:54,755 --> 00:07:56,540 So, that creates that whole dictionary, 122 00:07:56,540 --> 00:08:02,480 I pass that in to my request.get or requests with caching.get and 123 00:08:02,480 --> 00:08:05,510 I'm telling the request with caching to use 124 00:08:05,510 --> 00:08:09,960 the file called Flickrcache.text as it's caching file. 125 00:08:10,000 --> 00:08:15,725 So, I get a response object back and I'm just going to print the URL from that. 126 00:08:15,725 --> 00:08:20,540 That's the URL that we would have gone to if we didn't find it in the cache, 127 00:08:20,540 --> 00:08:23,000 and then we return a Python object. 128 00:08:23,000 --> 00:08:28,835 We turn the text string into either a list or a dictionary by calling this.JSON method. 129 00:08:28,835 --> 00:08:34,595 So, I'm going to invoke that on line 26 and then, 130 00:08:34,595 --> 00:08:38,405 once I get the results back as a Python object, 131 00:08:38,405 --> 00:08:40,790 in this case, it's going to be a dictionary, 132 00:08:40,790 --> 00:08:43,700 I have to go through my understand, extract, 133 00:08:43,700 --> 00:08:48,050 repeat process to figure out how to pull out the data that I want. 134 00:08:48,050 --> 00:08:53,420 Again, I've already done that so I'm not going to walk you through that process but I've 135 00:08:53,420 --> 00:08:59,270 discovered that the result dictionary has a top-level key called photos. 136 00:08:59,270 --> 00:09:02,330 As the value associated with the photos key, 137 00:09:02,330 --> 00:09:07,625 there's another dictionary, and that dictionary has a key called photo. 138 00:09:07,625 --> 00:09:09,380 I find that a little confusing, 139 00:09:09,380 --> 00:09:12,815 but that's the way the data comes back from Flickr. 140 00:09:12,815 --> 00:09:17,285 The value associated with that photo key is a list. 141 00:09:17,285 --> 00:09:19,265 It's a list of dictionaries, 142 00:09:19,265 --> 00:09:23,030 one dictionary for each photograph. 143 00:09:23,030 --> 00:09:29,720 The dictionary for one photograph has a key called owner and another key called ID. 144 00:09:29,720 --> 00:09:32,840 So, for each of the photo dictionaries that's returned, 145 00:09:32,840 --> 00:09:36,470 I'm going to extract the owner and the photo ID. 146 00:09:36,470 --> 00:09:44,450 I can stick those two values into a URL because I've looked up 147 00:09:44,450 --> 00:09:48,020 what is the format that Flickr uses for making URLs 148 00:09:48,020 --> 00:09:51,845 to actually load a page with a single photo in it, 149 00:09:51,845 --> 00:09:57,860 and I am just substituting owner and photo ID in to make this URL string. 150 00:09:57,860 --> 00:10:04,655 So, if I run this I'm going to get a printout of five different URLs. 151 00:10:04,655 --> 00:10:10,055 You can see the five URLs are here and above it, 152 00:10:10,055 --> 00:10:15,755 we got the URL that we would have fetched if we hadn't found the result in the cache. 153 00:10:15,755 --> 00:10:19,745 If this were a full Python environment, 154 00:10:19,745 --> 00:10:23,299 we would be able to import the web browser module, 155 00:10:23,299 --> 00:10:25,295 and we could call webbrowser.open, 156 00:10:25,295 --> 00:10:30,230 which would make this URL open up automatically in another tab. 157 00:10:30,230 --> 00:10:32,360 I don't have that option available to me, 158 00:10:32,360 --> 00:10:33,800 so I'm just going to, 159 00:10:33,800 --> 00:10:39,090 we printed it out, and I'll go and make a new tab. 160 00:10:39,430 --> 00:10:45,180 We will visit that URL, 161 00:10:46,870 --> 00:10:52,445 and there we have a photo that's tagged with mountains and river. 162 00:10:52,445 --> 00:10:56,490 I can see the river, I'm not quite sure where the mountain is for this one. 163 00:10:57,190 --> 00:11:01,620 We got some more. Here's another one. 164 00:11:06,790 --> 00:11:11,220 I put that URL in instead. 165 00:11:17,440 --> 00:11:21,830 Here's one that does have a river and a mountain. 166 00:11:21,830 --> 00:11:29,720 Now, notice that on line 23 I have the code printing out the URL. 167 00:11:29,720 --> 00:11:34,970 That's the URL that we would have visited if we hadn't found it in the cache. 168 00:11:34,970 --> 00:11:37,620 If I copy this, 169 00:11:39,040 --> 00:11:45,200 I can go to another tab and see what I 170 00:11:45,200 --> 00:11:51,065 would get if I actually went to that URL for the API call. 171 00:11:51,065 --> 00:11:55,280 The answer is I get a message saying, 172 00:11:55,280 --> 00:12:00,245 we failed because it's an invalid API key. 173 00:12:00,245 --> 00:12:04,865 So, I didn't provide a valid API key and so, 174 00:12:04,865 --> 00:12:06,785 I can't actually make that query. 175 00:12:06,785 --> 00:12:09,740 That's one of the advantages of getting the stuff from the cache is 176 00:12:09,740 --> 00:12:13,995 that we're not requiring you to have that API key. 177 00:12:13,995 --> 00:12:19,735 Now, if I were to change this and instead of asking for say, river and mountains, 178 00:12:19,735 --> 00:12:22,820 ask for cows and mountains, 179 00:12:22,960 --> 00:12:26,765 I've tried this before and we tend to get 180 00:12:26,765 --> 00:12:31,550 beautiful pictures from Switzerland and the Pyrenees in Europe. 181 00:12:31,550 --> 00:12:33,020 If I try to do this, 182 00:12:33,020 --> 00:12:36,890 it won't find it in that cache, 183 00:12:36,890 --> 00:12:38,555 and so it will try to fetch it. 184 00:12:38,555 --> 00:12:40,640 So, it's new and adding it to the cache. 185 00:12:40,640 --> 00:12:42,620 Here's what the URL is, 186 00:12:42,620 --> 00:12:47,465 but we get an error, 187 00:12:47,465 --> 00:12:50,820 and the error is on line 30. 188 00:12:51,400 --> 00:12:58,280 When we go to try to extract the photos and photo IDs on line 30, 189 00:12:58,280 --> 00:13:00,770 we're finding that we get a key error. 190 00:13:00,770 --> 00:13:03,200 In the dictionary of results, 191 00:13:03,200 --> 00:13:06,540 we don't have anything called photos. 192 00:13:08,590 --> 00:13:12,920 So, let's see what 193 00:13:12,920 --> 00:13:16,880 the result really was 194 00:13:16,880 --> 00:13:22,650 and it's going to be the same error message that we saw in the browser window. 195 00:13:34,000 --> 00:13:38,555 We get the error message that it was an invalid API key. 196 00:13:38,555 --> 00:13:42,110 So, you can make this work if you sign up for 197 00:13:42,110 --> 00:13:45,995 an API key and put your API key on line eight, 198 00:13:45,995 --> 00:13:49,430 you would be able to make new queries with this code. 199 00:13:49,430 --> 00:13:53,450 We didn't show it here, but there are other operations with Flickr 200 00:13:53,450 --> 00:13:57,035 that require user specific authentication. 201 00:13:57,035 --> 00:13:59,750 If you want to post photos to somebody's account, 202 00:13:59,750 --> 00:14:02,570 you'd need to prove that you have permission from them. 203 00:14:02,570 --> 00:14:05,240 APIs provide features for doing that kind of 204 00:14:05,240 --> 00:14:08,480 user authorization through a protocol called OAuth, 205 00:14:08,480 --> 00:14:11,915 but that's beyond the scope of this specialization. 206 00:14:11,915 --> 00:14:16,130 Once you've authorized, each request would get digitally 207 00:14:16,130 --> 00:14:21,125 signed and Python provides a module for doing that called, requests with OAuth. 208 00:14:21,125 --> 00:14:24,660 Again, it's beyond the scope of this specialization. 209 00:14:24,760 --> 00:14:27,695 The key takeaways from this lesson. 210 00:14:27,695 --> 00:14:31,510 One, sometimes you need to pass a lot of query parameters. 211 00:14:31,510 --> 00:14:35,160 Take a look at lines 13 through 20. 212 00:14:35,160 --> 00:14:39,050 Sometimes there are a lot of choices about what the query parameters are, 213 00:14:39,050 --> 00:14:42,185 remember that long list in the documentation. 214 00:14:42,185 --> 00:14:44,870 Sometimes you have to specify that you want things in 215 00:14:44,870 --> 00:14:47,660 JSON format because that might not be the default. 216 00:14:47,660 --> 00:14:50,960 Sometimes you have to specify other idiosyncratic things 217 00:14:50,960 --> 00:14:55,565 like this no JSON callback equals one query parameter. 218 00:14:55,565 --> 00:14:59,210 Finally, many sites require you to provide 219 00:14:59,210 --> 00:15:05,010 an API key which you get by registering with that site. See you next time.