Skip to content

Latest commit

 

History

History
407 lines (263 loc) · 26.7 KB

File metadata and controls

407 lines (263 loc) · 26.7 KB

七、将基于 Web 的 API 用于数据提取

基于 Web 的 API 允许用户与 Web 上的信息交互。API 以易于使用和维护的格式化模式直接处理数据。一些 API 在向用户提供数据之前还需要用户身份验证。本章将介绍 Python 和一些 web API 的使用,以与可用 API 进行交互并从中提取数据。通常,API 以可交换的文档格式提供数据,如 JSON、CSV 和 XML。

在本章中,我们将介绍以下主题:

  • web API 简介
  • 使用 Python 编程语言访问 web API
  • 通过 web API 处理和提取数据

技术要求

本章要求使用网络浏览器(Google Chrome 或 Mozilla Firefox)。我们将使用以下 Python 库:

  • requests
  • json
  • collections

如果您当前的 Python 设置中不存在这些库,请参阅设置部分中的第 2 章Python 和 Web–使用 urllib 和请求,了解如何下载它们。

本章的代码文件可在本书的 GitHub 存储库中找到:https://github.com/PacktPublishing/Hands-On-Web-Scraping-with-Python/tree/master/Chapter07

web API 简介

基于 web 的应用程序编程信息基于 web 的 API是网站提供的一个接口,用于返回接收到的请求的信息。web API(或 API)实际上是一种 web 服务,由网站向用户或第三方 web 应用程序或自动脚本提供,以共享和交换信息。

通常,这是一个通过 web 浏览器处理的用户界面UI,用于从向网站或 web 服务器发出的请求中检索特定信息。拥有大量任何类型信息的网站都可以为其用户提供 web API,这有助于信息共享。

API in the field of software applications is known for its set of facilities, such as methods and libraries, which can be used to further enhance, build, or develop applications. This is also known as a developer API.

Web API 不依赖于任何编程语言。它们可以轻松访问原始格式的基于 web 的信息,通常返回 JSON、XML 或 CSV 格式的结构化响应

它们按照 HTTP 原则(请求和响应周期)工作,但只接受一组预定义的请求和参数格式来生成响应。就安全性而言,许多 API 还提供身份验证工具,如向网站发出请求所需的 API 密钥。

休息和肥皂

API 是由基于软件架构或原理的 web 服务器提供的服务。简单对象访问协议SOAP)和表示状态转移REST****是访问 web 服务的方法。REST 是一种体系结构,SOAP 是一种基于 web 标准的协议。我们将在接下来的章节中讨论 RESTAPI。

**# 休息

其余(https://www.ics.uci.edu/~fielding/pubs/demission/rest_arch_style.htm是一种基于一组定义和寻址网络原则的软件体系结构。REST 是一种软件架构,而不是一套标准。REST 使用标准 HTTP 协议和GETPOSTPUTDELETE等方法提供服务。它是无状态的、多层的,并且还支持缓存

Web API 通常被归类为 RESTful Web 服务;它们为用户和其他通信资源提供接口。RESTfulWeb 服务(RESTAPI 或 web API)(https://restfulapi.net/ 是 web 为适应 REST 架构而提供的服务

通过 REST 提供的服务不需要适应新的标准、开发或框架。大多数情况下,它将使用 GET 请求以及已发布到 API 的查询字符串来搜索它们的响应。HTTP 状态代码(https://restfulapi.net/http-status-codes/ 通常跟踪(404、200、304)以确定 API 的响应。还可以以各种格式获取响应,例如 JSON、XML 和 CSV。

就在 REST 和 SOAP 之间的选择而言,REST 在处理方面比 SOAP 更容易、更高效,而且大量网站正在向公众提供 REST

肥皂

肥皂https://www.w3.org/TR/soap/is 是 W3C 指定的一组标准,在 web 服务方面也代表了 REST 的替代方案。SOAP 使用 HTTP 和SMTP简单邮件传输协议),用于在 internet 上以及通过远程过程交换文档。

SOAP 使用 XML 作为消息传递服务,也称为基于 XML 的协议。SOAP 请求包含描述发送到服务器的方法和参数的 XML 文档(带有信封和正文)。服务器将执行接收到的方法以及参数,并将 SOAP 响应发送回发起请求的程序。

SOAP 具有高度的可扩展性,并包含内置的错误处理。它还可以与其他协议一起使用,例如 SMTP。SOAP 还独立于平台和编程语言,主要在分布式企业环境中实现。

web API 的好处

随着信息在网络上的可用性,信息需求与日俱增。信息来源、可用性、设施以及共享和交换的技术已成为全球需求。API 是首选数据源之一,可用于检索数据

API 不仅是通过 web 浏览器与用户通信的一种方式,您还可以使用系统。API 允许系统和设备(如手机)之间的通信,而不管它们的底层系统或编程语言如何。许多移动应用程序生成对特定 API 的请求,并显示从响应中检索到的相关信息。PIs 不仅仅是检索数据的简单服务;它们用于交换和处理信息,甚至跨不同平台和服务在系统之间进行通信。

从 web 抓取的角度来看,通过 API 获得的响应或数据优先于使用抓取脚本检索的数据。原因如下:

  • API 返回的数据完全特定于正在执行的请求,以及应用于它的过滤器或参数。
  • 并非总是需要使用 Python 库解析 HTML 或 XML 等任务,例如BeautifulSouppyquery、*、*和lxml
  • 数据的格式是结构化的,易于处理。
  • 最终清单的数据清理和处理将更加容易,或者可能不需要。
  • 与编码、分析 web 以及应用 XPath 和 CSS 选择器检索数据相比,处理时间将显著减少。
  • 它们很容易处理。

从爬取的角度来看,在完全适应 web API 之前,还需要考虑某些因素,包括以下因素:

  • 并非所有网站都向用户提供访问 web API 的权限。
  • 来自 API 的响应特定于预定义参数集。这可能会根据可以提出的要求限制确切的请求,并限制立即获得数据的可用性。
  • 返回的响应仅限于特定的卷,例如每个请求返回的记录数和允许的最大请求数。
  • 尽管数据将以结构化格式提供,但它可以跨键值对分布,这可能需要一些额外的合并任务。

鉴于以上几点,我们可以看到 web API 是从网站获取信息的首选

访问 web API 和数据格式

在本节中,我们将探索 web 上可用的各种 API,向它们发送请求并接收响应,然后再解释它们如何通过 Python 编程语言工作。

让我们考虑一下下面的样本 URL,它提供的 API 带有参数、定位器和身份验证。通过使用这些,我们可以访问以下资源:

  • https://api.someexampledomain.com  
  • https://api.someexampledomain.com/resource?key1=value1&key2=value2
  • https://api.someexampledomain.com/resource?api_key=ACCESS_KEY&key1=value1&key2=value2
  • https://api.someexampledomain.com/resource/v1/2019/01

参数或键值对集合实际上是 web 提供的预定义变量集。通常,API 会提供一些关于其用法、HTTP 方法、可用密钥和类型或密钥可以接收的允许值的文档或基本指南,以及 API 支持的功能的其他信息,如以下屏幕截图所示:

API details and links from https://sunrise-sunset.org/api

最终用户和系统只能使用具有提供商允许的特性和功能的 API。

下面是一些实际的 API 链接和示例调用,它们显示 URL 中使用的格式和参数:

keyapi_keyapiKeyapi-key等参数是安全和跟踪措施所必需的,需要在处理任何 API 请求之前获取

The API links and example calls in this section are linked to the resources they are listed on. For example, https://api.twitter.com/1.1/search/tweets.json?q=nasa&result_type=popular is listed on https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets.

使用 web 浏览器向 web API 发出请求

通过查询字符串获取有关要应用的参数的信息并获取 API 密钥(如果需要)是获得 API 访问权限的初步步骤。与 Google、Twitter 和 Facebook 提供的开发人员 API 相比,大多数公共或免费 API 都非常简单,易于管理。

可以使用 web 浏览器进行 API 请求。然而,在本节中,我们将尝试展示访问 API 时可能遇到的一些一般情况,同时展示 RESTful API 的一些重要属性

案例 1–访问简单 API(请求和响应)

在本节中,我们将使用以下 URL:https://api.sunrise-sunset.org/json?lat=27.717245 &液化天然气=85.323959&日期=2019-03-04

让我们通过一个简单的 API 处理一个请求,以获取尼泊尔加德满都的日出和日落时间(UTC 提供)。查询字符串需要所选位置的lat(纬度)、lng(经度)和date的值。如下面的屏幕截图所示,我们得到的响应是 JSON 格式的(使用浏览器扩展格式化),通过使用基于浏览器的开发工具,使用请求方法和 HTTP 状态代码(200,即OKSuccess,验证其请求成功:

Response from https://api.sunrise-sunset.org/json?lat=27.717245&lng=85.323959&date=2019-03-04 with Status Code

响应以原始格式或 JSON 格式返回,如下代码所示。正常获取的 JSON 响应使用 Pythonjson库进行处理。在下面的代码中,已经使用requests库处理了 API 请求。requests提供各种处理 HTTP 的功能;例如,HTTP 状态码可以通过status_code获取,表头可以通过headers获取。在此,我们对status_codeheaders,尤其是Content-Type感兴趣,以便我们可以计划进一步处理和使用可能需要的库:

import requests
url =     'https://api.sunrise-sunset.org/json?lat=27.7172&lng=85.3239&date=2019-03-04'

    results = requests.get(url) #request url
    print    (    "Status Code: "    , results.status_code)
    print    (    "Headers-ContentType: "    , results.headers[    'Content-Type'    ])
    print    (    "Headers: "    , results.headers)

jsonResult = results.json() #read JSON content
    print    (    "Type JSON Results"    ,    type    (jsonResult))
    print    (jsonResult)
    print    (    "SunRise & Sunset: "    ,jsonResult[    'results'    ][    'sunrise'    ],    " & "    ,jsonResult[    'results'    ][    'sunset'    ])

我们可以看到,status_code200(即OK),而Content-Type是 JSON 类型。这让我们确认,我们可以使用 JSON 相关库向前推进。但是,在本例中,我们使用的是来自requests库的json()函数,它减少了我们对额外库的依赖,并将响应对象转换为dict对象。通过我们收到的dict,我们可以使用key:value对访问所需的元素:

    Type Results <class 'requests.models.Response'>
Status Code: 200
Headers-ContentType: application/json

Headers: {'Access-Control-Allow-Origin':'*','Content-Type':'application/json','Vary':'Accept-Encoding', 'Server':'nginx','Connection':'keep-alive','Content-Encoding':'gzip','Transfer-Encoding':'chunked','Date': 'Mon, 04 Mar 2019 07:48:29 GMT'}

Type JSON Results <class 'dict'>

{'status':'OK','results':{'civil_twilight_end':'12:44:16 PM','astronomical_twilight_end':'1:38:31 PM', 'civil_twilight_begin':'12:16:32 AM','sunrise':'12:39:54 AM',......,'sunset':'12:20:54 PM','solar_noon': '6:30:24 AM','day_length':'11:41:00'}}

SunRise & Sunset: 12:39:54 AM & 12:20:54 PM

案例 2–演示 API 的状态代码和信息性响应

在本节中,我们将使用以下 URL:https://api.twitter.com/1.1/search/tweets.json?q=

在本节中,我们将处理来自 Twitter 的 API 请求。需要请求的 URL 为https://api.twitter.com/1.1/search/tweets.json?q= 。通过使用这个 URL,我们可以很容易地确定查询字符串q是空的,并且没有提供 Twitter API 所期望的值。完整的 URL 应该是类似于的内容 https://api.twitter.com/1.1/search/tweets.json?q=nasa &结果类型=流行

返回的响应是针对不完整的 API 调用的,可以在以下屏幕截图中看到,以及 HTTP 状态代码(400Bad Request)和 API 返回的一条消息,该消息用“消息”表示错误:“错误的身份验证数据”。有关 Twitter API 搜索选项的更多信息,请参阅https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets

Incomplete request made to Twitter API

Twitter API 返回的响应实际上是信息,而不是错误。这些信息丰富的响应使 API 在被其他资源使用时更具可伸缩性,更易于调试。这也是 RESTfulWeb 服务的一个值得赞赏的特性。通过部署 API 参数和其他需求,可以轻松克服此类信息

以下代码将使用空查询字符串向 Twitter 发出请求,并标识响应:

    import     requests
    import     json
url =     'https://api.twitter.com/1.1/search/tweets.json?q='        

results = requests.get(url)
    print    (    "Status Code: "    , results.status_code)
    print    (    "Headers: Content-Type: "    , results.headers[    'Content-Type'    ])

jsonResult = results.content        #jsonResult = results.json()
    print(jsonResult)

jsonFinal = json.loads(jsonResult.decode())
    print    (jsonFinal)     #print(json.loads(requests.get(url).content.decode()))

        if     results.status_code==    400    :
        print    (jsonFinal[    'errors'    ][    0    ][    'message'    ])
    else    :
        pass    

前面的代码使用jsonPython 库加载通过loads()函数获得的解码jsonResult。我们也可以使用requests中的json(),就像我们在案例 1 中所做的那样。jsonFinal现在是一个 Python dictionary 对象,可以进行探索,以便我们可以找到它的'key:value'。最终结果如下:

Status Code: 400
Headers: Content-Type: application/json; charset=utf-8

b'{"errors":[{"code":215,"message":"Bad Authentication data."}]}'
{'errors': [{'message': 'Bad Authentication data.', 'code': 215}]}

Bad Authentication data.

案例 3–演示 RESTful API 缓存功能

在本节中,我们将使用以下 URL:https://api.github.com/

GitHUb(https://github.com/ 是开发人员及其代码存储库的场所。GithubAPI 在开发人员中非常有名,他们都来自不同的编程背景。正如我们在下面的屏幕截图中所看到的,响应是用 JSON 获得的。请求成功,因为返回的 HTTP 状态码为200,即OKSuccess

Response from https://api.github.com with HTTP Status Code 200

如你所见,我们给打了一个基本电话 https://api.github.com 。返回的内容包含 API 的链接,以及为特定调用提供的一些参数,如{/gist_id}{/target}{query}

让我们再次向 API 发送请求,但这次参数值没有任何更改或更新。我们将收到的内容与之前的响应类似,但在 HTTPStatus Code中会有差异;也就是说,与 200OK相比,我们将得到304 Not Modified

HTTP Status code 304 for https://api.github.com

此 H TTP 状态代码(304Not Modified显示 REST 的缓存功能。由于响应没有任何更新或更新的内容,因此客户端缓存功能开始发挥作用。这有助于处理时间、带宽时间和使用率。缓存是 RESTfulWeb 服务的重要属性之一。下面是显示 RESTful API 的缓存属性的 Python 代码,该属性是通过在向requests.get()发出请求时传递提供给headers参数的外部头来获得的:

    import     requests
url =     'https://api.github.com'

#First Request
    results = requests.get(url)
    print    (    "Status Code: "    , results.status_code)
    print    (    "Headers: "    , results.headers)

#Second Request with 'headers'
etag = results.headers[    'ETag'    ]
    print    (    "ETag: "    ,etag)

results = requests.get(url,     headers    ={    'If-None-Match'    : etag})
    print    (    "Status Code: "    , results.status_code)

requests用于在代码中调用url两次。我们还可以看到,第二个请求提供了头信息的etag,即If-None-Match。此特定标头检查使用ETag键作为 HTTP 响应标头获得的响应标头。ETag用于跟踪目的,通常识别存在的资源。这显示了缓存能力。有关ETag的更多信息,请参考https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag

ETagresults.headers采集,通过获取 HTTPStatus Code: 304进行第二次请求转发。以下代码显示了输出:

Status Code: 200
Headers: Content-Type: application/json; charset=utf-8
Headers: {'X-GitHub-Request-Id': 'A195:073C:37F223:79CCB0:5C8144B4', 'Status': '200 OK','ETag': 'W/"7dc470913f1fe9bb6c7355b50a0737bc"', 'Content-Encoding': 'gzip','Date': 'Thu, 07 Mar 2019 16:20:05 GMT',........, 'Content-Type': 'application/json; charset=utf-8', ....., 'Server': 'GitHub.com'}

ETag: W/"7dc470913f1fe9bb6c7355b50a0737bc"
Status Code: 304

在本节中,我们学习了各种 API,通过使用功能访问它们,并演示了一些与 web 抓取方法相关的重要概念。在下一节中,我们将使用 API 抓取数据。

使用 api 的 Web 抓取

在本节中,我们将请求 API 并通过它们收集所需的数据。从技术上讲,通过 API 获得的数据与执行刮取活动不同,因为我们不能仅从 API 中提取所需的数据并对其进行进一步处理。

示例 1–搜索和收集大学名称和 URL

在本例中,我们将使用 HIPO 提供的 API(https://hipolabs.com/ )搜索大学:http://universities.hipolabs.com/search?name=Wales

此 API 使用一个名为name的查询参数,该参数将查找大学名称。我们还将提供一个附加参数country,其中包含国家名称,如美国和英国。可从以下 URL 请求此 API,更多信息可在中找到 https://github.com/hipo/university-domains-list

我们导入所需的库,使用readUrl()函数请求 API 并返回 JSON 响应,如下代码所示:

    import     requests
    import     json
dataSet = []

def     readUrl(search):
    results = requests.get(url+search)
        print    (    "Status Code: "    , results.status_code)
        print    (    "Headers: Content-Type: "    , results.headers[    'Content-Type'    ])
                return     results.json()

返回 JSON 响应后,可以使用我们找到的键和索引检索所需的值,如以下屏幕截图所示:

JSON (formatted) obtained from the API

nameurl被遍历并附加到dataSet

url =     'http://universities.hipolabs.com/search?name='
    jsonResult = readUrl(    'Wales'    )     # print(jsonResult)

for     university     in     jsonResult:
    name = university[    'name'    ]
    url = university[    'web_pages'    ][    0    ]
    dataSet.append([name,url])

print    (    "Total Universities Found: "    ,    len    (dataSet))
    print    (dataSet)

最终结果如下:

 Status Code: 200
        Headers: Content-Type: application/json
        Total Universities Found: 10

        [['University of Wales', 'http://www.wales.ac.uk/'],
        ['University of Wales Institute, Cardiff', 'http://www.uwic.ac.uk/'], 
        ......., 
        ['University of Wales, Lampeter', 'http://www.lamp.ac.uk/'],
        ['University of Wales, Bangor', 'http://www.bangor.ac.uk/']]    

示例 2–从 GitHub 事件中抓取信息

在本例中,我们将跨页面收集有关type(事件类型)、created_at(事件创建日期)、id(事件标识码)和repo(存储库名称)的信息。我们将使用以下 URL:https://api.github.com/events

GitHubEvents列出了过去 90 天内开展的公共活动。这些事件以页面形式提供,每页 30 项,最多显示 300 项。事件中存在不同的部分,所有这些都揭示了关于actorrepoorgcreated_attype等的描述

For more details, please refer to the following link:   https://developer.github.com/v3/activity/events/.

以下是我们将使用的代码:

    if     __name__ ==     "__main__"    :
    eventTypes=[] 
        #IssueCommentEvent,WatchEvent,PullRequestReviewCommentEvent,CreateEvent

            for     page     in         range    (    1    ,     4    ): #First 3 pages
        events = readUrl(    'events?page='     +     str    (page))

                for     event     in     events:
            id = event[    'id'    ]
            type = event[    'type'    ]
            actor = event[    'actor'    ][    'display_login'    ]
            repoUrl = event[    'repo'    ][    'url'    ]
            createdAt = event[    'created_at'    ]
            eventTypes.append(type)
            dataSet.append([id, type, createdAt, repoUrl, actor])

    eventInfo =     dict    (Counter(eventTypes))

        print    (    "Individual Event Counts:"    , eventInfo)
        print    (    "CreateEvent Counts:"    , eventInfo[    'CreateEvent'    ])
        print    (    "DeleteEvent Counts:"    , eventInfo[    'DeleteEvent'    ])

    print    (    "Total Events Found: "    ,     len    (dataSet))
    print    (dataSet)

前面的代码为我们提供了以下输出:

Status Code: 200
Headers: Content-Type: application/json; charset=utf-8
................
Status Code: 200
Headers: Content-Type: application/json; charset=utf-8

Individual Event Counts: {'IssueCommentEvent': 8, 'PushEvent': 42, 'CreateEvent': 12, 'WatchEvent': 9, 'PullRequestEvent': 10, 'IssuesEvent': 2, 'DeleteEvent': 2, 'PublicEvent': 2, 'MemberEvent': 2, 'PullRequestReviewCommentEvent': 1}

CreateEvent Counts: 12
DeleteEvent Counts: 2
Total Events Found: 90

[['9206862975','PushEvent','2019-03-08T14:53:46Z','https://api.github.com/repos/CornerYoung/MDN','CornerYoung'],'https://api.github.com/repos/OUP/INTEGRATION-ANSIBLE','peter-masters'],.....................,'2019-03-08T14:53:47Z','https://api.github.com/repos/learn-co-curriculum/hs-zhw-shoes-layout','maxwellbenton']]

collectionsPython 模块中的Counter类用于从eventTypes获取元素的单个计数:

    from     collections     import     Counter

总结

API 提供了几个好处,我们在本章中已经介绍了所有这些好处。RESTful web 服务的需求在不断增长,未来对数据请求和响应的贡献将超过以往任何时候。结构化的、易于访问的、基于参数的过滤器使 API 更易于使用,并且在节省时间方面表现出色。

在下一章中,我们将学习 Selenium 以及如何使用它从 web 上获取数据。

进一步阅读