随着 JavaScript 和 Ajax 内容的生成和加载变得越来越普遍,解决该问题的方法之一:利用 Selenium 让浏览器自动加载网站并获取数据。
本章完全不用理会 JavaScript(没有必要运行甚至是加载 JavaScript),直接获得数据源:生成数据的 API。
1.API概述
API 定义了允许一个软件与另一个软件通信的标准语法,即便是这两个软件是用不同的语言编写的或者是架构不同。
本篇重点介绍 Web API(特别是允许 Web 服务器与浏览器交流的 API),,在其他上下文中,API 也会被用作一个通用的词,指允许 Java 程序与 Python 程序在同一台机器上通信的接口。API 并不一定是“跨网络的”,也并不总是涉及任何 Web 技术。
API 的文档通常将路由或者端点(endpoint)描述为你可以请求的 URL,而变量参数要么是 URL 路径,要么是 GET 请求的参数。例如以下两个链接pathparam分别作为路由路径里的一个参数,以及作为param1的参数值。(传递变量数据)

http://example.com/the-api-route/pathparam
http://example.com/the-api-route?param1=pathparam

API 的响应通常是 JSON 或者 XML 格式的。很多 API 允许你通过改变一个参数来改变响应类型。两种格式示例如下:

{“user”:{“id”: 123, “name”: “Ryan Mitchell”, “city”: “Boston”}}

<user><id>123</id><name>Ryan Mitchell</name><city>Boston</city></user>

(1)HTTP方法和API
利用 HTTP 从 Web 服务器获取信息有 4 种方式(或方法):

  • GET,GET 请求对服务器数据库的信息不会有任何影响。只是读取信息。
  • POST,当你填写表单或提交信息到 Web 服务器的后端程序时,使用的就是 POST。用API 发起一个 POST 请求,相当于说“请把这个信息保存到你的数据库里”。
  • PUT,PUT 请求用来更新一个对象或信息。例如更新老用户邮箱地址。
  • DELETE,DELETE 用于删除对象。DELETE 方法在公共 API 里面不常用,公共 API 主要用于传播信息或者允许用户创建或发布信息,而不是让用户删掉数据库中的信息。
  • (技术上看,不止存在以上 4 种方式(例如 HEAD、OPTIONS 和 CONNECT),但很少用,大多数 API 仅提供了以上 4 种方法,甚至是这 4 种方法的一个子集。)

GET 请求不同,除了你请求数据的 URL 或路由以外,POST、PUT 和 DELETE 请求还允许你在请求体中发送其他信息。
和你从 Web 服务器接收到的响应一样,请求体中的这个数据通常也是 JSON 格式的,有时是 XML 格式的,而且数据的格式是在 API 的语法中定义好的。
例如,用一个 API创建博客文章的评论,可能会发送一个 PUT 请求到:
http://example.com/comments?post=123
请求体为:
{“title”: “Great post about APIs!”, “body”: “Very informative. Really helped me out with a tricky technical challenge I was facing. Thanks for taking the time to write such a detailed blog post about PUT requests!”, “author”: {“name”: “Ryan Mitchell”, “website”: “http://pythonscraping.com”, “company”: “O’Reilly Media”}}
这里博客文章的 ID(123)作为参数传入 URL,即你做出的新评论的内容通过请求
体传送。参数和数据可以在参数变量和请求体中同时传送。而需要哪些参数以及在哪里传送依然是由 API 的语法决定的。

(2)更多关于API响应的介绍
API 的一个重要特性是会返回格式良好的响应。最常见的响应格式是 XML(eXtensible Markup Language,可扩展标记语言)和JSON(JavaScript Object Notation,JavaScript 对象表示法)。
JSON比XML受欢迎,一个是因为JSON文件通常比XML文件小。另一个原因是Web技术的改变(过去,服务器端用 PHP和 .NET 这些程序作为 API 的接收端。现在,服务器端也会用一些 JavaScript 框架作为 API的发送和接收端,比如 Angular 或 Backbone 等。像 Backbone 之类的 JavaScript 库处理 JSON 要比处理 XML 简单。)、
API 的响应类型受限于创建它的程序员的想象力。CSV 是另外一种典型的响应输出。一些 API 甚至被设计用来生成文件输出。一个请求可能是要求服务器生成一幅带有特
定文本的图像,或者请求特定的 XLSX 或 PDF 文件。一些API则完全没有响应。
如果API没有很好地进行配置,可能会使不可解析的栈跟踪或普通英文文本,当向 API 发出一个请求时,明智的做法通常是首先检查你得到的响应是 JSON 格式(或者是 XML、CSV 或其他你期望的格式)。
2.解析JSON数据
淘宝IP地址库网站查IP的API,用Python的 JSON 解析函数来解码:

import json
from urllib.request import urlopendef getCountry(ipAddress):response = urlopen('http://ip.taobao.com/service/getIpInfo.php?ip=' + ipAddress).read().decode('urf-8')responseJson = json.loads(response)return responseJson.get('data')['country']print(getCountry('50.78.253.58'))

JSON 解析库是 Python 标准库的一部分。Python 使用了一种更加灵活的方式,将 JSON 对象转换成字典,将 JSON 数组转换成列表,将 JSON 字符串转换成 Python 字符串,等等。
下面代码显示如何使用Python的JSON解析库,处理 JSON 字符串中可能出现的不同数据类型:

import json
jsonString = '{"arrayOfNums":[{"number": 0}, {"number": 1}, {"number": 2}], "arrayOfFruits":[{"fruit": "apple"}, {"fruit": "banana"}, {"fruit": "pear"}]}'
jsonObj = json.loads(jsonString)print(jsonObj.get('arrayOfNums'))
print(jsonObj.get('arrayOfNums')[1])
print(jsonObj.get('arrayOfNums')[1].get('number') + jsonObj.get('arrayOfNums')[2].get('number'))
print(jsonObj.get('arrayOfFruits')[2].get('fruit'))


第一行是一个字典对象列表,第二行是一个字典对象,第三行是一个整数(第一行字典列表中整数的和),第四行是一个字符串。

3.无文档的API
大多数 API 是没有发布任何文档的。这一切都与JavaScript 有关。
通常,在用户请求一个网页时,动态网站的 Web 服务器会做以下几件事情:

  • 处理来自请求网站页面的用户的 GET 请求
  • 从数据库检索页面需要呈现的数据
  • 按照 HTML 模板组织页面数据
  • 发送带格式的 HTML 给用户

由于 JavaScript 框架变得越来越普遍,很多 HTML 创建任务从原来的由服务器处理变成了由浏览器处理。服务器可能给用户浏览器发送一个硬编码的 HTML 模板,但是还需要单独的 Ajax 请求来加载内容,并将这些内容放到 HTML 模板中正确的位置(slot)。所有这些都发生在浏览器 / 客户端上。
爬虫抓取的是一个不带有任何内容的 HTML 模板。Selenium让网络爬虫可以变成浏览器,请求HTML 模板,执行任意的 JavaScript,允许加载所有的数据,然后再抓取网页的数据。整个内容管理系统(曾经只位于 Web 服务器中)基本上已经移到了浏览器端,当使用 Selenium 时,用户不需要的“额外信息”也被加载了。
因为服务器不再将数据处理成 HTML 格式,所以它们通常作为数据库本身的一个弱包装器。该弱包装器简单地从数据库中抽取数据,并通过一个 API 将数据返回给页面。这些 API 并未打算供除网页本身以外的任何人或者任何事使用,因此开发者未为这些 API 提供文档。但这些API的确是存在的。
用Selenium加载该页面可能发送近百次请求企鹅传输较多的数据,如果直接使用API的话,你只需发起一次请求。

(1)查找无文档的API
使用 Chrome 检查器查看用于构建页面的调用的请求和响应。Chrome 检查器窗口并点击网络选项卡Network。需要在页面加载前就打开这个窗口。当关闭时,它不会追踪网络调用。
API 调用有几个特征:

  • 它们通常包含 JSON 或 XML。你可以利用搜索 / 过滤字段过滤请求列表
  • 利用 GET 请求,URL 中会包含一个传递给它们的参数。
  • 它们通常是 XHR 类型的。(XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。XMLHttpRequest 可以用于获取任何类型的数据,而不仅仅是 XML。它甚至支持 HTTP 以外的协议(包括 file:// 和 FTP),尽管可能受到更多出于安全等原因的限制。)

(2)记录未被记录的API
要在网站上加载多个页面,在检查器控制台的网络选项卡中筛选出目标 API 调用。之后你可以看到这个调用在不同页面的变化,并且识别出该调用接收的字段和返回的字段。
API 调用可以留心以下字段进行识别和记录:

  • 使用的 HTTP 方法
  • 输入。路径参数,请求头(包括cookie),正文内容(对于PUT和POST调用)
  • 输出。响应头(包括cookie集合),响应正文类型,响应正文字段

(3)自动查找和记录API
查找和记录 API是一项烦琐和偏算法的工作。有些网站可能尝试混淆浏览器是如何获得数据的。查找和记录 API 主要是一个程序性任务。
本书作者创建的 https://github.com/REMitchell/apiscraper GitHub仓库,试图帮助查找和记录API。该工具会使用 Selenium、ChromeDriver 和一个叫作 BrowserMob Proxy 的库来加载页面,在一个域内抓取网页,分析页面加载过程中的网络流量,并将这些请求组织成可读的 API调用。项目包含以下文件:

  • apicall.py,包含定义一个 API 调用的属性(路径、参数等),以及确定两个 API 调用是否相同的逻辑。
  • apiFinder.py,一个主抓取类。被 webservice.py 和 consoleservice.py 用来实现查找 API 的过程。
  • browser.py,仅有 3 个方法,initialize、get 和 close,但是具有一项比较复杂的功能,即将BrowserMob Proxy 和 Selenium 捆绑在一起。滚动页面以确保整个页面都被加载,将HTTP 存档(HAR)文件保存到合适的位置以便后续处理。
  • consoleservice.py,处理来自控制台的命令,并且负责主 APIFinder 类。
  • harParser.py,它解析 HAR 文件并抽取 API 调用。
  • html_template.html,它提供在浏览器中显示 API 调用的一个模板。
  • README.md,Git 的 readme 页面。

https://bmp.lightbody.net/ 下载 BrowserMob Proxy 的二进制文件,并将其解压缩文件放到apiscraper 项目的路径下。二进制文件放在项目根路径下的browsermob-proxy-2.1.4/bin/browsermob-proxy位置。
下载(对应使用浏览器版本) ChromeDriver,并将其放在 apiscraper 项目路径下。
还需要安装这些Python 库:tldextract,selenium,browsermob-proxy(均用pip安装)
以上准备工作做好后,可以开始搜集API调用了。
命令行进入项目文件夹,输入:python consoleservice.py -h

python consoleservice.py -u 目标URL -s 搜索词,运行出现了一些问题:(未完全搞明白,未继续深入了解)

  • apiFinder.py文件中需要修改chromedriver.exe的路径,chromedriver.exe文件需要放到Chrome软件chrome.exe所在目录下,并将该目录添加到环境变量的Path中。
  • 运行有时候报错:UnicodeEncodeError: ‘gbk’ codec can’t encode character ‘\u2122’ in position。需要在错误提示所在命令行open()函数中加入encoding=‘utf-8’
  • 运行后apiscraper项目下的server.log提示“ ‘browsermob-proxy-2.1.4’ 不是内部或外部命令,也不是可运行的程序或批处理文件。”进入项目目录browsermob-proxy-2.1.4/bin/下直接执行browsermob-proxy.bat弹出相应窗口。

利用 -i 标志位,可以从提供的初始 URL 开始抓取多个页面。
所有的抓取数据存储在一个 HAR 文件中,默认放在项目路径下的 /har 文件夹中,而该路径可以通过 -d 标志位进行修改。
如果没有提供 URL,你可以传入包含已抓取的 HAR 文件的路径进行查找和分析。
项目提供了其他功能:去除非必需的参数(去除 GET 或 POST 参数,这些参数并不会影响 API 调用的返回值),多种 API 输出格式(命令行、HTML、JSON),区分指示单独 API 路由的路径参数和只是作为同一个 API 路由的 GET 参数的路径参数。

4.API与其他数据源结合
许多现代 Web 应用存在的理由就是抓取现有的数据,再用更好看的形式展现出来。如果你用 API 作为唯一的数据源,那么你最多就是复制别人数据库里的数据。新颖的方式将两个或多个数据源组合起来,或者把 API 作为一种工具,从全新的视角对抓取到的数据进行解释。
把 API 和网页抓取结合起来:维基百科词条的编辑历史页面,里面是一列编辑记录。用户先登录维基百科再编辑词条,显示用户名。不先登录就对词条进行编辑,显示IP地址。搜集IP地址然后查出这些 IP 地址的地理位置,代码如下:

# -*- coding:GBK -*-
from urllib.request import urlopen
from bs4 import BeautifulSoup
import json
import datetime
import re
import randomrandom.seed(datetime.datetime.now())
def getLinks(articleUrl):html = urlopen('http://en.wikipedia.org()'.format(articleUrl))bs = BeautifulSoup(html, 'html.parser')return bs.find('div', {'id': 'bodyContent'}).findAll('a', href=re.compile('^(/wiki/)((?!:).)*$'))def getHistoryIPs(pageUrl):# 编辑历史页面的URL链接格式是# http://en.wikipedia.org/w/index.php?title=Title_in_URL&action=historypageUrl = pageUrl.replace('/wiki/', '')historyUrl = 'http://en.wikipedia.org/w/index.php?title={}&action=history'.format(pageUrl)print('history url is: {}'.format(historyUrl))html = urlopen(historyUrl)bs = BeautifulSoup(html, 'html.parser')# 找出class属性是"mw-userlink mw-anonuserlink"的链接# 他们用IP地址代替用户名ipAddresses = bs.findAll('a', {'class': 'mw-anonuserlink'})addressList = set()for ipAddress in ipAddresses:addressList.add(ipAddress.get_text())return addressListdef getCountry(ipAddress):try:response = urlopen('http://ip.taobao.com/service/getIpInfo.php?ip={}'.format(ipAdress)).read().decode('urf-8')responseJson = json.loads(response)country = responseJson.get('data')['country']except:return Noneelse:return countrylinks = getLinks('/wiki/Python_(programming_language)')while(len(links) > 0):for link in links:print('-'*20)historyIPs = getHistoryIPs(link.attrs['href'])for historyIP in historyIPs:country = getCountry(historyIP)if country is not None:print('{} is from {}'.format(historyIP, country))newLink = links[random.randint(0, len(links)-1)].attrs['href']links = getLinks(newLink)

函数getHistoryIPs,后者搜索所有 class 属性为 mw-anonuserlink 的链接内容(匿名用户的 IP 地址),返回一个链接列表。
代码用随机的搜索模式来查找词条的编辑历史。首先获取起始词条链接到的所有词条的编辑历史。然后,随机选择一个词条作为起始点,再获取这个页面链接到的所有词条的编辑历史。重复这个过程,直到某个页面不包含其他维基词条的链接为止。
获得了编辑历史的 IP 地址数据,用函数getCountry查询 IP 地址所属的国家。

5.再说一点API
如果希望更多地了解 API 的构建和语法,推荐你阅读 Leonard Richardson、Mike Amundsen 和 Sam Ruby 合著的 RESTful Web APIs。该书针对 Web API 的用法提供了非常全面的理论介绍与实践指导。另外,Mike Amundsen 的精彩视频教学课程“Designing APIs for the Web”,也可以教你创利用API抓取数据建自己的 API。
真正的技巧通常是透彻地认识你眼前的网页,并直接从数据源获取数据。

12.利用API抓取数据相关推荐

  1. 玩转【斗鱼直播APP】系列之利用青花瓷抓取数据

    利用青花瓷抓取数据 青花瓷是什么? 当然这里可不是周董的歌曲, 而是我们用于抓取接口的工具. 安装一下这个软件 百度搜索该工具会找到很多绿色版(破解版的文艺说法). 安装即可, 不再累述. 手机端配置 ...

  2. 小码哥-玩转【斗鱼直播APP】系列之利用青花瓷抓取数据

    利用青花瓷抓取数据 青花瓷是什么? 当然这里可不是周董的歌曲, 而是我们用于抓取接口的工具. 安装一下这个软件 百度搜索该工具会找到很多绿色版(破解版的文艺说法). 安装即可, 不再累述. 手机端配置 ...

  3. 利用 API 爬取数据,试着爬取 QQ 音乐流行指数榜

    网址 QQ音乐流行指数网址:https://y.qq.com/n/yqq/toplist/4.html 结果 平凡天使 - G.E.M. 邓紫棋 少年 - 梦然 我们 - 陈奕迅 你要相信这不是最后一 ...

  4. python怎么编写wireshark抓的包_使用Wireshark 抓取数据包

    Wireshark 是一个网络封包分析软件.网络封包分析软件的功能是获取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换. 一  ...

  5. 【入门-R爬虫抓取数据】文本挖掘之数据爬虫

    今天主要介绍一下,文本挖掘的数据获取方式,上一篇很多人在问数据如何获取,今天给大家介绍下数据获取的方式,主要利用爬虫抓取数据.基于,之前对python爬虫没接触过,尝试过用R爬虫,今天就来介绍下,如何 ...

  6. Java模拟登录并抓取数据

    问题: 最近做一个抓取数据的项目,发现网上很多资料不完备,或者按照代码执行不能真实爬取数据,自己特别根据自己的网站进行登录并进行数据爬取. 未登录 登录后,正常抓取数据截图(预期目标数据) 解决办法: ...

  7. Scrapy 案例一:通过 API 抓取天气预报数据

    目录 一.概述 二.案例分析 三.编码实现 四.获取多个 city 的天气信息(简单实现) 一.概述 在一些简单的网站中,可以发现,所有的数据都在网页代码中,然而在实际获取数据的过程中,我们可以发现, ...

  8. pythonallowpos_利用Python抓取并分析京东商品评论数据

    2.1 内容简介 本章主要介绍如何利用Python抓取京东商城商品评论信息,并对这些评论信息进行分析和可视化.下面是要抓取的商品信息,一款女士文胸.这个商品共有红色,黑色和肤色等颜色, 70B到90D ...

  9. 基于Thinkphp5+phpQuery 网络爬虫抓取数据接口,统一输出接口数据api

    TP5_Splider 一个基于Thinkphp5+phpQuery 网络爬虫抓取数据接口 统一输出接口数据api.适合正在学习Vue,AngularJs框架学习 开发demo,需要接口并保证接口不跨 ...

  10. python抓取数据包_利用python-pypcap抓取带VLAN标签的数据包方法

    1.背景介绍 在采用通常的socket抓包方式下,操作系统会自动将收到包的VLAN信息剥离,导致上层应用收到的包不会含有VLAN标签信息.而libpcap虽然是基于socket实现抓包,但在收到数据包 ...

最新文章

  1. sign函数的功能oracle,Oracle中sign函数和decode函数的使用
  2. 第二章:2.5 卷积辅助求解方法
  3. ASIHTTPRequest类库简介和使用说明
  4. NumPy-快速处理数据--ndarray对象--多维数组的存取、结构体数组存取、内存对齐、Numpy内存结构...
  5. textbox 和textera 文本框多行后不能拉伸
  6. 洛谷 P1115 最大子段和
  7. 求平均数、百分率、最大值、最小值、两个字段相差秒数的SQL
  8. JS中对数组元素进行增、删、改、查的方法,以及其他方法
  9. 华为P50系列即将发布:麒麟9000E/9000处理器有戏?
  10. word分页保存_搞定office丨Word快捷技巧第3弹!
  11. spring boot 处理自定义注解
  12. Oracle 归档开启切换和归档日志删除(单实例和RAC)
  13. ubuntu下MySQL无法启动Couldn't find MySQL server (/usr/bin/mysqld_safe)”
  14. form表单序列化后的数据转json对象
  15. 梯度下降优化器小结(RMSProp,Momentum,Adam)
  16. js 获取图片经纬度及写经纬度
  17. 格雷希尔GripSeal快速密封接头G70外卡式、滑套式快速连接器型号规格
  18. 论文里引用专利参考文献怎么写?
  19. 什么样的CTA策略组合最合理?
  20. 一个使用LABVIEW将16进制字符串分割转化的办法

热门文章

  1. 条条道路通罗马感受古罗马帝国的辉煌时代!
  2. 第二十三章 宋桓公自行其是讨苦吃 齐桓公连克宋郑震东方
  3. 电赛无人机特征匹配(二):ORB算法+BFM算法+D-P轮廓检测算法
  4. 自恢复保险丝选型说明
  5. 通过两个列表构建字典_我在两个月内以99美元的价格构建了一个电子商务网站!
  6. 2.已知维数求超立方体邻接矩阵
  7. 操作系统知识点(考试版)
  8. 动态规划实现生物碱基序列全局匹配
  9. java开发微信抢红包挂_java实现微信抢红包算法
  10. Telnet + VTY(虚拟终端Virtual Teletype Terminal)远程管理路由器和交换机