html = ''' <div class="wrap"> Hello, World <p>This is a paragraph.</p> </div> ''' from pyquery import PyQuery as pq doc = pq(html) wrap = doc('.wrap') print(wrap.text())
from bs4 import BeautifulSoup soup = BeautifulSoup('<p>Hello</p>', 'lxml') print(soup.p.string)
在后面,Beautiful Soup的用法实例也统一用这个解析器来演示。
4. 基本用法
下面首先用实例来看看Beautiful Soup的基本用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
html = """ <html><head><title>The Dormouse's story</title></head> <body> <pclass="title"name="dromouse"><b>The Dormouse's story</b></p> <pclass="story">Once upon a time there were three little sisters; and their names were <ahref="http://example.com/elsie"class="sister"id="link1"><!-- Elsie --></a>, <ahref="http://example.com/lacie"class="sister"id="link2">Lacie</a> and <ahref="http://example.com/tillie"class="sister"id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <pclass="story">...</p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.prettify()) print(soup.title.string)
<html> <head> <title> The Dormouse's story </title> </head> <body> <pclass="title"name="dromouse"> <b> The Dormouse's story </b> </p> <pclass="story"> Once upon a time there were three little sisters; and their names were <aclass="sister"href="http://example.com/elsie"id="link1"> <!-- Elsie --> </a> , <aclass="sister"href="http://example.com/lacie"id="link2"> Lacie </a> and <aclass="sister"href="http://example.com/tillie"id="link3"> Tillie </a> ; and they lived at the bottom of a well. </p> <pclass="story"> ... </p> </body> </html> The Dormouse's story
html = """ <html><head><title>The Dormouse's story</title></head> <body> <pclass="title"name="dromouse"><b>The Dormouse's story</b></p> <pclass="story">Once upon a time there were three little sisters; and their names were <ahref="http://example.com/elsie"class="sister"id="link1"><!-- Elsie --></a>, <ahref="http://example.com/lacie"class="sister"id="link2">Lacie</a> and <ahref="http://example.com/tillie"class="sister"id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <pclass="story">...</p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.title) print(type(soup.title)) print(soup.title.string) print(soup.head) print(soup.p)
运行结果如下:
1 2 3 4 5
<title>The Dormouse's story</title> <class 'bs4.element.Tag'> The Dormouse's story <head><title>The Dormouse's story</title></head> <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
html = """ <html> <head> <title>The Dormouse's story</title> </head> <body> <pclass="story"> Once upon a time there were three little sisters; and their names were <ahref="http://example.com/elsie"class="sister"id="link1"> <span>Elsie</span> </a> <ahref="http://example.com/lacie"class="sister"id="link2">Lacie</a> and <ahref="http://example.com/tillie"class="sister"id="link3">Tillie</a> and they lived at the bottom of a well. </p> <pclass="story">...</p> """
运行结果如下:
1 2 3
['\n Once upon a time there were three little sisters; and their names were\n ', <aclass="sister"href="http://example.com/elsie"id="link1"> <span>Elsie</span> </a>, '\n', <aclass="sister"href="http://example.com/lacie"id="link2">Lacie</a>, ' \n and\n ', <aclass="sister"href="http://example.com/tillie"id="link3">Tillie</a>, '\n and they lived at the bottom of a well.\n ']
html = """ <html> <head> <title>The Dormouse's story</title> </head> <body> <pclass="story"> Once upon a time there were three little sisters; and their names were <ahref="http://example.com/elsie"class="sister"id="link1"> <span>Elsie</span> </a> </p> <pclass="story">...</p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.a.parent)
运行结果如下:
1 2 3 4 5 6
<pclass="story"> Once upon a time there were three little sisters; and their names were <aclass="sister"href="http://example.com/elsie"id="link1"> <span>Elsie</span> </a> </p>
html = """ <html> <body> <p class="story"> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"> <span>Elsie</span> </a> Hello <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a> and they lived at the bottom of a well. </p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print('Next Sibling', soup.a.next_sibling) print('Prev Sibling', soup.a.previous_sibling) print('Next Siblings', list(enumerate(soup.a.next_siblings))) print('Prev Siblings', list(enumerate(soup.a.previous_siblings)))
运行结果如下:
1 2 3 4 5 6 7 8
Next Sibling Hello
Prev Sibling Once upon a time there were three little sisters; and their names were
Next Siblings [(0, '\n Hello\n '), (1, <a class="sister"href="http://example.com/lacie"id="link2">Lacie</a>), (2, ' \n and\n '), (3, <a class="sister"href="http://example.com/tillie"id="link3">Tillie</a>), (4, '\n and they lived at the bottom of a well.\n ')] Prev Siblings [(0, '\n Once upon a time there were three little sisters; and their names were\n ')]
html = """ <html> <body> <p class="story"> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1">Bob</a><a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> </p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print('Next Sibling:') print(type(soup.a.next_sibling)) print(soup.a.next_sibling) print(soup.a.next_sibling.string) print('Parent:') print(type(soup.a.parents)) print(list(soup.a.parents)[0]) print(list(soup.a.parents)[0].attrs['class'])
运行结果如下:
1 2 3 4 5 6 7 8 9 10 11
NextSibling: <class 'bs4.element.Tag'> <aclass="sister" href="http://example.com/lacie" id="link2">Lacie</a> Lacie Parent: <class 'generator'> <pclass="story"> Onceuponatimetherewerethreelittlesisters; and their names were <a class="sister" href="http://example.com/elsie" id="link1">Bob</a><a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> </p> ['story']
for ul in soup.find_all(name='ul'): print(ul.find_all(name='li')) for li in ul.find_all(name='li'): print(li.string)
运行结果如下:
1 2 3 4 5 6 7
[<li class="element">Foo</li>, <liclass="element">Bar</li>, <liclass="element">Jay</li>] Foo Bar Jay [<liclass="element">Foo</li>, <liclass="element">Bar</li>] Foo Bar
import re html=''' <div class="panel"> <div class="panel-body"> <a>Hello, this is a link</a> <a>Hello, this is a link, too</a> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.find_all(text=re.compile('link')))
运行结果如下:
1
['Hello, this is a link', 'Hello, this is a link, too']
from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') for li in soup.select('li'): print('Get Text:', li.get_text()) print('String:', li.string)
运行结果如下:
1 2 3 4 5 6 7 8 9 10
GetText: Foo String: Foo GetText: Bar String: Bar GetText: Jay String: Jay GetText: Foo String: Foo GetText: Bar String: Bar
from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//*') print(result)
运行结果如下:
1
[<Element html at0x10510d9c8>, <Element body at0x10510da08>, <Element divat0x10510da48>, <Element ul at0x10510da88>, <Element li at0x10510dac8>, <Element a at0x10510db48>, <Element li at0x10510db88>, <Element a at0x10510dbc8>, <Element li at0x10510dc08>, <Element a at0x10510db08>, <Element li at0x10510dc48>, <Element a at0x10510dc88>, <Element li at0x10510dcc8>, <Element a at0x10510dd08>]
from lxml import etree text = ''' <li class="li li-first"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[@class="li"]/a/text()') print(result)
from lxml import etree text = ''' <li class="li li-first" name="item"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()') print(result)
text = ''' <div> <ul> <liclass="item-0"><a href="link1.html"><span>first item</span></a></li> <liclass="item-1"><a href="link2.html">second item</a></li> <liclass="item-inactive"><a href="link3.html">third item</a></li> <liclass="item-1"><a href="link4.html">fourth item</a></li> <liclass="item-0"><a href="link5.html">fifth item</a> </ul> </div> ''' html = etree.HTML(text) result = html.xpath('//li[1]/ancestor::*') print(result) result = html.xpath('//li[1]/ancestor::div') print(result) result = html.xpath('//li[1]/attribute::*') print(result) result = html.xpath('//li[1]/child::a[@href="link1.html"]') print(result) result = html.xpath('//li[1]/descendant::span') print(result) result = html.xpath('//li[1]/following::*[2]') print(result) result = html.xpath('//li[1]/following-sibling::*') `print(result)`
运行结果如下:
1 2 3 4 5 6 7
[<Element html at 0x107941808>, <Element body at 0x1079418c8>, <Element div at 0x107941908>, <Element ul at 0x107941948>] [<Element div at 0x107941908>] ['item-0'] [<Element a at 0x1079418c8>] [<Element span at 0x107941948>] [<Element a at 0x1079418c8>] [<Element li at 0x107941948>, <Element li at 0x107941988>, <Element li at 0x1079419c8>, <Element li at 0x107941a08>]
defwrite_to_json(content): with open('result.txt', 'a') as f: print(type(json.dumps(content))) f.write(json.dumps(content, ensure_ascii=False,).encode('utf-8'))
defwrite_to_file(content): with open('result.txt', 'a', encoding='utf-8') as f: f.write(json.dumps(content, ensure_ascii=False) + '\n')
defmain(offset): url = 'http://maoyan.com/board/4?offset=' + str(offset) html = get_one_page(url) for item in parse_one_page(html): print(item) write_to_file(item)
if __name__ == '__main__': for i in range(10): main(offset=i * 10) time.sleep(1)
还是上面的 HTML 文本,如果想获取所有a节点的超链接、歌手和歌名,就可以将search()方法换成findall()方法。如果有返回结果的话,就是列表类型,所以需要遍历一下来依次获取每组内容。代码如下:
1 2 3 4 5 6
results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S) print(results) print(type(results)) for result in results: print(result) print(result[0], result[1], result[2])
前面我们使用 urllib 处理过 Cookies,写法比较复杂,而有了 requests,获取和设置 Cookies 只需一步即可完成。
我们先用一个实例看一下获取 Cookies 的过程:
1 2 3 4 5 6
import requests
r = requests.get("https://www.baidu.com") print(r.cookies) forkey, value in r.cookies.items(): print(key + '=' + value)
运行结果如下:
1 2 3
<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>, <Cookie __bsi=13533594356813414194_00_14_N_N_2_0303_C02F_N_N_N_0 for .www.baidu.com/>]> BDORZ=27315 __bsi=13533594356813414194_00_14_N_N_2_0303_C02F_N_N_N_0
cookies = 'q_c1=31653b264a074fc9a57816d1ea93ed8b|1474273938000|1474273938000; d_c0="AGDAs254kAqPTr6NW1U3XTLFzKhMPQ6H_nc=|1474273938"; __utmv=51854390.100-1|2=registration_date=20130902=1^3=entry_date=20130902=1;a_t="2.0AACAfbwdAAAXAAAAso0QWAAAgH28HQAAAGDAs254kAoXAAAAYQJVTQ4FCVgA360us8BAklzLYNEHUd6kmHtRQX5a6hiZxKCynnycerLQ3gIkoJLOCQ==";z_c0=Mi4wQUFDQWZid2RBQUFBWU1DemJuaVFDaGNBQUFCaEFsVk5EZ1VKV0FEZnJTNnp3RUNTWE10ZzBRZFIzcVNZZTFGQmZn|1474887858|64b4d4234a21de774c42c837fe0b672fdb5763b0' jar = requests.cookies.RequestsCookieJar() headers = { 'Host': 'www.zhihu.com', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36' } for cookie in cookies.split(';'): key, value = cookie.split('=', 1) jar.set(key, value) r = requests.get("http://www.zhihu.com", cookies=jar, headers=headers) print(r.text)
使用get()方法成功实现一个 GET 请求,这倒不算什么,更方便之处在于其他的请求类型依然可以用一句话来完成,示例如下:
1 2 3 4 5
r = requests.post('http://httpbin.org/post') r = requests.put('http://httpbin.org/put') r = requests.delete('http://httpbin.org/delete') r = requests.head('http://httpbin.org/get') r = requests.options('http://httpbin.org/get')
r = requests.get("https://www.zhihu.com/explore") print(r.text)
运行结果如下:
1 2 3
<html><body><h1>500 Server Error</h1> An internal server error occured. </body></html>
但如果加上headers并加上User-Agent信息,那就没问题了:
1 2 3 4 5 6 7
import requests
headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36' } r = requests.get("https://www.zhihu.com/explore", headers=headers) print(r.text)
当然,我们可以在headers这个参数中任意添加其他的字段信息。
4. POST 请求
前面我们了解了最基本的 GET 请求,另外一种比较常见的请求方式是 POST。使用requests实现 POST 请求同样非常简单,示例如下:
1 2 3 4 5
import requests
data = {'name': 'germey', 'age': '22'} r = requests.post("http://httpbin.org/post", data=data) print(r.text)
在浏览网站的过程中,我们经常会遇到需要登录的情况,有些页面只有登录之后才可以访问,而且登录之后可以连续访问很多次网站,但是有时候过一段时间就需要重新登录。还有一些网站,在打开浏览器时就自动登录了,而且很长时间都不会失效,这种情况又是为什么?其实这里面涉及会话和 Cookies 的相关知识,本节就来揭开它们的神秘面纱。
1. 静态网页和动态网页
在开始之前,我们需要先了解一下静态网页和动态网页的概念。这里还是前面的示例代码,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<!DOCTYPE html> <html> <head> <metacharset="UTF-8"> <title>This is a Demo</title> </head> <body> <divid="container"> <divclass="wrapper"> <h2class="title">Hello World</h2> <pclass="text">Hello, this is a paragraph.</p> </div> </div> </body> </html>
这是最基本的 HTML 代码,我们将其保存为一个.html 文件,然后把它放在某台具有固定公网 IP 的主机上,主机上装上 Apache 或 Nginx 等服务器,这样这台主机就可以作为服务器了,其他人便可以通过访问服务器看到这个页面,这就搭建了一个最简单的网站。
这种网页的内容是 HTML 代码编写的,文字、图片等内容均通过写好的 HTML 代码来指定,这种页面叫作静态网页。它加载速度快,编写简单,但是存在很大的缺陷,如可维护性差,不能根据 URL 灵活多变地显示内容等。例如,我们想要给这个网页的 URL 传入一个name参数,让其在网页中显示出来,是无法做到的。
而在 Web 中,会话对象用来存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在会话对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个会话对象。当会话过期或被放弃后,服务器将终止该会话。
(2) Cookies
Cookies 指某些网站为了辨别用户身份、进行会话跟踪而存储在用户本地终端上的数据。
会话维持
那么,我们怎样利用 Cookies 保持状态呢?当客户端第一次请求服务器时,服务器会返回一个请求头中带有Set-Cookie字段的响应给客户端,用来标记是哪一个用户,客户端浏览器会把 Cookies 保存起来。当浏览器下一次再请求该网站时,浏览器会把此 Cookies 放到请求头一起提交给服务器,Cookies 携带了会话 ID 信息,服务器检查该 Cookies 即可找到对应的会话是什么,然后再判断会话来以此来辨认用户状态。
在成功登录某个网站时,服务器会告诉客户端设置哪些 Cookies 信息,在后续访问页面时客户端会把 Cookies 发送给服务器,服务器再找到对应的会话加以判断。如果会话中的某些设置登录状态的变量是有效的,那就证明用户处于登录状态,此时返回登录之后才可以查看的网页内容,浏览器再进行解析便可以看到了。
反之,如果传给服务器的 Cookies 是无效的,或者会话已经过期了,我们将不能继续访问页面,此时可能会收到错误的响应或者跳转到登录页面重新登录。
但是当我们关闭浏览器时,浏览器不会主动在关闭之前通知服务器它将要关闭,所以服务器根本不会有机会知道浏览器已经关闭。之所以会有这种错觉,是因为大部分会话机制都使用会话 Cookie 来保存会话 ID 信息,而关闭浏览器后 Cookies 就消失了,再次连接服务器时,也就无法找到原来的会话了。如果服务器设置的 Cookies 保存到硬盘上,或者使用某种手段改写浏览器发出的 HTTP 请求头,把原来的 Cookies 发送给服务器,则再次打开浏览器,仍然能够找到原来的会话 ID,依旧还是可以保持登录状态的。
HTML 是用来描述网页的一种语言,其全称叫作 Hyper Text Markup Language,即超文本标记语言。网页包括文字、按钮、图片和视频等各种复杂的元素,其基础架构就是 HTML。不同类型的文字通过不同类型的标签来表示,如图片用img标签表示,视频用video标签表示,段落用p标签表示,它们之间的布局又常通过布局标签div嵌套组合而成,各种标签通过不同的排列和嵌套才形成了网页的框架。
在 Chrome 浏览器中打开百度,右击并选择“检查”项(或按 F12 键),打开开发者模式,这时在 Elements 选项卡中即可看到网页的源代码,如图 2-9 所示。
我们首先用例子来感受一下 HTML 的基本结构。新建一个文本文件,名称可以自取,后缀为 html,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<!DOCTYPE html> <html> <head> <metacharset="UTF-8"> <title>This is a Demo</title> </head> <body> <divid="container"> <divclass="wrapper"> <h2class="title">Hello World</h2> <pclass="text">Hello, this is a paragraph.</p> </div> </div> </body> </html>
这就是一个最简单的 HTML 实例。开头用DOCTYPE定义了文档类型,其次最外层是html标签,最后还有对应的结束标签来表示闭合,其内部是head标签和body标签,分别代表网页头和网页体,它们也需要结束标签。head标签内定义了一些页面的配置和引用,如:
URL 是 URI 的子集,也就是说每个 URL 都是 URI,但不是每个 URI 都是 URL。那么,怎样的 URI 不是 URL 呢?URI 还包括一个子类叫作 URN,它的全称为 Universal Resource Name,即统一资源名称。URN 只命名资源而不指定如何定位资源,比如 urn:isbn:0451450523 指定了一本书的 ISBN,可以唯一标识这本书,但是没有指定到哪里定位这本书,这就是 URN。URL、URN 和 URI 的关系可以用图 2-1 表示。
图 2-1 URL、URN 和 URI 关系图
但是在目前的互联网中,URN 用得非常少,所以几乎所有的 URI 都是 URL,一般的网页链接我们既可以称为 URL,也可以称为 URI,我个人习惯称为 URL。
2. 超文本
接下来,我们再了解一个概念——超文本,其英文名称叫作 hypertext,我们在浏览器里看到的网页就是超文本解析而成的,其网页源代码是一系列 HTML 代码,里面包含了一系列标签,比如img显示图片,p指定显示段落等。浏览器解析这些标签后,便形成了我们平常看到的网页,而网页的源代码 HTML 就可以称作超文本。
例如,我们在 Chrome 浏览器里面打开任意一个页面,如淘宝首页,右击任一地方并选择“检查”项(或者直接按快捷键 F12),即可打开浏览器的开发者工具,这时在 Elements 选项卡即可看到当前网页的源代码,这些源代码都是超文本,如图 2-2 所示。
HTTP 的全称是 Hyper Text Transfer Protocol,中文名叫作超文本传输协议。HTTP 协议是用于从网络传输超文本数据到本地浏览器的传送协议,它能保证高效而准确地传送超文本文档。HTTP 由万维网协会(World Wide Web Consortium)和 Internet 工作小组 IETF(Internet Engineering Task Force)共同合作制定的规范,目前广泛使用的是 HTTP 1.1 版本。
HTTPS 的全称是 Hyper Text Transfer Protocol over Secure Socket Layer,是以安全为目标的 HTTP 通道,简单讲是 HTTP 的安全版,即 HTTP 下加入 SSL 层,简称为 HTTPS。
Host:用于指定请求资源的主机 IP 和端口号,其内容为请求 URL 的原始服务器或网关的位置。从 HTTP 1.1 版本开始,请求必须包含此内容。
Cookie:也常用复数形式 Cookies,这是网站为了辨别用户进行会话跟踪而存储在用户本地的数据。它的主要功能是维持当前访问会话。例如,我们输入用户名和密码成功登录某个网站后,服务器会用会话保存登录状态信息,后面我们每次刷新或请求该站点的其他页面时,会发现都是登录状态,这就是 Cookies 的功劳。Cookies 里有信息标识了我们所对应的服务器的会话,每次浏览器在请求该站点的页面时,都会在请求头中加上 Cookies 并将其发送给服务器,服务器通过 Cookies 识别出是我们自己,并且查出当前状态是登录状态,所以返回结果就是登录之后才能看到的网页内容。
中国空气质量在线监测分析平台是一个收录全国各大城市天气数据的网站,包括温度、湿度、PM 2.5、AQI 等数据,链接为:https://www.aqistudy.cn/html/city_detail.html,预览图如下: 通过这个网站我们可以获取到各大城市任何一天的天气数据,对数据分析还是非常有用的。 然而不幸的是,该网站的数据接口通信都被加密了。经过分析之后发现其页面数据是通过 Ajax 加载的,数据接口地址是:https://www.aqistudy.cn/apinew/aqistudyapi.php,是一个 POST 形式访问的接口,这个接口的请求数据和返回数据都被加密了,即 POST 请求的 Data、返回的数据都被加密了,下图是数据接口的 Form Data 部分,可见传输数据是一个加密后的字符串: 下图是该接口返回的内容,同样是经过加密的字符串: 遇到这种接口加密的情况,一般来说我们会选择避开请求接口的方式进行数据爬取,如使用 Selenium 模拟浏览器来执行。但这个网站的数据是图表展示的,所以其数据会变得难以提取。 那怎么办呢?刚啊!
var getParam = (function () { function ObjectSort(obj) { var newObject = {}; Object.keys(obj).sort().map(function (key) { newObject[key] = obj[key] }); return newObject } return function (method, obj) { var appId = '1a45f75b824b2dc628d5955356b5ef18'; var clienttype = 'WEB'; var timestamp = new Date().getTime(); var param = { appId: appId, method: method, timestamp: timestamp, clienttype: clienttype, object: obj, secret: hex_md5(appId + method + timestamp + clienttype + JSON.stringify(ObjectSort(obj))) }; param = BASE64.encrypt(JSON.stringify(param)); return AES.encrypt(param, aes_client_key, aes_client_iv) } })();
可以看到这里使用了 Base64 和 AES 加密。加密之后的字符串便作为 POST Data 传送给服务器了,然后服务器再进行解密处理,然后进行逻辑处理,然后再对处理后的数据进行加密,返回了加密后的数据,那么 JavaScript 再接收到之后再进行一次解密,再渲染才能得到正常的结果。 所以这里还需要分析服务器传回的数据是怎样解密的。顺腾摸瓜,很容易就找到一个 decodeData() 方法,其定义如下:
1 2 3 4 5 6
function decodeData(data) { data = AES.decrypt(data, aes_server_key, aes_server_iv); data = DES.decrypt(data, des_key, des_iv); data = BASE64.decrypt(data); returndata }
嗯,这里又经过了三层解密,才把正常的明文数据解析出来。 所以一切都清晰了,我们需要实现两个过程才能正常使用这个接口,即实现 POST Data 的加密过程和 Response Data 的解密过程。其中 POST Data 的加密过程是 Base64 + AES 加密,Response Data 的解密是 AES + DES + Base64 解密。加密解密的 Key 也都在 JavaScript 文件里能找到,我们用 Python 实现这些加密解密过程就可以了。 所以接下来怎么办?接着刚啊! 接着刚才怪! 何必去费那些事去用 Python 重写一遍 JavaScript,万一二者里面有数据格式不统一或者二者由于语言不兼容问题导致计算结果偏差,上哪里去 Debug? 那怎么办?这里我们借助于 PyExecJS 库来实现 JavaScript 模拟就好了。
curl -sSL http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/internet | sh -
DaoCloud 的安装脚本:
1
curl -sSL https://get.daocloud.io/docker | sh
这两个脚本可以任选其一,速度都非常不错。
等待脚本执行完毕之后,就可以使用 Docker 相关命令了,如运行测试 Hello World 镜像:
1
docker run hello-world
运行结果:
1 2 3 4 5 6 7
Unable tofind image 'hello-world:latest' locally latest: Pulling from library/hello-world 78445dd45222: Pull complete Digest:sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears tobe working correctly.
如果出现类似上面提示的内容,则证明 Docker 可以正常使用了。
4. Mac 下的安装
Mac 平台同样有两种选择:Docker for Mac 和 Docker Toolbox。
Docker for Mac 要求系统为 OS X EI Captain 10.11 或更新,至少 4GB 内存。如果你的系统满足此要求,则强烈建议安装 Docker for Mac。