模拟登陆——以github为例

1.分析登录过程

我们以github为例,分析登录的过程

打开 GitHub 的登录页面,链接为 https://github.com/login ,输入 GitHub 的用户名和密码,打开开发者工具,将 Preserve Log 选项勾选上,这表示显示持续日志,如下图所示。

在这里插入图片描述

点击登录按钮,这时便会看到开发者工具下方显示了各个请求过程,如下图所示。

在这里插入图片描述
点击第一个请求,进入其详情页面,如下图所示。

在这里插入图片描述

可以看到请求的 URL 为 https://github.com/session ,请求方式为 POST。再往下看,我们观察到它的 Form Data 和 Headers 这两部分内容:

在这里插入图片描述

Headers 里面包含了 Cookies、Host、Origin、Referer、User-Agent 等信息。Form Data 包含了 5 个字段,commit 是固定的字符串 Sign in,utf8 是一个勾选字符,authenticity_token 较长,其初步判断是一个 Base64 加密的字符串,login 是登录的用户名,password 是登录的密码。

综上所述,我们现在无法直接构造的内容有 Cookies 和 authenticity_token。下面我们再来探寻一下这两部分内容如何获取。

在登录之前我们会访问到一个登录页面,此页面是通过 GET 形式访问的。输入用户名密码,点击登录按钮,浏览器发送这两部分信息,也就是说 Cookies 和 authenticity_token 一定是在访问登录页的时候设置的。

这时再退出登录,回到登录页,重新访问登录页,截获发生的请求:

在这里插入图片描述
访问登录页面的请求如图所示,Response Headers 有一个 Set-Cookie 字段。这就是设置 Cookies 的过程。

另外,我们发现 Response Headers 没有和 authenticity_token 相关的信息,所以可能 authenticity_token 还隐藏在其他的地方或者是计算出来的。我们再从网页的源码探寻,搜索相关字段,发现源代码里面隐藏着此信息,它是一个隐藏式表单元素,如下图所示:

在这里插入图片描述
现在我们已经获取到所有信息,接下来实现模拟登录。

2.代码实现

首先我们定义一个 Login 类,初始化一些变量:

class Login(object):
    def __init__(self):
        self.headers = {
    
    
            'Referer': 'https://github.com/',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
            'Host': 'github.com'
        }
        self.login_url = 'https://github.com/login'
        self.post_url = 'https://github.com/session'
        self.logined_url = 'https://github.com/settings/profile'
        self.feed_url = 'https://github.com/dashboard-feed'
        self.session = requests.Session()

这里最重要的一个变量就是 requests 库的 Session,它可以帮助我们维持一个会话,而且可以自动处理 Cookies,我们不用再去担心 Cookies 的问题。

接下来,访问登录页面要完成两件事:一是通过此页面获取初始的 Cookies,二是提取出 authenticity_token

在这里我们实现一个 token() 方法,如下所示:

def token(self):
        response = self.session.get(self.login_url, headers=self.headers)
        selector = pq(response.text)
        token = selector('input[name="authenticity_token"]').attr('value')

我们用 Session 对象的 get() 方法访问 GitHub 的登录页面,然后用 XPath 解析出登录所需的authenticity_token` 信息并返回。

现在已经获取初始的 Cookiesauthenticity_token,开始模拟登录,实现一个 login() 方法,如下所示:

def login(self, email, password):
    post_data = {
    
    
        'commit': 'Sign in',
        'utf8': '✓',
        'authenticity_token': self.token(),
        'login': email,
        'password': password
    }

    response = self.session.post(self.post_url, data=post_data, headers=self.headers)
    response = self.session.get(self.feed_url, headers=self.headers)
    if response.status_code == 200:
        self.dynamics(response.text)

    response = self.session.get(self.logined_url, headers=self.headers)
    if response.status_code == 200:
        self.profile(response.text)

首先构造一个表单,复制各个字段,其中 email 和 password 是以变量的形式传递。然后再用 Session 对象的 post() 方法模拟登录即可。现在的github 关注人动态是通过ajax加载的,要向’https://github.com/dashboard-feed’ 发送请求。由于 requests 自动处理了重定向信息,我们登录成功后就可以直接跳转到首页,首页会显示所关注人的动态信息,得到响应之后我们用 dynamics() 方法来对其进行处理。接下来再用 Session 对象请求个人详情页,然后用 profile() 方法来处理个人详情页信息。

其中,dynamics() 方法和 profile() 方法的实现如下所示:

def dynamics(self, html):
    selector = pq(html)
    #print(selector.text())
    dynamics = selector('div[class="d-flex flex-items-baseline"] div')
    dynamics.find('span').remove()
    #print(dynamics.text())
    for item in dynamics.items():
        dynamic = item.text().strip()
        print(dynamic)

def profile(self, html):
    selector = pq(html)
    #print(selector.text())
    name = selector('input[id="user_profile_name"]').attr('value')
    email = selector('select[id="user_profile_email"] option[selected="selected"]').text()
    print(name,email)

我们新建一个 Login 对象,记住下面要输入自己的邮箱和密码,然后运行程序,如下所示:

if __name__ == "__main__":
    login = Login()
    login.login(email='email', password='password')

完整代码如下:

import requests
from pyquery import PyQuery as pq

class Login(object):
    def __init__(self):
        self.headers = {
    
    
            'Referer': 'https://github.com/',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36',
            'Host': 'github.com'
        }
        self.login_url = 'https://github.com/login'
        self.post_url='https://github.com/session'
        self.feed_url = 'https://github.com/dashboard-feed'
        self.logined_url = 'https://github.com/settings/profile'
        ## 维持会话,自动处理cookies
        self.session = requests.Session()
    
    ## 解析出登录所需要的
    def token(self):
        response = self.session.get(self.login_url, headers=self.headers)
        selector = pq(response.text)
        token = selector('input[name="authenticity_token"]').attr('value')
        return token
    def login(self, email, password):
        #print(self.token())
        post_data = {
    
    
            'commit': 'Sign in',
            'utf8': '✓',
            'authenticity_token': self.token(),
            'login': email,
            'password': password
        }
        response = self.session.post(self.post_url, data=post_data, headers=self.headers)
        response = self.session.get(self.feed_url, headers=self.headers)
        if response.status_code == 200:
            self.dynamics(response.text)
            #print(response.text)
        print("================================")
        response = self.session.get(self.logined_url, headers=self.headers)
        if response.status_code == 200:
            self.profile(response.text)
    
    ## 关注人的动态信息
    def dynamics(self, html):
        selector = pq(html)
        #print(selector.text())
        dynamics = selector('div[class="d-flex flex-items-baseline"] div')
        dynamics.find('span').remove()
        #print(dynamics.text())
        for item in dynamics.items():
            dynamic = item.text().strip()
            print(dynamic)
    ## 详情页面
    def profile(self, html):
        selector = pq(html)
        #print(selector.text())
        name = selector('input[id="user_profile_name"]').attr('value')
        email = selector('select[id="user_profile_email"] option[selected="selected"]').text()
        print(name,email)
if __name__ == "__main__":
    login = Login()
    login.login(email='[email protected]', password='password')

运行结果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43328040/article/details/108896053