前面写的代码虽然完成了爬取的功能,但是过于凌乱,于是打算重构一遍。首先从登陆开始
改进前的代码 面向过程 这是第一次写的登录函数,获取登录信息和登录本身是放在一起的。
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() 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 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 if (login_data_parser!=None and callable(login_data_parser)): login_data,cookies=login_data_parser(login_url) 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 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