原因是,当我们第一次输入账号密码后,服务器会返回给我们一个 Cookie 值,而之后再返回该网页的其它跳转网页时,客户端会将该 Cookie 值绑在请求上,发送给服务器,这样服务器就可以利用 Cookie 来判别我们是哪一个用户,找到会话,然后判别登陆状态,若状态为登陆,则不需要用户重新输入用户名密码来进行登陆。 这次我们需要做的就是利用 Python 进行模拟登陆,实现跨页面访问而不需要重新输入用户名及密码。 1. 首先,分析登陆页面打开登陆页面:https://www.github.com/login 打开开发者工具,分析请求,找到对应的请求头,用以构建爬虫的请求头。 一般使用Host和User-Agent两个参数即可伪装成浏览器;构建请求头如下:
self.headers = { 'Host': 'github.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0;Win64;x64;rv:62.0) Gecko/20100101 Firefox/62.0', } # User-Agent必须连在一起,中间不能出现空格,如‘User - Agent’(两天找不到错最后是因为多打了两个空格,哭)
这里存在一个问题:使用 Chrome 的 User-Agent 来获取源码中的 token 值时,返回的列表为空;而使用火狐浏览器的 User-Agent 时,返回的列表是我们想要的内容。这里可以发现,服务器对不同 User-Agent 的返回值是不同的,至于为什么?还需要进一步探索。 2. 接着,分析点击登陆之后的页面我们在登陆之后,访问其它页面,客户端发送给服务器 Cookie 值和表单,在服务器端找到会话,进而继续访问;所以我们模拟登陆需要的是模拟 Cookie(request.Session()函数)和表单。 Step1: 查看cookie 发现第一次登陆后,响应头会返回 Set-Cookie 值,即设置 Cookie 返回给客户端,创建一个会话 session。 Step2: 分析登陆后页面的请求头 发现其与登陆页面的请求头是一致的,所以我们在请求登陆页面和 github 个人详情页面时,可以使用同一个请求头。 Step3: 分析登陆后页面的 Form-Data (浏览器发送给服务器的表单) Form-Data 体中含有用户名和密码,是客户端给服务器发送的表单信息,利用这些信息,可判断是在哪个会话中。 发现其中: commit、utf8 其属性的值每次访问都是不变的 login、password 的值分别是我们登陆所用的账户名和密码 而 authenticity-token 每次访问登陆时,其值都是变化的,我们无法人为输入,需要从源中获得;发现它藏在登陆页面的源码中
这里介绍一个快速查找源码中关键字的方法: 在该界面中选中之后,按Ctrl+F,在箭头执行的输入框内输入我们想要查找的关键字,即可得到结果。 Step4: 获取 authenticity_token 的值 获取xpath: 非常方便! 解析源码,获取 authenticity_token 的值:这里我们用到 lxml 里的 etree 模块,即利用 xpath 来获取信息。 代码如下: def get_token(self): response = self.session.get(url=self.login_url, headers=self.headers) selector = etree.HTML(response.text) # 解析HTML对象 print(selector.xpath('//*[@id="login"]/form/input[2]/@value')) # 提取所有id属性为login元素下的form元素的第二个input元素的value属性所对应的值 token = selector.xpath('//*[@id="login"]/form/input[2]/@value') return token
Step5: 构建 form-data 头 form_data = { 'commit': 'Sign in', 'utf8': '✓', 'authenticity_token': self.get_token(), 'login': email, 'password': passwd }
3. 全部代码如下import requests from lxml import etree
USER = '***' # 隐藏的用户名和密码,哈哈 PASSWORD = '******'
# 创建Login类,封装模拟登陆 class Login(object): def __init__(self): self.headers = { 'Host': 'github.com', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0;Win64;x64;rv:62.0) Gecko/20100101 Firefox/62.0', # 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Mobile Safari/537.36', # 使用谷歌浏览器的User-Agent时,返回的token为空列表,但使用火狐则成功,这是为什么? } self.login_url = 'https://www.github.com/login' # 登陆页面的URL self.profile_url = 'https://www.github.com/settings/profile' # 个人详情页URL self.session = requests.Session() # requests.Session()帮我们维持一个会话,自动处理Cookie
# 获取authentici_token def get_token(self): response = self.session.get(url=self.login_url, headers=self.headers) selector = etree.HTML(response.text) # 解析HTML对象 print(selector.xpath('//*[@id="login"]/form/input[2]/@value')) # 提取所有id属性为login元素下的form元素的第二个input元素的value属性所对应的值 token = selector.xpath('//*[@id="login"]/form/input[2]/@value') return token
# 登陆至个人详情页,获取信息 def login(self, email, passwd): form_data = { 'commit': 'Sign in', 'utf8': '✓', 'authenticity_token': self.get_token(), 'login': email, 'password': passwd } response = self.session.get(url=self.profile_url, data=form_data, headers=self.headers) print(response.status_code) if response.status_code == 200: self.dynamics(response.text)
response = self.session.post(url=self.profile_url, headers=self.headers) if response.status_code == 200: self.profile(response.text)
def dynamics(self, html): pass
def profile(self, html): pass
if __name__ == '__main__': login = Login() login.login(USER, PASSWORD)
是不是很有趣,好了今天就到这里吧!See You! 本篇图文由团队 郭维强 同学提供,他的 CSDN Blog 地址为: https://blog.csdn.net/qq_43271202
参考图文
经过8年多的发展,LSGO软件技术团队在地理信息系统、数据统计分析、计算机视觉领域积累了丰富的研发经验,也建立了人才培养的完备体系。
|