一、http协议简介
在进入http协议网络编程之前,我们先来对HTTP协议进行简单的介绍。
早期的软件基本上都是运行在大型机上,软件的使用者则通过哑终端来控制使用计算机资源,在PC技术不断普及后,软件使用者所操作的软件通常运行在PC机上,这种类型的软件架构,称为C/S架构。在后续随着互联网技术的兴起,这种类型的软件架构并不适用互联的应用,因此一种新的软件架构产生了,这就是当前比较流行的B/S架构。
在web应用中,服务器把网页数据传给客户端,实际上就是把HTML代码传给客户端,在这个传输的过程中http协议作为传输控制协议。这是一个基于“请求、响应”模型的协议,即服务端收到客户端的请求后便会给出相应的响应,即使这个响应是拒绝为客户端提供服务。这话比较拗口,我们通过抓包方式来实际展示浏览器和服务器之间的数据交付,以用户打开浏览器并在浏览器中输入:http://www.baidu.com为例(下文是wireshark跟踪TCP流时显示打印信息):
------这部分表示客户端向服务端发起请求: 客户端希望能从服务端获取到www.baidu.com的数据;
GET /su?wd=www.baidu.com&action=opensearch&ie=UTF-8&from=ie8 HTTP/1.1
Accept: */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; MALNJS)
Accept-Encoding: gzip, deflate
Host: suggestion.baidu.com
DNT: 1
Connection: Keep-Alive
Cookie: BAIDUID=0D9B36937CB6DC69ABD369FAEDBDFC9E:FG=1; BIDUPSID=CF04BDC232B7E45C5DBB5076EDFCB4B3; BDUSS=RNMUFEaHl1TDF5a0w4d0dwb0NEY21kNGQwWlVYQWRrMnVCMjVDcEFhZVBqMUJWQVFBQUFBJCQAAAAAAAAAAAEAAAB4g69GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI8CKVWPAilVRj; BDRCVFR[Z5ezkSKcPHm]=9xWipS8B-FspA7EnHc1QhPEUf; H_PS_PSSID=13457_1467_13719_13075_10812_12867_13322_12691_13692_10562_12723_13761_13781_11 599_13610_13086_8498
------这部分表示服务端收到客户端的请求后,给出了一个 200 ok的响应,当然可能还有其他的类型的响应,比如3xx表示重定向,4xx表示客户端发送的请求有错误,5xx表示服务器端处理时发生了错误等等。
HTTP/1.1 200 OK
Date: Mon, 04 May 2015 13:20:13 GMT
Server: suggestion.baidu.zbb.df
Content-Length: 122
Content-Type: text/javascript; charset=UTF-8
Cache-Control: private
Expires: Mon, 04 May 2015 14:20:13 GMT
Content-Encoding: gzip
Connection: Keep-Alive
关于HTTP协议,还有太多的东西,要详细了解这部分,可参考《HTTP权威指南》。
二、HTTP协议网络编程
1)、从http服务器上下载数据
python中通过httplib库,我们很容易的实现http客户端模拟,以下面代码为例:
import httplib host ='www.baidu.com' httpclient = httplib.HTTPConnection(host, 80, timeout=30) httpclient.request('GET', '/') response = httpclient.getresponse()
这段代码运行结果如下:
<!DOCTYPE html><!--STATUS OK-->
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="dns-prefetch" href="//s1.bdstatic.com"/>
<link rel="dns-prefetch" href="//t1.baidu.com"/>
<link rel="dns-prefetch" href="//t2.baidu.com"/>
<link rel="dns-prefetch" href="//t3.baidu.com"/>
<link rel="dns-prefetch" href="//t10.baidu.com"/>
<link rel="dns-prefetch" href="//t11.baidu.com"/>
<link rel="dns-prefetch" href="//t12.baidu.com"/>
<link rel="dns-prefetch" href="//b1.bdstatic.com"/>
<title>百度一下,你就知道</title>
从代码上看,httplib模拟出来的客户端和使用urllib、urllib2的模拟客户端的代码很相似,于是会有人产生不解,httplib和urllib、urllib2有啥区别,实质上,区别仅在于urllib2是urllib的增强版,提供了比urllib更强大的功能,urllib与urllib2相比httolib提供了更高层次的封装,很多http本身的细节在urllib和urllib2库中已经被屏蔽掉了。
2)、模拟浏览器
先来看不模拟浏览器的python代码运行效果,代码:
import urllib import urllib2 url = 'http://www.qiushibaike.com/hot/page/1' # headers = {'User-Agent':'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; MALNJS)'} try: # request = urllib2.Request(url) request = urllib2.Request(url) response = urllib2.urlopen(request) print response.read() except urllib2.URLError, e: if hasattr(e,"code"): print e.code if hasattr(e,"reason"): print e.reason
程序运行结果出错,如下:
Traceback (most recent call last):
File "D:/工作学习/程序设计/python程序设计/python web开发学习实录/python模拟浏览器.py", line 10, in <module>
response = urllib2.urlopen(request)
File "D:\应用软件\软件安装\python\lib\urllib2.py", line 127, in urlopen
return _opener.open(url, data, timeout)
使用代码模拟流浪器访问http服务器,代码如下:
import urllib import urllib2 url = 'http://www.qiushibaike.com/hot/page/1' headers = {'User-Agent':'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; MALNJS)'} try: request = urllib2.Request(url,headers = headers) response = urllib2.urlopen(request) print response.read() except urllib2.URLError, e: if hasattr(e,"code"): print e.code if hasattr(e,"reason"):
程序运行结果如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1,IE=edge">
<meta property="wb:webmaster" content="cb81e216cbe9a3b9" />
……
3)、 使用post方式提交数据:
以登录http://weibo.kedacom.com为例,对应代码如下:
import urllib import urllib2 values = {"email":" [email protected]","password":"xxxxxxx "} data = urllib.urlencode(values) url = " http://weibo.kedacom.com " # request = urllib2.Request(url,data) headers = {'User-Agent':'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; MALNJS)'} request = urllib2.Request(url,data,headers = headers) response = urllib2.urlopen(request)
4)、 提取访问网站后的cookie模拟网站登录
cookie是客户端在访问服务器时,服务器返回给客户端一个标识,利用cookie服务器能有效地对客户端的身边进行识别,使用cookie访问网站的过程中,cookie都是作为客户端头部信息的一部分,现用一个写得比较菜的代码来加以说明,当然这里仅仅是为了说明模拟登陆网站的原理:
import urllib import urllib2 import cookielib headers={ 'Referer': 'http://weibo.kedacom.com/weibo/login', 'User-Agent': 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0; MALNJS)', } values={'username':'[email protected]', 'password':'xxxxxx', 'encode':'false', 'rememberMe':'true' }
url='http://weibo.kedacom.com/weibo/login/weiboLogin' filename = 'cookie.txt' #声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件 cookie = cookielib.MozillaCookieJar(filename) opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie)) request = urllib2.Request(url,postdata,headers) result = opener.open(request) #保存cookie到cookie.txt中 cookie.save(ignore_discard=True, ignore_expires=True) url1='http://weibo.kedacom.com/weibo/j_spring_security_check?j_password_encoder=false&j_username=zhongside%40kedacom.com&j_password=888888&j_login_type=weibo&_spring_security_remember_me=true' result = opener.open(url1) cookie.save(ignore_discard=True, ignore_expires=True) url2='http://weibo.kedacom.com/weibo/login/success' result = opener.open(url2) cookie.save(ignore_discard=True, ignore_expires=True) url3='http://weibo.kedacom.com/weibo/home' result = opener.open(url3) print result.read()