python爬虫学习笔记4模拟登录函数的优化

前面写的代码虽然完成了爬取的功能,但是过于凌乱,于是打算重构一遍。首先从登陆开始

改进前的代码

面向过程

这是第一次写的登录函数,获取登录信息和登录本身是放在一起的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def login():
"""
登录并返回已经登录的会话
:return: 已经登录的会话(session)
"""
#设置
login_url = 'http://ids.chd.edu.cn/authserver/login?service=http%3A%2F%2Fportal.chd.edu.cn%2F'
headers={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36',
}
#新建会话
session=requests.session()

#获取登录校验码
html=session.post(login_url,headers=headers).text
soup=BeautifulSoup(html,'lxml')
lt=soup.find('input',{'name':'lt'})['value']
dllt=soup.find('input',{'name':'dllt'})['value']
execution = soup.find('input', {'name': 'execution'})['value']
_eventId = soup.find('input', {'name': '_eventId'})['value']
rmShown = soup.find('input', {'name': 'rmShown'})['value']
login_data={
'username': input("请输入学号:"),
'password': input("请输入密码:"),
'btn':'',
'lt': lt,
'dllt': dllt,
'execution': execution,
'_eventId': _eventId,
'rmShown': rmShown
}

#登录
response=session.post(login_url,headers=headers,data=login_data)
if response.url=='http://portal.chd.edu.cn/':
print('登录成功!')

return session

面向对象

第二次是将全部函数封装到类中,这次将获取登录信息从其中分出来。但是两者关系仍然太过于紧密。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class spider:
'''
爬虫类
'''

def __init__(self,headers):
self.session=requests.session()#初始化登录session
self.is_login=False#登录状态
self.headers=headers#头信息
self.cookiejar=http.cookiejar.LWPCookieJar('cookie.txt')

def get_login_data(self,login_url):
'''
获取登录需要的数据
:param login_url: 登录页面url
:return: 一个存有登录数据的字典
'''
# 获取登录校验码
html = self.session.post(login_url, headers=self.headers).text
soup = BeautifulSoup(html, 'lxml')
lt = soup.find('input', {'name': 'lt'})['value']
dllt = soup.find('input', {'name': 'dllt'})['value']
execution = soup.find('input', {'name': 'execution'})['value']
_eventId = soup.find('input', {'name': '_eventId'})['value']
rmShown = soup.find('input', {'name': 'rmShown'})['value']
login_data = {
'username': input("请输入学号:"),
'password': input("请输入密码:"),
'btn': '',
'lt': lt,
'dllt': dllt,
'execution': execution,
'_eventId': _eventId,
'rmShown': rmShown
}
return login_data

def login(self,login_url):
"""
登录并返回已经登录的会话
:return: 已经登录的会话(session)
"""
if self.load_cookie():
self.is_login = True

else:

#获取登录信息
login_data=self.get_login_data(login_url)

# 登录
response = self.session.post(login_url, headers=self.headers, data=login_data)
if response.url!=login_url:
print("登录成功")
self.is_login=True
self.save_cookie()
else:
print("登录失败")
return self.session

#省略后面的函数

改进

这次改进,我打算让login()函数与获取登录信息用的函数关系没有那么紧密,让后者可以被替换或者不用。

所以使用了回调函数,也就是将函数指针作为参数传入,不过 python 变量本身就像指针一样,直接传变量即可。

函数头

1
def login(self,login_url,login_data_parser=None,target_url=None):

传入了三个参数,

  • login_url : 显而易见,这是登录页面的 url
  • login_data_parser : 这是一个函数,用于解析页面中随机生成的隐藏域代码的函数,可以不传入
  • target_url : 用于判断是否登录成功,这是登录之后会跳转到的页面

获取登录信息

接着判断参数是否为函数(是否可调用),如果可以调用,就调用它获取登录信息。在这里不需要关心函数内部具体如何获取,而只用关心它的接口。

这个函数的返回值是一个装有登录信息的 dict,和一个 cookies。

1
2
3
4
5
def login(self,login_url,login_data_parser=None,target_url=None):
login_data=None
#get the login data
if(login_data_parser!=None and callable(login_data_parser)):
login_data,cookies=login_data_parser(login_url)

登录

然后就完成了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def login(self,login_url,login_data_parser=None,target_url=None):
'''
login
:param login_url: the url you want to login
:param login_data_parser: a callback function to get the login_data you need when you login,return (login_data,response.cookies)
:param target_url: Used to determine if you have logged in successfully

:return: response of login
'''
login_data=None

#get the login data
if(login_data_parser!=None and callable(login_data_parser)):
login_data,cookies=login_data_parser(login_url)

#login
response=requests.post(login_url,headers=self.headers,data=login_data,cookies=cookies)

if(target_url!=None and response.url==target_url):
print("login successfully")

self.cookies=cookies
return response

获取登录信息函数

这个和前面就是一样的了。只要修改传给 login 函数的函数,就可以获取不同网站的登录信息。login 函数变得更加通用了,不再过于依赖登录信息函数存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def chd_login_data_parser(self,url):
'''
This parser is for chd
:param url: the url you want to login
:return (a dict with login data,cookies)
'''
response=requests.get(login_url)
html=response.text
# parse the html
soup=BeautifulSoup(html,'lxml')
lt=soup.find('input',{'name':'lt'})['value']
dllt=soup.find('input',{'name':'dllt'})['value']
execution = soup.find('input', {'name': 'execution'})['value']
_eventId = soup.find('input', {'name': '_eventId'})['value']
rmShown = soup.find('input', {'name': 'rmShown'})['value']
login_data={
'username': input('input account:'),
'password': input('input passwd:'),
'btn':'',
'lt': lt,
'dllt': dllt,
'execution': execution,
'_eventId': _eventId,
'rmShown': rmShown
}
return login_data,response.cookies
作者

憧憬少

发布于

2019-04-12

更新于

2019-04-12

许可协议