模拟登录的原理
通常情况下,用户通过浏览器登录网站时,在特定的登录界面,输入个人登录信息,提交之后便能返回一个包含数据的网页。在浏览器层面的机制是,浏览器提交包含必要信息的http Request,服务器返回http Response。其中HTTP Request内容包括下面5项:
URL=基本的URL+可选的查询字符串
Request Headers:必须或可选
Cookie:可选
Post data:当时POST方法时需要
http Response的内容包括以下两项: Html源码或者图片,json字符串等
Cookies:后续的访问中如果需要cookie,返回内容中则会包含cookie
其中URL是统一资源定位符的简称,是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,包括主机部分、文件路径部分;Request Headers是向服务请求信息的头信息,包含编码格式、用户代理、提交主机和路径等信息;Post data是指提交的用户、内容、格式参数等。Cookies是服务器发送给浏览器的、保存在本地的、服务器用来识别用户的文件,以及用来判断用户是否合法以及一些登录信息。
网页抓取的原理
如上所述,模拟登录之后,网站服务器会返回html文件,html是带有标签的有较为严格语法和格式的文本文件,不同的标签之间有不同的内容,可根据相关标签以及数据特征,利用正则表达式抓取需要的数据或者代表数据的可进一步挖掘的链接。
模拟登录的实现过程
1、获取所需要的参数 IE浏览器为开发者提供了强大的工具,获取参数的流程为:
打开浏览器
输入网址
开发人员工具
网络启用
网络流量捕获
输入密码和账号登录
找到第一条发起程序为“单击”的记录
详细信息
“请求标头”和“请求正文”
请求标头和请求正文中包含着客户端和浏览器进行交互的参数。其中的一些参数是默认的,不需要设置;有些参数是和用户本身相关的,如用户名、密码、是否记住密码等;
有些参数是客户端和服务器交互产生的。确定参数的步骤是,首先,从字面意思理解,其次在交互记录中搜索参数名,观察参数是在哪个过程产生的。在请求正文看到的部分参数是经过编码的,需要解码。
2、 获取登录百度账号需要的参数
按照上文的步骤,使用IE9浏览器的自带工具,轻松获得相关参数。其中staticpage是跳转页,解码之后或者在“摘要”记录中得知http://www.baidu.com/cache/user/html/v3jump .html。经过搜索,得知token参数首先出现在URL为https://passport.baidu.com/v2/api/?login的响应正文中,需要在返回的页码中抓取;apiver参数设置返回的文本是json格式还是html格式。在请求标头中除了有常规设置参数,还有cookie,在登录过程中需要获取cookie,以便和服务器交互。
3、 登录的具体的代码实现
3.1 导入登录过程使用的库
import re;
mport cookielib;
import urllib;
import urllib2;
其中re库是解析正则表达式,并进行抓取匹配;cookielib库获取并管理cookies;urllib和urllib2库是根据URL、post data参数想服务器请求并解码数据。
3.2 cookie检测函数
通过检测cookiejar返回的cookie键与cookieNameList是否完全匹配来判断是否登录成功。
def checkAllCookiesExist(cookieNameList, cookieJar) :
cookiesDict = {};
for eachCookieName in cookieNameList :
cookiesDict[eachCookieName] = False;
allCookieFound = True;
for cookie in cookieJar :
if(cookie.name in cookiesDict.keys()) :
cookiesDict[cookie.name] = True;
for Cookie in cookiesDict.keys() :
if(not cookiesDict[eachCookie]) :
allCookieFound = False;
break;
return allCookieFound;
3.3 模拟登录百度
def emulateLoginBaidu():
cj = cookielib.CookieJar(); .
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj));
urllib2.install_opener(opener);
创建一个cookieJar对象,用来保存cookies,并和一个http处理器绑定并打开安装。
print "[step1] to get cookie BAIDUID";
baiduMainUrl = "http://www.baidu.com/";
resp = urllib2.urlopen(baiduMainUrl);
打开百度主页,并获取cookie BAIDUID。
print "[step2] to get token value";
getapiUrl = "https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true";
getapiResp = urllib2.urlopen(getapiUrl);
getapiRespHtml = getapiResp.read();
print "getapiResp=",getapiResp;
IsfoundToken= re.search("bdPass\.api\.params\.login_token='(?P\w+)';",
getapiRespHtml);
以上程序用于获取post data中的token参数,首先获得getapiUrl 网址的html,然后用re标准库中的search函数搜索匹配,返回一个标志是否匹配成功的布尔值。
if(IsfoundToken):
tokenVal = IsfoundToken.group("tokenVal");
print "tokenVal=",tokenVal;
print "[step3] emulate login baidu";
staticpage = "http://www.baidu.com/cache/user/html/v3jump.html";
baiduMainLoginUrl = "https://passport.baidu.com/v2/api/?login";
postDict = {
'charset' : "utf-8",
'token' : tokenVal,
'isPhone' : "false",
'index' : "0",
'safeflg' : "0",
'staticpage' : staticpage,
'loginType' : "1",
'tpl' : "mn",
'username' : "username",
'password' : "password",
'mem_pass' : "on",
}[3];
设置postdata参数值,并不是每个参数都需要设置,部分参数是默认的。
postData = urllib.urlencode(postDict);
将postdata进行编码,例如http://www.baidu.com/cache/user/html/v3jump.html编码的结果是http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fjump.html,其它参数类似。
req = urllib2.Request(baiduMainLoginUrl, postData);
resp = urllib2.urlopen(req);
Python标准库urllib2的两个函数分别提交用户请求及数据,并接受返回数据
cookiesToCheck = ['BDUSS', 'PTOKEN', 'STOKEN', 'SAVEUSERID','UBI','HISTORY',' USERNAMETYPE'];
抓取网页的实现过程
上文中使用正则表达式在返回的网页中成功抓取token,Python的标准库HTMLParser提供了强大的识别html文本标签和数据的函数,当在使用的时候就从HTMLParser派生出新的类,然后重新定义这几个以handler_开头的函数,常用的几个函数包括:
handle_startendtag 处理开始标签和结束标签
handle_starttag 处理开始标签,比如
handle_endtag 处理结束标签,比如
handle_charref 处理特殊字符串,就是以开头的,一般是内码表示的字符
handle_entityref 处理一些特殊字符,以&开头的,比如
handle_data 处理数据,就是data中间的那些数据
以下程序用来抓取百度贴吧电影吧帖子的标头,作为示范:
import HTMLParser
import urllib2
url='http://tieba.baidu.com/f?ie=utf-8&kw=电影&fr=ibaidu'
html=ullib2.urlopen(url).read().deode(gbk)
class myHTMLParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.flag=0
def handle_starttag(self,tag,attrs):
if tag=="a":
for (name,value) in attrs:
if name="class"&value=j_th_tit:
self.flag=1
def handle_data(self,data):
if self.flag=1:
print(data)
if __name__=='__main__':
myhtmlparse=myHTMLParser()
myhtmlparse.feed(a)