这次讲一个很重要的技巧:那就是利用代码模拟登录某些网站。
首先我们重点讲下session以及cookiejar:官方文档
"""A Requests session.
Provides cookie persistence, connection-pooling, and configuration.
Basic Usage::
>>> import requests
>>> s = requests.Session()
>>> s.get('https://httpbin.org/get')
<Response [200]>
Or as a context manager::
>>> with requests.Session() as s:
>>> s.get('https://httpbin.org/get')
<Response [200]>
"""
大意是说session对象具有自动保存cookie等功能
cookiejar:
r"""HTTP cookie handling for web clients.
This module has (now fairly distant) origins in Gisle Aas' Perl module
HTTP::Cookies, from the libwww-perl library.
Docstrings, comments and debug strings in this code refer to the
attributes of the HTTP cookie system as cookie-attributes, to distinguish
them clearly from Python attributes.
Class diagram (note that BSDDBCookieJar and the MSIE* classes are not
distributed with the Python standard library, but are available from
http://wwwsearch.sf.net/):
CookieJar____
/ \ \
FileCookieJar \ \
/ | \ \ \
MozillaCookieJar | LWPCookieJar \ \
| | \
| ---MSIEBase | \
| / | | \
| / MSIEDBCookieJar BSDDBCookieJar
|/
MSIECookieJar
"""
讲下cookiejar和session的关系,cookiejar是底层库,可以形象的把cookiejar看作保存cookie的罐子,cookie里面可以保存很多cookie。session的实现调用了底层cookejar,可以认为session对象能把cookie对象自动保存到cookiejar中
使用session对象十分十分方便,我们首先创造session对象,用requests.session()就可以了,然后后面的所有网络请求全部用session的get,post函数等,而不用requests对象的get,post函数,这样session对象能自动保存访问过的网页设置的cookie
理论上我们使用session是不需要知道cookiejar是什么东西的,因为设计requests的人早就帮我们封装好了一切。这里我还是讲下cookiejar的用法
from http import cookiejar
from urllib import request
cookie_jar = cookiejar.CookieJar()
cookie_handler = request.HTTPCookieProcessor(cookie_jar)
cookie_opener = request.build_opener(cookie_handler)
# cookiejar保存cookie
cookie_opener.open('登陆页面网址', timeout=10, data={'登陆的表单数据,字典格式'})
# 用以及保存了cookie的opener对象去访问别的网页
response = cookie_opener.open('需要登陆后才能访问的网页')
我们再次感受到requests库的简便与强大。。我们这次爬取还是用session
我们在使用爬虫的时候经常会遇到某些必须要求我们登陆才能获取到信息的情况,比如淘宝商品信息的爬取。
这时候我们一般有两种方式,第一种是手动设置cookies,具体见上一篇文章;
这篇文章里我们介绍下另外一种方法:代码实现自动抓取cookies然后模拟登陆,这样做有一个好处,只要你输入正确的账号密码,就可以直接获取到你想要获取的信息。
废话不多说,我们直接开始,然后这次我们选择的目标网站是我的母校的教务处网站,我准备从中查询到成绩和课表信息:
我们首先要人工登陆,注意在登陆之前(就是输入账号密码的那个网页)打开开发者工具:我们把这个preserve log 勾选上,这个的意思是保存历史信息,就是说跳转到新的url后原来界面的network里面的元素还在而不会刷新掉。
找到all选项下的一个是POST请求的元素(一般在前面),把详细信息拉到最下面会有一个form表单,这是post请求的参数:
其中username和password就是你刚刚输入的账号和密码,下面还有些参数,有的需要有的不需要。
这些是为了安全性创造的一些参数,尤其是lt参数,其实就是一个随机数,每次刷新都不一样,但是lt参数你错一个字母都无法成功登陆,execution参数是你登陆了几次的意思,如果你账号密码输入错误他就会增加,比如从e1s1变成了e2s2可能,然后你一直错,它一直加,最后可能在错误的次数达到一定程度后就给你上验证码了,所以我们最好一次就成功登陆。
所以我第一个想啊就是先让程序抓取这个登陆页面的lt后,利用这个lt再去登陆。
但是有个小问题:随着我每次刷新,lt和execution都是在动态变化的,这肯定不行(因为你抓取第一次,相当于第一次访问登陆页面,他给你第一次的lt,你再用第一次的lt去登陆的时候,,别人的lt在你登陆的过程中就变化了,生成了第二次的lt),那我们要怎么办呢? 以及它是根据什么判断我们是第几次访问到登陆页面的??
答案就是 cookies
我们在每次进入到登陆界面的网站,它会给我们设置一个cookies来判断我们是第几次访问,如果我们账号密码输入错误,它的cookes也会变化,从而导致lt变化。
也就是说,只要我们在获取到lt后,用当时获取到lt的cookies去发送登录请求,就能正常登陆:
我们先看看cookie与lt的关系:
第二次请求:
从这里我们就可以看出cookie与lt的关系,可以这么简单的理解:一个cookie对应一个lt。
然后我们只要用前面请求lt的cookie去模拟登录,它的lt就不会“刷新”。
之后我们利用lt和其他参数拼接成的data字典传给post请求的data参数,生成网页导入本地,打开后是这样:就代表你成功登陆了,可以保存登陆后的cookies。
代表哪里出了问题,要么是data参数不正确还是啥的,这样没成功登陆是保存不了cookies的。 这样,获取了登陆后的cookies后,我们就可以用保存了cookies的session对象继续去访问里面的网站,比如成绩网站:我们进去后查看源网页,随便搜索一门课的成绩:
没有任何数据。这样的网站我们已经见过很多了,前面的搜狗百度图片,都是这样的,称为瀑布流式网页,Ajax动态网页,我们利用开发者工具,抓取数据:
在XHR项一个个找,看哪个是返回给我们的数据,最后发现最下面的xscjx.do是成绩数据:
欧克,找到目标url了,继续爬取:
人傻了,我们不是登陆了吗?怎么回事?
所以我们还要用session再登陆下这个成绩网页保存cookies:
然后再获取数据就大功告成:
注意ajax的那个网页返回的数据是json格式的。
这种最简单搞了,直接转成python字典,然后需要啥爬啥。
简单处理下:
当然你可以导入scv表格,做GUI界面,反正数据都搞到了,做啥看兴趣了。
比如你可以设置输入账号密码:
课表的话类似吧,,以前没搞过,现在试试:
这个可能代表是第几学期的课表,post下就欧克了。 结果完全没问题昂:
当然数据获取到了,你怎么去使用就看你的兴趣了。
谢谢观看@**@
源代码:
# coding='utf-8'
import requests
from lxml import etree
class Spider:
def __init__(self, url='', path=''):
self.url = url if url else ''
self.path = path if path else ''
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
}
def get_response(self, url):
return self.session.get(url, headers=self.headers)
# 模拟登录,保存cookie
def post_response(self, url, data):
return self.session.post(url, headers=self.headers, data=data)
def get_etree(self, xml):
return etree.HTML(xml)
def parse_data(self, target, fmt):
return target.xpath(fmt)
def set_data(self, name='', psw=''):
self.data = {
'username': name if name else '17030140051',
'password': psw if psw else 'ForeverLove520',
'lt': self.lt,
'execution': 'e1s1',
'_eventId': 'submit',
'rmShown': '1',
}
def set_lt(self, url):
response = self.get_response(url)
target = self.get_etree(response.content.decode('utf-8'))
result_list = self.parse_data(target, r'//input[@name="lt"]')
self.lt = result_list[0].get('value')
def start(self):
log_in_url = 'http://ids.xidian.edu.cn/authserver/login?service=http%3A%2F%2Fehall.xidian.edu.cn%2Flogin%3Fservice%3Dhttp%3A%2F%2Fehall.xidian.edu.cn%2Fnew%2Findex.html'
self.session = requests.session()
# 给的参数是登陆网页,作用是抓取动态lt,同时保存cookies
self.set_lt(log_in_url)
name = input('输入学号:')
psw = input('输入密码:')
self.set_data(name, psw)
# 模拟登录
response = self.post_response(log_in_url, self.data)
# 这步必须要,成绩网站也给我们设置cookies了
# 我们首先要获取成绩查询所在的网址
# 这步真的搞哭我了....................一把辛酸泪
'''
# 这个可以用来搞到所有的可用的app
all_useable_url = 'http://ehall.xidian.edu.cn/jsonp/getUserAllUsableApp?searchKey=&_=1585880169428'
response = self.get_response(all_useable_url)
all_apps = response.json()['data']
for each in all_apps:
# 查成绩:
if each['appName'] == '成绩查询':
score_appid = each['appId']
break
'''
score_appid = '4768574631264620'
# 只需要访问这个临时的网站我们就可以获得cookies,然后去申请成绩数据
temp_url = 'http://ehall.xidian.edu.cn//appShow?appId={}'.format(score_appid)
response = self.get_response(temp_url)
# 查成绩的json url,开发者工具抓取
self.url = 'http://ehall.xidian.edu.cn/jwapp/sys/cjcx/modules/cjcx/xscjcx.do'
response = self.get_response(self.url)
score_list = response.json()['datas']['xscjcx']['rows']
print('\n\n成绩:\n')
for each in score_list:
print(each['KCM'], ":", each['ZCJ'])
print('\n\n\n____________________________\n\n\n课表:\n')
course_end_url = 'http://ehall.xidian.edu.cn/jwapp/sys/wdkb/modules/xskcb/xsllsykb.do'
response = self.post_response(course_end_url, {'XNXQDM': '2019-2020-2'})
course_list = response.json()['datas']['xsllsykb']['rows']
for each in course_list:
print(each['KCM'], ':', each['YPSJDD'])
Spider().start()
'''
from http import cookiejar
from urllib import request
cookie_jar = cookiejar.CookieJar()
cookie_handler = request.HTTPCookieProcessor(cookie_jar)
cookie_opener = request.build_opener(cookie_handler)
# cookiejar保存cookie
cookie_opener.open('https://image.baidu.com', timeout=10, data={'键':'值'})
# 用以及保存了cookie的opener对象去访问别的网页
response = cookie_opener.open('需要登陆后才能访问的网页')
'''
'''
from http import cookiejar
from urllib import request
cookie_jar = cookiejar.CookieJar()
cookie_handler = request.HTTPCookieProcessor(cookie_jar)
cookie_opener = request.build_opener(cookie_handler)
# cookiejar保存cookie
cookie_opener.open('https://image.baidu.com', timeout=10, data={'键':'值'})
# 用以及保存了cookie的opener对象去访问别的网页
response = cookie_opener.open('需要登陆后才能访问的网页')
'''