Python爬虫——模拟登录

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: Python爬虫——模拟登录

模拟登录
模拟登录现在主要分为两种模式,一种是基于Session和Cookie的模拟登录,一种是基于JWT(JSON Web Token)的模拟登录。

简单来说,打开网页后模拟登录,服务器会返回带有Set-Cookie字段的响应头,客户端会生成对应的Cookie,其中保存着与SessionID相关的信息,之后发送给服务器的请求都会携带这个生成的Cookie。这种模式的核心是获取客户端登录后生成的Cookie。对于第二种模式也是如此,现在很多网站采取的开发模式是前后端分离式,所以使用JWT进行登录校验越来越普遍。请求数据时,服务器会校验请求中携带的JWT是否有效,如果有效,就返回正常的数据,所以,这种模式其实就是获取JWT。

基于分析结果,我们可以手动在浏览器里输入用户名和密码,再把Cookie或者JWT复制到代码中请求数据,但是这样做明显会增加人工工作量。所以说实现爬虫,就要程序模拟登录。

1. 模拟登录的基本原理
用爬虫修改GitHub的个人设置,用爬虫提交购票订单,用爬虫发微博,能做到吗?答案是能,这时需要用到一些模拟登录相关的技术。

1.1网站登录验证的实现
实现模拟登录,首先得了解网站如何验证登录内容。登录一般需要两个内容——用户名和密码,也有的网站是填写手机号获取验证码,或者微信扫码,或者OAuth验证等,从根本上看,这些方式都是把一些可供认证的信息提交给服务器。
1.2基于Session和Cookie
Session和Cookie一定是相互配合工作的。
Cookie里可能只保存了SessionID相关的信息,服务器能根据这个信息找到对应的Session。当用户登录后,服务器会在对应的Session里标记一个字段,代表用户已处于登录状态或者其他(如角色、登录时间)。用户每次访问网站的时候都带着Cookie,服务器每次都找到对应的Session,然后看一下用户的状态是否为登录状态,再决定返回什么结果或执行什么操作。
Cookie里直接保存了某些凭证信息。例如用户发起登录请求,服务器校验通过后,返回给客户端端响应头里面可能带有Set-Cookie字段,里面包含类似凭证的信息,这样客户端会执行设置Cookie的操作,将那些类似凭证的信息保存到Cookie里,以后再访问网站时都携带着Cookie,服务器拿着其中的信息进行校验,自然也能检测登录状态。
1.3基于JWT
前后端分离的开发模式越来越火,JWT的英文全称为JSON Web Token,执行一种基于JSON的开放标准,实际上就是在每次登录时都通过一个Token字段校验登录状态。Token可以直接用于认证,也可以传递一些额外信息。JWT一般是一个经过Base64编码技加密的字符串,有自己的标准。格式类似下面这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

有两个起分隔作用的“.“,可以把JWT看成一个三段式的加密字符串。这三部分分别是Header、Payload和Signature。

(1) Header:包含了Token的类型(“typ”)和签名算法(“alg”)信息。通常情况下,这个部分会指定为{"alg": "HS256", "typ": "JWT"},表示使用HMAC SHA-256算法进行签名。

(2) Payload:包含了要传输的信息,也称为声明(claims)。其中可以包含注册声明(registered claims)、公共声明(public claims)和私有声明(private claims),例如用户ID、姓名等信息。一些注册声明包括iss(issuer)、sub(subject)、aud(audience)、exp(expiration time)、nbf(not before time)等。

(3) Signature:利用密钥secret对Header和Payload进行签名后生成的结果,以确保JWT的完整性和真实性。签名通常使用Base64编码的Header和Payload,并结合一个密钥使用指定的算法进行计算得到。

JWT的格式是明文的,因此不应将敏感数据直接存储在JWT中,特别是不要在Payload中存储敏感信息,因为Payload中的内容可以被Base64解码。敏感信息应该通过加密或其他安全手段进行处理。

模拟登录
基于Session和Cookie模拟登录

  • 如果要用爬虫实现基于Session和Cookie的模拟登录,最主要的是要维护好Cookie的信息,因为爬虫相当于客户端的浏览器,我们把浏览器做的事情模拟好就行。
  • 第一,如果已经在浏览器中登录了自己的账号,那么可以直接把Cookie复制给爬虫。这是最省时省力的方式,相当于手动在浏览器中登录。我们把Cookie放到爬虫代码里,爬虫每次请求的时候都将其放到请求中,可以说完全模拟了浏览器的操作。之后服务器的动作和前面一样,通过Cookie校验登录状态,如果校验没问题,就执行某些操作或返回某些内容。
  • 第二,如果想让爬虫完全自动化操作,那么可以直接使用爬虫模拟登录过程。大多数时候,登录过程其实就是一个POST请求。用爬虫把用户名、密码等信息提交给服务器,服务器返回的响应头里面可能会有Set-Cookie字段,我们只需把这个字段里的内容保存下来就行了。所以最主要的是把这个过程中的Cookie维持好。当然,可能会遭遇一些困难,例如登录过程中伴随着各种校验参数,不好直接模拟请求;客户端设置Cookie的过程是通过JavaScript语言实现的,所以可能还得仔细分析其中的逻辑,尤其是用requests这样的请求库进行模拟登录时,遇到问题会比较多。
  • 第三,可以用一些简单的方式模拟登录,即实现登录过程的自动化。例如用Selenium、Pyppeteer或Playwright驱动浏览器模拟执行一些操作(如填写用户名和密码、提交表单等)。登录成功后,通过Selenium或Pyppeteer获取当前浏览器的Cookie并保存。同样之后就可以拿着Cookie的内容发起请求,实现模拟登录。

核心是维护好客户端的Cookie信息。总之,每次请求时都携带Cookie信息就能实现模拟登录了。

基于JWT模拟登录
由于JWT的字符串就是用户访问的凭证,所以模拟登录只需做到下面几步。

(1)模拟登录操作。例如拿着用户名和密码信息请求登录接口,获取服务器返回的结果,这个结果通常包含JWT信息,将其保存下来即可。

(2)之后发送给服务器的请求均携带JWT。在JWT不过期的情况下,通常能正常访问和执行操作。携带方式多种多样,因网站而异。

(3)如果JWT过期了,可能需要再次做第一步,重新获取JWT。

2. 基于Session和Cookie的模拟登录爬取实战

准备工作
安装好requests库并掌握其基本用法
安装好Selenium库并掌握其基本用法

案例介绍
案例网站:https://login2.scrape.center/,访问这个网站,会打开一个登录页面,如图所示:

image.png

输入用户名和密码(都是admin),然后单击登录按钮。登录成功后, 我们可以看到一个熟悉的页面,如图所示:

这个网站基于传统的MVC模式开发的,因此比较适合基于Session加Cookie的模拟登录。

image.png
3.模拟登录

打开开发者工具,重新执行登录操作,查看登录过程中产生的请求,如图所示:

image.png

从图中看到, 浏览器发起了一个POST请求,目标URL是https://login2.scrape.center/login,并通过表单提交的方式向服务器提交了登录数据,其中包括username和password两个字段,返回的状态码是302,Response Headers的location字段为根页面,同时Response Headers还包含Set-Cookie字段,其中设置了sessionid。

要实现模拟登录,只需要模拟这个POST请求就好了。每次发出的请求默认都是独立且互不干扰的,例如第一次调用post方法模拟登录了网站,紧接着调用get方法请求了主页。这两个请求是完全独立的,第一次请求获取的Cookie并不能传给第二次请求,因此常规的顺序调用无法达到模拟登录的效果。代码如下:

import requests
from urllib.parse import urljoin

BASE_URL = 'https://login2.scrape.center/'
LOGIN_URL = urljoin(BASE_URL, '/login')
INDEX_URL = urljoin(BASE_URL, 'page/1')
USERNAME = 'admin'
PASSWORD = 'admin'

response_login = requests.post(LOGIN_URL, data={
   'username':USERNAME, 'password':PASSWORD})

response_index = requests.get(INDEX_URL)
print('Response Status', response_index.status_code)
print('Response URL', response_index.url)

这里定义了3个URL、用户名和密码,然后调用requests库的post方法请求了登录页面进行模拟登录,紧接着调用get方法请求网站首页来获取页面内容,它能正常获取数据吗?由于requests可以自动处理重定向,所以在最后把响应的URL打印出来,如果结果是INDEX_URL,就证明模拟登录成功并获取了网站首页的内容,如果结果是LOGIN_URL,就说明跳回登录页面,模拟登录失败。

运行代码,结果如下:

Response Status 200
Response URL https://login2.scrape.center/login?next=/page/1

可以看到,最后打印的页面URL是登录页面的URL。这里也通过response_index的text属性来看一下页面源代码,这里就是登录页面的源码内容。

说明没有成功完成模拟登录,这就印证了按序调用requests的post、get方法是发出两个请求,两次对应的Session不是同一个,这里我们只是模拟了第一个Session,并不影响第二个Session的状态,因此模拟登录也就无效了。

模拟登录的关键在于两次发出的请求的Cookie相同。因此这里可以把第一次模拟登录后的Cookie保存下来,在第二次请求的时候加上这个Cookie,代码改写如下:

import requests
from urllib.parse import urljoin

BASE_URL = 'https://login2.scrape.center/'
LOGIN_URL= urljoin(BASE_URL, '/login')
INDEX_URL = urljoin(BASE_URL, '/page/1')
USERNAME = 'admin'
PASSWORD = 'admin'

response_login = requests.post(LOGIN_URL, data={
   'username':USERNAME, 'password':PASSWORD}, allow_redirects=False)

cookies = response_login.cookies
print('cookies', cookies)

response_index = requests.get(INDEX_URL, cookies=cookies)
print('Response Status', response_index.status_code)
print('Response URL', response_index.url)

由于requests具有自动处理重定向的能力,所以在模拟登录的过程中要加上allow_redirects参数并将值设置为False,使requests不自动处理重定向。这里将登陆后服务器返回的响应内容赋值给respose_login变量,然后调用response_login的cookies属性就可以获取网站的Cookie信息。由于requests自动帮我们解析了响应头中的Set-Cookie字段并设置了Cookie,因此不需要我们再去手动解析。接着调用requests的get方法请求网站首页,增加了一个参数cookies,传入的值是第一次模拟登录后获取的Cookie,这样第二次请求就携带上了第一次模拟登录获取的Cookie信息,之后网站会根据里面的SessionID信息找到同一个Session,并校验出当前发出请求的用户已经处于登录状态,然后返回正确的结果。

运行结果如下:

cookies <RequestsCookieJar[<Cookie sessionid=4uo2e22pvr8uamixodq1kvt7eimucb46 for login2.scrape.center/>]>
Response Status 200
Response URL https://login2.scrape.center/page/1

返回的是INDEX_URL,模拟登录成功!这种方式比较繁琐,每次请求都需要处理并传递一次Cookie,有没有更简便的方法呢?

有的,可以直接借助requests内置的Session对象帮我们自动处理Cookie,使用Session对象之后, requests会自动保存每次请求后设置的Cookie,并在下次请求时携带上它。实现代码如下:

import requests
from urllib.parse import urljoin

BASE_URL = 'https://login2.scrape.center/'
LOGIN_URL= urljoin(BASE_URL, '/login')
INDEX_URL = urljoin(BASE_URL, '/page/1')
USERNAME = 'admin'
PASSWORD = 'admin'

session = requests.Session()

response_login = session.post(LOGIN_URL, data={
   'username': USERNAME, 'password':PASSWORD})

cookies = session.cookies
print('Cookies', cookies)

response_index = session.get(INDEX_URL)
print('Response Status', response_index.status_code)
print('Response URL', response_index.url)

这里声明了一个Session对象,然后每次发出请求的时候都直接调用Session对象的post方法或get方法就好了,是我们无须再关心Cookie的处理和传递问题。

运行结果如下:

Cookies <RequestsCookieJar[<Cookie sessionid=pywnmm7eagodwrzf81y3ch8g9w7rngrp for login2.scrape.center/>]>
Response Status 200
Response URL https://login2.scrape.center/page/1

建议使用Session对象进行请求,这样更方便。

这个案例比较简单,如果碰上复杂一点的网站,例如带验证码、带加密参数的网站,直接requests并不能很好处理模拟登录,那登录不了,整个页面不久没法爬取了吗?可以用Selenium模拟浏览器的操作,进而实现模拟登录,然后获取登录成功后的Cookie,再把获取的Cookie交由requests等爬取。

代码如下:

from urllib.parse import urljoin
from selenium.webdriver.chrome.service import Service
from selenium import webdriver
from selenium.webdriver.common.by import By
import requests
import time

options = webdriver.ChromeOptions()
services = Service('../第7章JavaScript动态渲染页面爬取/Selenium/chromedriver')

browser = webdriver.Chrome(service=services, options=options)

BASE_URL = 'https://login2.scrape.center/'
LOGIN_URL= urljoin(BASE_URL, '/login')
INDEX_URL = urljoin(BASE_URL, '/page/1')
USERNAME = 'admin'
PASSWORD = 'admin'

browser.get(BASE_URL)
browser.find_element(By.CSS_SELECTOR, 'input[name="username"]').send_keys(USERNAME)
browser.find_element(By.CSS_SELECTOR, 'input[name="password"]').send_keys(PASSWORD)
browser.find_element(By.CSS_SELECTOR, 'input[type="submit"]').click()
time.sleep(10)

# 从浏览器对象中获取Cookie信息
cookies = browser.get_cookies()
print('Cookies', cookies)
browser.close()

# 把Cookie信息放入请求中
session = requests.Session()
for cookie in cookies:
    session.cookies.set(cookie['name'], cookie['value'])

response_index = session.get(INDEX_URL)
print('Response Status', response_index.status_code)
print('Response URL', response_index.url)

这里我们先使用Selenium打开Chrome浏览器,然后访问登录页面,模拟输入用户和密码,并点击登录按钮。浏览器会提示登录成功,并跳转到主页面。

这时,调用get_cookies方法便能获取当前浏览器等所有Cookie信息,这就是模拟登录成功后的Cookie,用它能访问其他数据了。之后,声明了一个Session对象,赋值给session变量,然后遍历了刚才获取的所有Cookie信息,将每个Cookie信息依次设置到session的cookies属性上,随后拿这个session请求网站首页,就能够获取想要的信息了而不会跳转到登录页面。

运行结果如下:

Cookies [{
   'domain': 'login2.scrape.center', 'expiry': 1713340840, 'httpOnly': True, 'name': 'sessionid', 'path': '/', 'sameSite': 'Lax', 'secure': False, 'value': 'qhqcw24s38ghr20zvi82lnsr8ghehkwb'}]
Response Status 200
Response URL https://login2.scrape.center/page/1

可以看到,这里的模拟登录和获取Cookie信息后的爬取都成功。当碰到难以模拟登录的情况时,可以使用Selenium等模拟浏览器的操作方式,使用它获取模拟登录后的Cookie,再用这个Cookie爬取其他页面。

相关文章
|
1天前
|
数据采集 JSON 算法
Python爬虫——基于JWT的模拟登录爬取实战
Python爬虫——基于JWT的模拟登录爬取实战
10 1
Python爬虫——基于JWT的模拟登录爬取实战
|
1天前
|
数据采集 前端开发 NoSQL
Python编程异步爬虫实战案例
Python编程异步爬虫实战案例
10 2
|
2天前
|
数据采集 消息中间件 API
Python爬虫验证码识别——手机验证码的自动化处理
Python爬虫验证码识别——手机验证码的自动化处理
|
2天前
|
数据采集 JSON 网络协议
Python编程异步爬虫——aiohttp的使用
Python编程异步爬虫——aiohttp的使用
|
2天前
|
数据采集 调度 Python
Python编程异步爬虫——协程的基本原理(一)
Python编程异步爬虫——协程的基本原理(一)
|
2天前
|
数据采集 Python
Python编程异步爬虫——协程的基本原理(二)
Python编程异步爬虫——协程的基本原理(二)
|
3天前
|
数据采集 JavaScript 前端开发
如何使用Python爬虫处理JavaScript动态加载的内容?
如何使用Python爬虫处理JavaScript动态加载的内容?
|
2月前
|
机器学习/深度学习 数据采集 数据可视化
基于爬虫和机器学习的招聘数据分析与可视化系统,python django框架,前端bootstrap,机器学习有八种带有可视化大屏和后台
本文介绍了一个基于Python Django框架和Bootstrap前端技术,集成了机器学习算法和数据可视化的招聘数据分析与可视化系统,该系统通过爬虫技术获取职位信息,并使用多种机器学习模型进行薪资预测、职位匹配和趋势分析,提供了一个直观的可视化大屏和后台管理系统,以优化招聘策略并提升决策质量。
125 4
|
2月前
|
数据采集 存储 搜索推荐
打造个性化网页爬虫:从零开始的Python教程
【8月更文挑战第31天】在数字信息的海洋中,网页爬虫是一艘能够自动搜集网络数据的神奇船只。本文将引导你启航,用Python语言建造属于你自己的网页爬虫。我们将一起探索如何从无到有,一步步构建一个能够抓取、解析并存储网页数据的基础爬虫。文章不仅分享代码,更带你理解背后的逻辑,让你能在遇到问题时自行找到解决方案。无论你是编程新手还是有一定基础的开发者,这篇文章都会为你打开一扇通往数据世界的新窗。
|
3月前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
【7月更文挑战第31天】在网络数据的海洋中,使用Python的`requests`库构建网络爬虫就像探索未知的航船。HTTP协议指导爬虫与服务器交流,收集信息。HTTP请求包括请求行、头和体,响应则含状态行、头和体。`requests`简化了发送各种HTTP请求的过程。
75 4