【Lecture 3.2】 从网络上请求信息

24.3 URL剖析

浏览器或其他程序使用 URL 来指定要连接的服务器和要请求的页面。URL的整体结构为:

<scheme>://<host>:<port>/<path>

The host will usually be a domain name, like si.umich.edu or github.com or google.com

当URL指定域名时,计算机程序要做的第一件事就是**查找(Lookups)**域名以找到32位IP地址。示例,现在github.com 的IP地址为192.30.252.130。 例如,如果 github 将其服务器移至其他位置或与其他Internet提供商签约,则可能会发生变化。使用称为域名系统(DNS)的简称。 从域名到IP地址的映射更改可能需要一段时间才能传播:如果github.com宣布与其域相关联的新IP地址,则某些计算机可能需要长达24小时才能将github.com转换为 新的IP地址。

或者,host 可以直接是IP地址。 这种情况不太常见,因为IP地址更难记,而且即使远程服务器保留其域名但移至其他IP地址,包含域名的URL也将继续起作用。

The :port is optional. If it is omitted, the default port number is 80.

port是可选的,如果省略,则默认端口号为80。该端口号在接收端用于确定哪个计算机程序应获取已接收的数据。 在本课程中,我们可能不会遇到任何包含 : 和端口号的URL。

The /path is also optional. It specifies something about which page, or more generally which contents, are being requested.

示例

For example, consider the url https://github.com/presnick/runestone:

  • https:// says to use the secure http protocol
  • github.com says to connect to the server at github.com, which currently maps to the IP address 192.30.252.130. The connection will be made on the default port, which is 443 for https.
  • /presnick/runestone says to ask the remote server for the page presnick/runestone. It is up to the remote server to decide how to map that to the contents of a file it has access to, or to some content that it generates on the fly.

The url http://blueserver.com/path?k=val is another example that we can consider.

The path here a bit different from https://github.com/presnick/runestone because it includes what are called “query parameters”, the information after the ?.

这种URL结构,其中有一个“query parameters”参数。在slash后是路径path,然后是问号和键等于值对,这是一种非常常见的URL模式,我们将在以后的课程中使用该模式来编写从程序中获取数据的程序。

image-20200617194504271

24.2. The Internet: Behind the Scenes: Router

Internet 是一种传输机制,可让任何连接的设备与任何其他连接的设备进行通信。

  1. 每个设备都有一个全球唯一的IP地址,该地址是 32 位数字。 通常,IP地址表示为四个十进制数字的序列,每个数字都在 (0-255) 范围内。 例如,当我刚才检查笔记本电脑的IP地址时,它是141.211.203.248。 以141.211开头的任何IP地址都用于密歇根大学的设备。

  2. 数据被切成合理大小的数据包(最大65,535字节,但通常要小得多)。

  3. 每个数据包都有一个包含目标IP地址标头header

  4. Each packet is routed independently, 从一个计算设备传递到另一个计算设备,直到到达目的地。 进行数据包转发的计算设备称为路由器 routers。 每个路由器都保留一个地址表 address table,该地址表show:当它收到某个目标地址的数据包时,应将该数据包传递给哪个邻居。

    路由器之间一直在互相交谈,传递有关如何更新路由表的信息。 该系统旨在抵抗任何局部损坏。 如果某些路由器停止工作,则其余的路由器会互相通信

  5. 在目标位置,数据包被重组为原始数据消息。

image-20200617194526949

24.4. The HTTP Protocol 协议

HTTP 是用于指定 Web 浏览器或其他程序与 Web 服务器通信的协议

**Step 1: the client makes a request to the server. ** 客户端向服务器请求

  1. 如果请求仅涉及获取数据,则客户端发送 GET 形式的消息,其中 是URL的路径部分
  2. 如果请求涉及发送一些数据(例如文件上传或某些身份验证信息),则该消息以 POST 开头
  3. 无论哪种情况,客户端都会发送一些HTTP标头。 这些包括
    • The type of client program. This allows the server to send back different things to small mobile devices than desktop browsers (a “responsive” website)
    • Any cookies that the server previously asked the client to hold onto. This allows the server to continue previous interactions, rather than treating every request as stand-alone. It also allows ad networks to place personalized ads.
  4. 在 HTTP 标头之后,对于 POST 类型的通信,有一些数据(请求的主体)。

image-20200617194552593

image-20200617194610140

**Step 2: the server responds to the client. ** 响应

服务器首先发送回一些HTTP标头。 这些包括:

  1. a response code indicating whether the server thinks it has fulfilled the request or not.
  2. 它要发回的内容类型的描述(例如,当它发送html格式的文本时为text / html)。
  3. 它希望客户端保留的任何cookie,并在下次与服务器通信时发回

标头之后是内容。 如果您在浏览器中右击“查看源代码”,就会看到这些内容。

image-20200617194628277

24.5. Using REST APIs

REST stands for REpresentational State Transfer它最初具有更抽象的含义,但已成为website的一种功能的简写,类似于python函数,将某些参数用作输入值,并以长文本字符串的形式产生输出

REST API指定了外部程序如何向网站发出 HTTP requests ,以请求执行一些计算,并将数据/字符串作为输出返回。当一个网站被设计成接受由其他计算机程序产生的 requests ,并产生供其他程序使用的 outputs 时,它有时被称为 a web service,而不是产生供人们在网络浏览器中使用的 a web site

We will examine a common pattern used in REST APIs, where there is a base URL that defines an “endpoint”, and then additional information is appended to the URL as query parameters, and the response comes back not as HTML but as a format called JSON.

24.5.1. URL Structure for REST APIs

本章介绍一种特别常见且特别简单的格式,其中请求信息直接在URL中进行编码。这很方便,因为如果出现问题,我们可以通过将URL复制到浏览器中进行调试,看看当它尝试访问该URL时会发生什么。

URL以这种格式具有标准结构:a common pattern

  1. the base URL
  2. a ? character
  3. one or more key-value pairs, formatted as key=value pairs 并且使用 & 符号分隔.

For example, consider the URL https://itunes.apple.com/search?term=Ann+Arbor&entity=podcast.

  • the base URL: https://itunes.apple.com/search

  • a ? character

  • key=value pairs. In this case, there are two pairs. The keys are term and entity. An & separates the two pairs.

    image-20200617194646373

所有这些部分串联在一起形成完整的URL。

image-20200617194739459

24.5.2. Encoding URL Parameters

地址编码的理解

Here’s another URL that has a similar format.

https://www.google.com/search?q=%22violins+and+guitars%22&tbm=isch.

这是在谷歌上搜索匹配字符串 “violins and guitars” 的图像的url。它实际上并不基于REST API,因为返回的内容应该显示在浏览器中。但是,URL具有与我们上面探讨的结构相同的结构,并引入了 “Encoding ” URL参数的思想,我们把它分解来看:

  1. The base URL is https://www.google.com/search

  2. ?,term是 q

  3. Two key=value parameters, separated by &

    • q=%22violins+and+guitars%22 says that the query to search for is “violins and guitars”.
    • tbm=isch says to go to the tab for image search

Now why is "violins and guitars" represented in the URL as %22violins+and+guitars%22?

答案是有些字符在网址中是不安全的。例如,不允许网址路径包含双引号字符。它也不能包含 :/ 或 空格。每当我们想要在一个网址中包含这些字符中的一个时,我们必须用其他字符对它们进行 Encoding

  1. 空格 被编码为 +

  2. "双引号 被编码为%22

  3. : 冒号 将被编码为%3A。等等。

因为所有关于请求的信息都直接编码在URL中。调试REST API请求相对容易,因为您总是可以像我一样在单独的浏览器窗口中打开URL,并立即查看站点返回的内容。 You might find that, that comes in handy for debugging

24.6. Fetching a page 读取一个页面

浏览器只是一个计算机程序,可获取内容并以一种不错的方式显示它们。

24.6.1. 使用 request.get在 python中 fetch

In Python, there’s a module available, called requests. You can use the get function in the requests module to fetch the contents of a page.

And our request module will only work for fetching from some sites. In particular, you can’t use it to fetch regular html pages like umich.edu.

例子

import requests
import json

page = requests.get("https://api.datamuse.com/words?rel_rhy=funny")
print(type(page))
# 返回的数据类型是 request模块下的 Response 对象,实例化给变量page
# <class 'requests.Response'>


print(page.text[:150]) # print the first 150 characters
# [{"word":"money","score":4415,"numSyllables":2},{"word":"honey","score":1211,"numSyllables":2},{"word":"sunny","score":718,"numSyllables":2},{"word":"


print(page.url) # print the url that was fetched
# https://api.datamuse.com/words?rel_rhy=funny
print("------")

# Response 类的方法
x = page.json() # turn page.text into a python object/json对象 list 或者 dictionary

print(type(x))
# <class 'list'>

print("---first item in the list---")
print(x[0])
# {'word': 'money', 'score': 4415, 'numSyllables': 2}

print("---the whole list, pretty printed---")
print(json.dumps(x, indent=2)) # pretty print the results
# 将全文 dumps输出

解析:

  1. 函数 page.json() : This is just a shorthand for saying json.loads(page.text).

24.6.2. More Details of Response objects

关于响应的对象,requests.get 返回的是一个类 requst.Responsepage 是 实例化这个类的变量名

Once we run requests.get, a python object is returned, It’s an instance of a class called Response that is defined in the requests module.

每一个类的实例都有一些属性,对于相同的属性,不同的实例有不同的值。对于这些实例,还可以调用某些类中定义的方法,格式是 <instance>.<methode()> .

Each instance of the class has some attributes; different instances have different values for the same attribute. All instances can also invoke certain methods that are defined for the class.

怎样创建一个类的实例呢?

page = requests.get("https://api.datamuse.com/words?rel_rhy=funny")
<class 'requests.Response'> # a class called Response that is defined in the requests module.
# page 是 一个instance

本文介绍 返回的 instance/Response类下的两个属性和一个方法

  1. The .text attribute. 它包含文件的内容或可从url获得的其他信息(有时还会显示错误消息)。
  2. The .url attribute. 稍后我们将看到 request.get 使用第二个可选参数,该参数用于将一些字符添加基本url(第一个参数的)的末尾。 .url 属性显示从输入参数生成的完整URL。 它对于调试目的很有帮助; 您可以打印出URL,将其粘贴到浏览器中,然后确切地看到返回的内容。
  3. The .json() method. 通过将.text属性的内容传递给jsons.loads函数,将文本转换为python列表或字典。

调用属性没有括号

【拓展属性】

  1. The .status_code attribute.

    • 当服务器认为正在发回所请求的内容时,它将发送代码200。
    • 当所请求的页面不存在时,它会发回代码404,有时也称为“找不到文件”
    • 页面移至其他位置后,它将发送回代码301和客户端应从中检索的其他URL。 在请求模块的完整实现中,get函数是如此智能,以至于当它获得301时,它会查看并获取新的url。 例如,github使用https将所有请求重定向到使用https(安全的HTTP协议)的相应页面。 因此,当我们要求提供http://github.com/presnick/runestone时,github会发回301代码和URL https://github.com/presnick/runestone。 然后,requests.get函数将获取另一个URL。 它报告状态为200和更新的URL。 我们必须做进一步的查询才能发现发生了重定向(请参阅下文)
  2. The .headers attribute

    .headers属性的值是由键和值组成的字典。 要找出所有标题,您可以运行代码并添加一条语句print(p.headers.keys())。 标头之一是 ‘Content-type’。 一些可能的values 是 text/html; charset-utf-8application/json; charset=utf-8.

  3. The .history attribute 如果有重定向,则包含先前响应的列表。

24.6.3.[编码url] Using requests.get to encode URL parameters

当你想要编码一个url参数时,你没有必要记住所有的特殊字符的替换,request module 完成了这些任务。

requests modul 中的 get 函数采用一个称为 params= 的可选参数,输入为字典。 该词典中的键和值将被自动地编码到 发送请求的URL上。

例如:在下面,基本网址是 https://google.com/search , A dictionary with two parameters is passed.

Thus, the whole url is that base url, plus a question mark, “?”, plus a “q=…” and a “tbm=…” separated by an “&”.

d = {'q': '"violins and guitars"', 'tbm': 'isch'}
results = requests.get("https://google.com/search", params=d)
print(results.url)

换句话说,最终访问的网址是

 https://www.google.com/search?q=%22violins+and+guitars%22&tbm=isch

引号=%22,空格=+,

实际上,因为字典键在python中是无序的,所以最终的URL有时可能具有其他顺序的编码键-值对:

https://www.google.com/search?tbm=isch&q=%22violins+and+guitars%22.

幸运的是,大多数接受此格式的URL参数的网站将以任意顺序接受键/值对。

以下是 url 的更多示例,概述了url的 the base part (将在调用 request.get() 时作为第一个参数)和参数(将作为字典编写并在调用 request 时传递给params= )。

image-20200617194807077

举例

import requests

# 显示的网址 page = requests.get("https://api.datamuse.com/words?rel_rhy=funny")
kval_pairs = {'rel_rhy': 'funny'}
page = requests.get("https://api.datamuse.com/words", params=kval_pairs)
print(page.text[:150]) # print the first 150 characters
print(page.url) # print the url that was fetched

image-20200617194826061

You’ve now seen the basics of the requests module. You pass in the URL, you get back a response object.With that response object, you can ask for its .text attribute or you can call a <obj.json()> method to have the text contents turned into a Python list or dictionary.

24.8. 弄清楚如何使用REST API

假设您已经了解了API的存在,并且想弄清楚如何使用它。 您需要回答五个问题。

  1. What is the baseurl?
  2. What keys should you provide in the dictionary you pass for the 【params parameter】?
  3. What values should you provide associated with those keys?
  4. 您是否需要验证自己是否是API的许可用户,如果需要,怎么做
  5. 将提供的数据的结构和含义是什么?

这些问题的答案总是取决于运行服务器的服务提供商做出的设计选择。因此,他们提供的官方文件通常是最有帮助的。

24.8.1. Example: the datamuse API

我们现在以 datamuse API为例。 单击该链接以打开提供文档的网页。 您已经看到了完整URL的示例,https://api.datamuse.com/words?rel_rhy=funny。 让我们浏览文档以回答这五个问题。

首先,在标题为“What is it good for?”的部分中。 有一个header为“…use https://api.datamuse.com…”的列。 这specifies 了URL的第一部分base url:“ https://api.datamuse.com/”。 但是,所有示例 after the / and before the ? 还包含一些其他字符:either words or sug. 。这些称为 endpoints,因此,baseurl 将包含两个 endpoints之一

https://api.datamuse.com/words
https://api.datamuse.com/sug

于参数字典值的内容的问题二和三的答案,可以在描述特定端点(两个端点对应不同的)的文档部分找到。

例如,查看 “ words” endpoints 的文档:整个请求将返回一些单词,使用参数来限制搜索的单词范围

return some words, params specify constraints that restrict the search。

如果 URL 包含 ml = funny,则将返回的所有单词将 “have a meaning related to” to the word funny.。

如果网址中包含 rel_cns = book,则 have “Consonant(辅音) match” to “book”

与这些键关联的值将是单词,例如 book 和 funny。

许多API提供程序都要求您事先注册才能使用API,然后对每个请求进行身份验证。 这样,他们可以收费或以某种方式限制使用。 身份验证的一种流行形式是拥有一个个人“ api_key”,您可以将其作为URL中的“键=值”对之一进行传递。 例如,flickr API要求这样做,我们将在本章后面看到。 某些服务,例如Facebook和Twitter,要求使用甚至更复杂,更安全的身份验证形式,其中使用凭据对请求进行密码签名。 我们将不涉及更复杂的身份验证的使用,因为它很难调试。

最后,、the datamuse documentation provides a section “Interpreting the results” ,解释了API将返回什么类型的数据。在这种情况下,结构非常简单,它是一个JSON格式的字典列表,其中每个字典提供一个满足查询中硬约束的单词,以及一个表示软约束匹配程度的分数。下面有例子

24.8.2. 定义一个函数来重复调用

假设您正在编写一个计算机程序,该程序将自动将文本段落翻译成具有意思不变但具有更多韵律的段落。 您可能需要反复访问 datamuse API,并传递与键rel_rhy相关的不同值(passing different values associated with the key rel_rhy) 让我们做一个python函数来做到这一点。 You can think of it as a wrapper for the call to 【requests.get】.

import requests

def get_rhymes(word):
    baseurl = 'https://api.datamuse.com/words'
    # Set up an empty dictionary for query parameters
    params_diction = {}
    # 输入的单词组成键值对 rel_rhy: <word> ,赋值给 param=,(多个参数也能自动处理)
    params_dictnion['rel_rhy'] = word
    params_diction['max'] = 3
    # 请求网站 返回一个 respond 类的实例/对象 <class 'requests.Response'>
    resp = requests.get(baseurl, params=params_diction)
    
    # 使用类的方法,返回json对象(列表)
    word_ds = resp.json()
    # 遍历这个列表里的元素(字典),将键为‘word’的值返回出来
    return [d['word'] for d in word_ds]
    # 或者返回这个json对象内容,注当有两个return,后面的不起作用
    # return resp.json()

print(get_rhymes("funny"))
---
# 1.['money', 'honey', 'sunny']
# 2. [{'word': 'money', 'score': 4415, 'numSyllables': 2}, {'word': 'honey', 'score': 1211, 'numSyllables': 2}, {'word': 'sunny', 'score': 718, 'numSyllables': 2}]

这是对阅读REST API文档的介绍,也是对创建一个函数来重复对单个API发出类似类型的请求的介绍。

24.9. Debugging calls to requests.get()

In a full python environment, you will not always get a Response object back from a call to requests.get.

可能会出错的第一件事是,在调用 request.get(dest_url) 时收到runtime错误。 在这种情况下,出错的可能性有两种。

  1. One possibility is that the value provided for the params parameter is not a valid dictionary or doesn’t have key-value pairs that can be converted into text strings suitable for putting into a URL.

    For example, if you execute requests.get("http://github.com", params = [0,1]), [0,1] is a list rather than a dictionary and the python interpreter generates the error, TypeError: 'int' object is not iterable.

  2. The second possibility is that the variable dest_url is either not bound to a string, or is bound to a string that isn’t a valid URL.

    For example, it might be bound to the string "http://foo.bar/bat". foo.bar is not a valid domain name that can be resolved to an ip address, so there’s no server to contact. That will yield an error of type requests.exceptions.ConnectionError.

不幸的是,如果对 request.get 的调用产生错误,则不会返回 Response 对象,因此您将需要其他方法来查看产生了什么URL。 下面定义的(测试)函数采用与 request.get 相同的参数,并以字符串形式返回完整的URL用以检查是不是url错了,without trying to fetch it.

import requests
def requestURL(baseurl, params = {}):
    # This function accepts a URL path and a params diction as inputs.
    # It calls requests.get() with those inputs,
    # and returns the full URL of the data you want to get.
    req = requests.Request(method = 'GET', url = baseurl, params = params)
    prepped = req.prepare()
    return prepped.url

print(requestURL(some_base_url, some_params_dictionary))

假设 requestURL() 返回一个URL,将您从 params dictionary 打印输出中看到的内容与您打印输出的URL中看到的内容进行匹配。如果您有一个的网址示例 of a URL from the API documentation,请查看if the structure of your URL与那里的相匹配。也许您拼错了一个API参数名,或者拼错了 base url.

如果 requests.get() 执行时没有生成 a runtime error,,您仍然没有完成 error checking。没有错误意味着你的计算机设法连接到 some web server and get some kind of response,,但这并不意味着它得到了你希望得到的数据。

幸运的是,requests.get() 返回的响应对象具有.url属性,这将帮助您进行调试。 在程序开发过程中,最好将程序打印出来。 这比调用 requestURL() 函数方便,但仅当 request.get() 成功返回了Response对象时才有用,否则需要使用检查函数查看url。

In other cases, it’s just obviously the wrong data. Print out the first couple hundred characters of the response text to see if it makes sense.

import requests
dest_url = <some expr>
d = <some dictionary>
resp = requests.get(dest_url, params = d)
print(resp.url)
print(resp.text[:200])

24.7. [关于对响应内容的缓存] Caching Response Content

如果您从REST API获得复杂的数据,您可能需要多次尝试来编写和调试代码,以您想要的方式处理这些数据。(请参见嵌套数据一章。)出于多种原因,最好不要每次运行程序时都联系REST API来重新请求相同的数据。

I’m going to introduce you to a programming pattern called caching. it works like:

  1. 在执行一些 expensive 的操作(例如,调用request.get以从REST API获取数据)之前,请检查是否已经saved (“cached”)了发出该请求将得到的结果。
  2. 如果是,读取这些存档
  3. 如果不是,执行expensive的操作,然后将结果(例如复杂数据)保存(“缓存”)在您的缓存中,这样下次就不必再执行了。

24.7.1. The requests_with_caching module

在本书中,我们提供了一个特殊的模块,称为 request_with_caching,使用此模块的方法如下

  1. 调用这个模块 import requests_with_caching
  2. Instead of invoking requests.get(), you’ll invoke requests_with_caching.get().

您将获得与获得的响应对象完全相同的响应对象。 但是,您还将在输出窗口中获得以下三种诊断消息之一的打印输出:

  • found in permanent cache
  • found in page-specific cache
  • new; adding to cache

【略过,需要再看】

第三周的课,先登录再看

https://fopp.umsi.education/books/published/fopp/InternetAPIs/cachingResponses.html

This get method in requests.with.caching is going to return exactly the same result, a response object, just like if I were to call request.get.
在 3 分 17 秒处开始记录3:17
But the way it works is that, it’s going to first look in the cache and see if it can find the result there. If so, it’ll give us the results from the cache. If it can’t find it in the cache, it calls the real request.get and it returns that but it also saves the result in the cache so that the next time, we’ll get the result from the cache.

Welcome back. At this point, you should be able to read API documentation in order to formulate and debug invocations of the request.get function. You should be able to understand and appreciate the requests with caching module.

欢迎回来。此时,您应该能够阅读API文档,以便制定和调试请求的调用。您应该能够理解和欣赏缓存模块的请求。


24.11. [理解iTunes API文档] Searching for Media on iTunes

The fully-qualified URL must have the following format:

https://itunes.apple.com/search?<parameterkeyvalue>

键值对的格式

<key1=value1&key2=value2&key3=value3>

下表定义了可以指定以在 iTunes Store 或 Apple Books Store 中搜索内容的参数键和值:

image-20200617194858601

Earlier we showed a possible query for podcasts about Ann Arbor.

从上面的格式我们知道 base url 是:

https://itunes.apple.com/search

要确定哪些参数是必需的 Required ,我们可以查看文档中的表格以了解iTuens API服务器将理解哪些参数键和值

  1. term 是必填参数,没有默认值,因此我们必须提供该参数。

    image-20200617194923404

    注意必须是字符串,不必使用加号

  2. 我们还想确保我们正在搜索 podcast

    image-20200617194944761

    Note that both entity and media are parameters we can use for this task. Entity can be more specific though, so you may need to use that in rather than media!

现在,我们的代码可以向 iTunes API 发出请求了。我们使用 request _ with _ caching 模块,并在活动代码中提供对查询的缓存响应。您可以尝试运行不同的查询,但是如果您由于某种原因无法访问itunes webserver,它可能无法工作。

import requests_with_caching
import json

parameters = {"term": "Ann Arbor", "entity": "podcast"}
iTunes_response = requests_with_caching.get("https://itunes.apple.com/search", params = parameters,permanent_cache_file="itunes_cache.txt")

py_data = json.loads(iTunes_response.text)

found in permanent_cache

image-20200617195006173

掌握了这一结果之后,您将必须执行之前称为 理解, 提取, 重复,的过程。 例如,要打印出所有返回的podcast 的名称,可以运行以下代码。

import requests_with_caching
import json

parameters = {"term": "Ann Arbor", "entity": "podcast"}
iTunes_response = requests_with_caching.get("https://itunes.apple.com/search", params = parameters, permanent_cache_file="itunes_cache.txt")

py_data = json.loads(iTunes_response.text)
for r in py_data['results']:
    print(r['trackName'])

输出

found in permanent_cache
Ann Arbor Stories | Ann Arbor District Library
Vineyard Church of Ann Arbor Sermon Podcast
Harvest Mission Community Church (Ann Arbor) Sermons
Grace Bible Church Ann Arbor
Grace Ann Arbor Church
Sermons from First Pres
Antioch Ann Arbor
Blue Ocean Faith Ann Arbor Sunday Sermons
It’s Hot In Here
Radiant Church - Ann Arbor: Sermons
Calvary Sunday Messages
Fellow Youths | Ann Arbor District Library
Behind The Marquee | Ann Arbor District Library
Ann Arbor SPARK CEO Podcast
Bethel AME - Ann Arbor
Sermons – NewLifeA2.org
Ann Arbor West Side UMC Sermons
Martin Bandyke Under Covers | Ann Arbor District Library
Grace Ann Arbor Podcast
Mosaic Church of Ann Arbor
A2 City News
Presenting Alfred Hitchcock Presents | Ann Arbor District Library
Redeemer Ann Arbor
Zion Lutheran Ann Arbor
Living Writers
2|42 Community Church - Ann Arbor

24.12. [使用flicker的api查找标签图片] Searching for tags on flickr

考虑另一项web服务,图像共享网站 flickr。人们通过网络浏览器或移动应用与网站互动。上传照片的用户可以为每张照片指定一个或多个简短的“标签”。标签是像 “sunset”“spaghetti” 这样描述照片的词。它们可用于搜索或查找相关照片。API 官方文档 You can explore the official documentation about the site.

端点 search https://www.flickr.com/services/api/flickr.photos.search.html

该API比您在前面的子章中检查过的API更复杂,因为它需要身份验证,并且因为它需要您指定要以JSON格式返回结果。 它还提供了学习文档中使用API的更多良好实践。 另外,检查照片数据非常有趣。

在flickr上进行照片search的URL的结构为:

  1. base URL is https://api.flickr.com/services/rest/
  2. ?
  3. key=value pairs, separated by &s:
    • One pair is method=flickr.photos.search. This says to do a photo search, rather than one of the many other operations that the API allows. Don’t be confused by the word “method” here– it is not a python method. That’s just the name flickr uses to distinguish among the different operations a client application can request.
    • format=json. This says to return results in JSON format.
    • per_page=5. This says to return 5 results at a time.
    • tags=mountains,river. This says to return things that are tagged with “mountains” and “river”.
    • tag_mode=all. This says to return things that are tagged with both mountains and river.
    • media=photos. This says to return photos
    • api_key=.... Flickr only lets authorized applications access the API. Each request must include a secret code as a value associated with api_key. Anyone can get a key. See the documentation for how to get one. We recommend that you get one so that you can test out the sample code in this chapter creatively. We have included some cached responses, and they are accessible even without an API key.
    • nojsoncallback=1. This says to return the raw JSON result without a function wrapper around the JSON response.

让我们把所有东西放在一起,制作一个小的检索工具来查找 包含特定标签tag的flickr图像。当然,在浏览器中,你可以使用 flickr 的搜索工具。但是,通过这个应用编程接口做这件事打开了其他的可能性,你可以探索常规flickr网站上没有提供的功能。

Searching for “mountains” and “rivers” usually produces beautiful images that are “safe for work”,所以下面的例子就是这样搜索的。我们已经在我们的代码窗口中缓存了特定搜索的响应。即使您没有提供有效的flickr api_key,这也允许代码运行。We’ve also checked to make sure that the five returned images are indeed safe for work。如果您在浏览器之外运行此代码,或者如果您执行其他搜索,您将需要提供一个有效 valid 的 flickr api_key。

# import statements
import requests_with_caching
import json
# import webbrowser

# apply for a flickr authentication key at http://www.flickr.com/services/apps/create/apply/?
# paste the key (not the secret) as the value of the variable flickr_key
flickr_key = 'yourkeyhere'

# 输入想要搜索的 标签(字符串格式)
def get_flickr_data(tags_string):
    baseurl = "https://api.flickr.com/services/rest/"
    
    params_diction = {}
    params_diction["api_key"] = flickr_key # from the above global variable
    params_diction["tags"] = tags_string # must be a comma separated string to work correctly
    params_diction["tag_mode"] = "all"
    params_diction["method"] = "flickr.photos.search"
    params_diction["per_page"] = 5
    params_diction["media"] = "photos"
    # 要求返回 json 对象
    params_diction["format"] = "json"
    params_diction["nojsoncallback"] = 1
    # 命令
    flickr_resp = requests_with_caching.get(baseurl, params = params_diction, permanent_cache_file="flickr_cache.txt")
    # Useful for debugging: print the url! Uncomment the below line to do so.
    # print(flickr_resp.url) # Paste the result into the browser to check it out...
    
    # The response sent back is loaded into a python dictionary using json.loads().
    return flickr_resp.json()

# 测试搜索工具,注意多个标签使用 comma 分隔,返回的是 json(字典)
result_river_mts = get_flickr_data("river,mountains")

# 使用 理解, 提取, 重复过程查看json的内容...

# Some code to open up a few photos that are tagged with the mountains and river tags...

photos = result_river_mts['photos']['photo']
for photo in photos:
    owner = photo['owner']
    photo_id = photo['id']
    url = 'https://www.flickr.com/photos/{}/{}'.format(owner, photo_id)
    print(url)
    # webbrowser.open(url)
found in permanent_cache
https://api.flickr.com/services/rest/?api_key=yourkeyhere&tags=river%2Cmountains&tag_mode=all&method=flickr.photos.search&per_page=5&media=photos&format=json&nojsoncallback=1
https://www.flickr.com/photos/45934971@N07/44858440865
https://www.flickr.com/photos/145056248@N07/43953569330
https://www.flickr.com/photos/145056248@N07/43953448610
https://www.flickr.com/photos/131540074@N08/44857602655
https://www.flickr.com/photos/145056248@N07/44857423045

The response sent back by flickr is loaded into a python dictionary using json.loads().

最后,我们遍历返回的照片 字典列表,提取两个字段:ownerphoto_id。 这些用于创建新的URL,其格式为flickr期望用于显示包含单个图像的网页。 在我们的环境中,您必须将这些URL复制到新标签页中才能查看照片。 在完整的python环境中,您可以取消注释导入【webbrowser模块】 的代码行和调用webbrowser.open() 函数的代码行。 如果一切顺利,则应该打开五个浏览器选项卡,每个选项卡上都有一些flickr用户用 “mountains” and “rivers” 标记的图片。

image-20200617195028844

在调用 api 时一定有一些键值对是强制的 required

如果

Here’s an example response. You’ll notice that it doesn’t look much like our favorite JSON format. This is actually XML format. So by default, Flickr returns data not in the JSON format, but in XML. It’s possible to parse XML in Python.

编写一个函数,输入时 tag_string, 加入到键值对的字典中,再调用api

So, I’m going to invoke that on line 26 and then, once I get the results back as a Python object, in this case, it’s going to be a dictionary, I have to go through my understand, extract, repeat process to figure out how to pull out the data that I want.

24.13. 非英语字符的Unicode

有时,您可能需要处理包含不属于标准英语字母表的字符的文本,如é、ф、ф或。如果您使用REST APIs从Twitter、Facebook或flickr等社交媒体网站获取用户贡献的内容,这种情况尤其可能发生。

Python的字符串采用unicode编码,允许字符来自更大的字母表,包括在中文、日文和韩文字母表中使用的超过75,000个表意字符。在Python中,一切都很好,比如切片、追加和连接字符串以及使用。find()或in运算符。

只有当您想要将字符串input到Python中,或者将它们打印到输出窗口中,或者将它们write to 文件中时,事情才会变得棘手。

对于输出,您的终端(输出)窗口通常设置为仅显示受限语言集的字符(可能只有英语)。如果您对包含其他字符的字符串发出 print 语句,它可能无法在您的终端窗口中正确显示。事实上,您可能会收到一条错误消息。我们将在本页稍后提供一个变通方法。

如果要将 unicode 文本存储在文件中,则必须选择 “encoding”。 这类似于URL字符串中特殊字符的编码,但不完全相同。In a file, each unicode character has to be encoded as one or more “bytes” for storage in a file 在文件中,每个unicode字符必须编码为一个或多个“字节”才能存储在文件中。 到目前为止,我们一直在避免有关数据编码data encodings的低级细节,但是对位和字节bits and bytes有所了解将有助于理解这一点。

A bit is a BInary digiT. It is a single value restricted to two (binary) possibilities as 0 or 1. Computers store bits as electrical charges (high or low voltage) or as magnetic polarities, or some other way that we need not be concerned about. A sequence of eight 0-1 bits is called a byte. For example: 01001000. There are 2^^8=256 distinct eight-bit bytes. If we only had 256 possible letters in our alphabet, we could simply encode each letter as one of the available bytes. 当我们只使用普通的python字符串(仅使用ASCII字母(英语,再加上一些特殊字符))时,编码就变得如此简单,如此简单,以至于我们以前不必考虑它。

当可能的字符数为75,000时,因为只有256个不同的字节(八位序列),所以不能全部用一个字节进行编码。 有许多可能的编码。 使用 REST API,您最有可能遇到的一个称为UTF-8。 单个unicode字符映射到最多四个字节的序列。

If you read in a UTF-8 encoded text, and get the contents using .read() or .readlines(), you will need to “decode” the contents in order to turn it into a proper unicode string that you can read and use.

幸运的是,请求模块通常会自动为我们处理此问题。When we fetch a webpage that is in json format, the webpage will have a header called ‘content-type’ that will say something like application/json; charset=utf8. 如果它以这种方式指定utf8字符集,请求模块将自动将内容解码成unicode: requests.get(“该网页”)。文本将产生一个字符串,每个1到4字节的序列都转换成一个字符。

如果由于某种原因,您得到了utf编码的json格式的文本,但是请求模块没有神奇地为您解码,那么json.loads()函数调用可以为您完成解码。loads()采用可选参数编码。它的默认值是“utf-8”,所以您不需要指定它,除非您认为您收到的文本不是“utf-8”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值