代码目录: https://github.com/brandon-rhodes/fopnp/tree/m/py3
每台机器通过一个Docker容器实现
邮箱地址:207 N. Defiance St Archbold, OH
获取该物理地址的纬度和经度:
virtualenv -p python3 geo_env
cd geo_env
ls
O]bin/ include/ lib/
.bin/activate
python -c 'import pygeocoder'
O]error
pygcocoder包未安装,在虚拟环境中使用pip安装pygeocoder包
pip install pygeocoder
python -c 'import pygeocoder'
#获取经度与纬度 search1.py
#!/usr/bin/env python3
from pygeocoder import Geocoder
if __name__ == "__main__":
address = '207 N. Definace St, Archbold, OH'
print(Geocoder.geocode(address)[o].coordinates)
执行python3 search1.py
O](41.521954, -84.306691)
pygeocoder接口背后的原理是怎样的?
后面将详细学习如何在一个包含至少6层的网络协议栈的顶层构建这个复杂的服务
如果需要自己为谷歌地图API编写客户端:没有使用直接提供地理编功能的三方库,而是使用更底层的Requests库。
#!/usr/bin/env python3
#/chapter01/search2.py
import requests
def geocode(address):
parameters = {'address': address, 'sensor': 'false'}
base = 'http://maps.googleapis.com/maps/api/geocode/json'
response = requests.get(base, params=parameters)
answer = response.json()
print(answer['results'][0]['geometry']['location']
if __name__ == '__main__':
gecode('207 N. Definace St, Archbold, OH')
执行python3 search2.py
O]{‘lat‘: 41.521954, ‘lng‘: -84.306691}
结果并不完全相同,JSON数据将结果封装为对象,Requests库以Python字典形式提供该对象
search2.py没有通过地址和纬度直接解决问题,而是通过构造URL,获取查询响应,然后将结果转化为JSON,一步步解决问题
search2.py脚本构建了一个URL,并获取了该URL查询的响应文档,为了使URL查询看起来像一个基础操作,Web浏览器做了很多事情。
URL可以获取某个文档的原因:描述了网络上该特定文档的位置及获取方法。提供了更底层协议查询该文档所需的指令,search2.py就能够解析URL并获取响应文档了。
URL包含了协议的名称,后面跟着保存文档的主机名,最后是该主机上特定文档的路径。
Requests库从谷歌获取结果的具体原理,其实就是由HTTP提供的,如果不想使用Requests库提供的功能,而是想直接使用HTTP来获取结果,使用/chapter01/search3.py
#!/usr/bin/env python3
import http.client
import json
from urllib.parse import quote_plus
base = '/maps/api/geocode/json'
def geocode(address):
path = '{}?address={}&sensor=false'.format(base, quote_polus(address))
connection = http.client.HTTPConnection('maps.google.com')
connection.request('GET', path)
rawreply = connection.getresponse().read()
reply = json.loads(rawreply.decode('utf-8'))
print(reply['results'][o]['geometry']['location'])
if __name__ == '__main__':
geocode('207 N. Definace St, Archbold, OH')
该程序直接使用HTTP协议:请求连接特定主机->手动构造带path参数的GET查询->直接从HTTP连接获取响应结果。
此方法没有使用字典将查询参数方便的表示为独立的键值对,而是手动嵌入到查询地址中。
要通过该方法完成查询,需要在?后跟上&分隔的参数,这些参数通过name=value的形式表示
HTTP协议利用,现代操作系统提供的使用TCP协议在IP网络的不同程序间进行纯文本网络会话的功能,在两台机器间传输数据。
HTTP协议精确描述了两台主机间通过TCP传输的信息格式,并以此提供HTTP的各项功能。
通过Python可以方便操作的网络协议栈的最底层:/chapter01/search4.py,像网络发送了一个原始文本信息作为请求,并收到了很多原始文本作为响应。
#!/usr/bin/env python3
import socket
from urllib.parse import quote_plus
request_text = """GET /maps/api/geocode/json?address={}&sensor=false HTTP/.1\r/n/
Host: maps.google.com:80\r\nUser-Agent:search4.py (Foundations of Python Network Programming)\r\nConnection: close\r\n\r\n"""
def geocode(address):
sock = socket.socket()
sock.connect(('maps.google.com', 80))
request = request_text.format(quote_plus(address))
sock.sendall(request.encode('ascii'))
raw_reply = b''
while True:
more = sock.recv(4096)
if not more:
break
raw_reply += more
print(raw_reply.decode('utf-8'))
if __name__ == '__main__':
geocode('207 N. Defiance St, Archbold, OH')
search4.py本质的不同:深入到最底层:使用主机操作系统提供的原始socket()函数来支持IP网络上的网络通信。相当于C写底层系统一样
原始网络通信的过程就是发送与接收字符串的过程。发送的查询是一个字符串,接收到的响应同样也是一个字符串。
通过sendall()函数传入的参数了解到该HTTP查询的具体内容。查询中包含了关键字GET,GET后跟着待获取文档的路径以及支持的HTTP版本。
GET /maps/api/geocode/json?address=207+N.+Defiance+St%2C+Archbold%2C+OH&sensor=false HTTP/1.1
GET信息后跟着一些请求头,每个请求头包含了名称、冒号、值。最后是请求结束的回车符和换行符
python search4.py
O]
HTTP/1.1 200 OK
Content-Type:
Date: Sat, 23 Nov 2013 18:34:30 GMT
Expires: Sun, 24 Nov 2013 18:34:30 GMT
Cache-Control:public, max-age=86500
Vary: Accept-Language
Access-Control_Allow-Origin: *
Server: mafe
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Alternate-Protocol: 80:quic
Connection: close
{
"results": [
{
...
"formatted_address": "207 North Definace Street, Archbold, OH 43502, USA",
"geometry": {
"location" : {
"lat" : 41.521954,
"lng" : -84.306691
},
...
},
"types" : [ "street_address" ]
}
],
"status": "OK"
}
HTTP响应的结构和HTTP请求类似。首先是状态行,跟着一些响应头,响应头后有一个空行,接着就是响应体(JSON格式~JavaScripts数据结构),此JSON就是之前查询的响应结果,描述了查询谷歌地理编码API返回的地理位置。
这里所有状态和响应头都是使用httplib库处理的底层细节。如果没有没有分层,网络通信的具体细节即为此。
谷歌地理编码API,封装了1)如何用URL表示地理信息查询;2)如何获取包含坐标信息的JSON数据
URL,标识了科通过HTTP获取的文档
HTTP层,支持面向文档的命令(GET),操作使用了原始TCP/IP套接字
TCP/IP套接字,只处理字节串的发送和接收
socket()API层以下的几层:
使用python3操作这两个过程相当简单。
if __name__ == '__main__':
# Translating from the outside world of bytes to Unicode characters.
input_bytes = b'\xff\xfe4\x001\x003\x00 \x00i\x00s\x00 \x00i\x00.\x00'
input_characters = input_bytes.decode('utf-16')
print(repr(input_characters))
# Translating chsracters back into bytes before sending them.
output_characters = 'We copy you down, Eagle.\n'
output_bytes = output_characters.encode('utf-8')
with open ('eagle.txt', 'wb') as f:
f.write(output_bytes)
注意在调用两者的repr()方法时的区别:
为了消除字节串与字符串带来的混淆,Py3只对字符串类型提供了大量的字符串方法。
但两者本质都是允许资源共享的精心设计的机制。
Python程序很少直接操作IP这么底层的协议。
由于纯数字表示的地址不便记忆,人们使用主机名(hostname)来代替IP地址,只要键入google.com就可访问谷歌,其实,它是将主机名解析到了类似74.125.67.103的地址,实际上通过互联网将数据包传输到了该地址。
# 1-7 chapter01/getname.py 主机名转换为IP地址
import socket
if __name__ == '__main__':
hostname = 'www.python.org'
addr = socket.gethostbyname(hostname)
print('The IP address of {} is {}'.format(hostname, addr))
O]The IP address of www.python.org is 151.101.40.223
4字节IP已经不够用,又部署了IPv6的拓展地址机制,允许使用16字节的地址:fe80::fcfd:4aff:fecf:ea4e,只要代码从用户处接收IP地址或主机名,将它们传递给网络库来处理,那么就永远不需要考虑IPv4和v6的区别,运行代码的操作系统会知道使用的IP版本,并作出相应的解析。
一旦程序请求操作系统向某一特定IP地址发送数据,操作系统就需要决定如何使用该机器连接的某一物理网络来传输数据。这一决定就叫路由(routing)
路由只是在网络边缘时才这么容易,Py应用程序很少运行在互联网骨干路由器上,所以实际情况几乎全是简单路由情形。
py代码直接使用主机操作系统体统的功能,去正确选择数据包路由,和之前依靠操作系统来将主机名解析至IP地址是一样的。
IP支持的数据包极大,最大可至64KB,但是构建于IP网络之上的实际网络设备,通常并不支持这么大的数据包,所以分组是必要的。以太网只支持1500B的数据包。
网络数据包中包含一个表示"不分组"(DF,Don't Fragment)的标记,在源计算机与目的计算机之间的某条物理网络无法容纳太大的数据包时,发送者可以通过这个标记选择是否进行分组。
DF标记无法由Py程序控制,由操作系统来设置。-->一开始就一直发小的,接收小的
系统通常使用的逻辑:
一个互联网子网能够接收的最大数据包叫做最大传输单元(MTU),90年代,互联网服务商(DSL链路电话公司)使用PPPoE,PPPoE对IP数据包进行封装,封装后大小只有1492B,不是以太网允许的1500B,使得很多默认1500B的网站措手不及,还使用错误的安全措施,阻塞了所有的ICMP数据包,收不到错误信息,就不知道1500B的数据包到达客户的DSL链路时无法兼容,导致小文件和网页的访问没有问题,Telnet和SSH等交互式协议也都正常,这两种操作发送的数据包都小于1492B。一旦用户尝试下载一个大文件,或者Telnet/SSH一次性大量输出好几个屏幕的信息,那么连接就会被冻结并无法响应。
余下的章节,学习IP层之上的协议,描述IP的官方资源是IETF发布的RFC文档,可以了解到网际协议工作的每个细节,网址:http://tools.ietf.org/html/rfc791
或《TCP/IP详解:协议》
原文:https://www.cnblogs.com/wangxue533/p/11936665.html