5. requests模块高级

requests模块高级

版权声明:本博客来自路飞学城Python全栈开发培训课件,仅用于学习之用,严禁用于商业用途。
欢迎访问路飞学城官网:https://www.luffycity.com/

本节重点

  • requests模块的Cookies处理

  • requests模块的代理IP操作

1. requests模块的Cookies处理

1.1 会话和Cookies

在浏览网站的过程中,我们经常会遇到需要登录的情况,有些页面只有登录之后才可以访问,而且登录之后可以连续访问很多次网站,但是有时候过一段时间就需要重新登录。还有一些网站,在打开浏览器时就自动登录了,而且很长时间都不会失效,这种情况又是为什么?其实这里面就涉及到了会话和cookie的相关知识,本节就来揭开他们的神秘面纱。

1.2 无状态HTTP

HTTP的无状态指的是http协议对事物处理是没有记忆能力的,也就是说服务器不知道客户端是什么状态。当我们向服务器发送请求后,服务器解析此请求,然后返回对应的响应,服务器负责完成这个过程,而且这个过程是完全独立的,服务器不会记录前后状态的变化,也就是缺少状态记录。这就意味着如果后续需要处理前面的信息,则必须重传,这导致需要额外传递一些前面的重复请求,才能获取后续响应,然而这种效果显然不是我们想要的。为了保持前后状态,我们肯定不能将前面的请求全部重传一次,这太浪费资源了,对于这种需要用户登录页面来说,更是棘手。

这时两个保持http连接状态的技术出现了,分别是会话和cookie。会话在服务端,用来保存用户的会话信息。cookie在客户端,有了cookie,浏览器在下次访问网页时会自动附带上cookie发送给服务器,服务器识别cookie并鉴定出是哪个用户,然后在判断出用户的相关状态,然后返回对应的响应。

  • 会话:会话(对象)是用来存储特定用户进行会话所需的属性及配置信息的。
  • cookie:指的是某些网站为了辨别用户身份、进行会话跟踪而存储在用户本地终端上的数据。
  • 会话维持:当客户端第一次请求服务器时,服务器会返回一个响应对象,响应头中带有Set-Cookie字段,cookie会被客户端进行存储,该字段表明服务器已经为该客户端用户创建了一个会话对象,用来存储该用户的相关属性机器配置信息。当浏览器下一次再请求该网站时,浏览器会把cookie放到请求头中一起提交给服务器,cookie中携带了对应会话的ID信息,服务器会检查该cookie即可找到对应的会话是什么,然后再判断会话来以此辨别用户状态。
  • 形象案例:当iphone用户第一次向iphone的售后客服打电话咨询相关问题时,售后客服会针对当前用户创建一个唯一的“问题描述”,用来记录当前用户的iphone产品出现的相关问题,然后当用户阐述清楚问题后,售后客服就会将问题记录在所谓的“问题描述”中,并将“问题描述”的唯一编号通过短信告诉该用户。这样的好处就是,下次该用户的产品再次出现问题向售后电话咨询时,提供了“问题描述”的唯一编码后,就不需要在将该产品之前的问题再次进行描述了。此案例中,“问题描述”就相当于是会话,“问题描述”的唯一编码就是会话ID,短信就是cookie。

1.3 案例之github的模拟登录

1.3.1 项目需求

使用requests模块实现github的模拟登录

1.3.2 浏览器登录流程
  • 录入用户名和密码,点击登录

    在这里插入图片描述

  • 页面跳转,显示登录成功后的主页面:

    在这里插入图片描述

1.3.3 requests模拟登陆初试
  • 通过抓包工具捕获点击登陆按钮后对应的post请求数据包

    在这里插入图片描述

    在这里插入图片描述

  • 获取该数据包的url和请求参数

  • 发起请求,进行持久化存储

    import requests
    from lxml import etree
    
    headers = {
        ‘User-Agent’: ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36}
    url = ‘https://github.com/session‘
    data = {
        ‘commit’: ‘Sign in,
        ‘utf8’: ‘✓’,
        ‘authenticity_token’:’lGnVdPaEf/jfBd6dzkQFpd4idQZxO96HSvRzWIPo5099dvmZTqEx+Q13zLjQzYpE08YmZdJStHd+Vk6Yhz7t/Q==,
        ‘login’: ‘bobo328410948@sina.com’,
        ‘password’: ‘bobo@15027900535,
        ‘webauthn-support’: ‘supported’,
    }
    page_text = requests.post(url=url,headers=headers,data=data).text
    
    with open(./git.html’,’w’,encoding=’utf-8) as fp:
        fp.write(page_text)
    
  • 查看爬取下来的页面是否为登陆成功后的页面:

    在这里插入图片描述

    不是登陆成功后的页面,则表示模拟登陆失败!

1.3.4 模拟登陆失败原因分析

检查分析抓取的登陆时发起的post请求对应的数据包:

在这里插入图片描述

该数据包是携带了cookie进行的请求发送,那么该cookie是什么时候存储到客户端的呢?一定是该请求的前次请求时产生的cookie。

在浏览器进行登陆请求发送之前,我们已经是第二次访问的github服务器端了,因为第一次是在浏览器中访问登陆页面时。

抓取该次请求的数据包,查看响应头信息中是否存在set-cookie,如果有,则证实该次请求时,服务器端给客户端创建了会话对象,且创建了cookie返回给了客户端进行存储。

在这里插入图片描述

果然存在set-cookie,因此,我们在使用requests模块进行模拟登陆时,发起的请求也是需要携带cookie的。那么cookie如何被携带到requests的请求中呢?

1.3.5 requests模块cookie处理

requests模块处理cookie的两种方式:

  • 将cookie手动从抓包工具中获取,然后封装到requests请求的headers中,将headers作用到请求方法中。(不建议)

    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
        'Cookie':'xxxxxxxxx'
    }
    
  • 创建会话对象,使用会话对象进行请求发送。因为会话中会自动携带且处理cookie。(推荐)

特别说明: 在使用会话对象发起登陆请求时,需要额外注意该请求参数:

’authenticity_token':'lGnVdPaEf/jfBd6dzkQFpd4idQZxO96HSvRzWIPo5099dvmZTqEx+Q13zLjQzYpE08YmZdJStHd+Vk6Yhz7t/Q==' ,

该参数的value值看起来像是一组密文数据,则该数据很有可能是动态变化的,因此需要动态捕获,动态赋值。如果该参数不处理的话,也是会登陆失败的。那么该值应该从那哪里动态捕获呢?这种值我们可以统一称为动态taken参数,该值一般都会动态存在与该请求对应的前台页面中,因此我们在登陆页面的源码中进行搜索:

在这里插入图片描述

因此我们可以通过数据解析,动态解析捕获到该请求参数的值。详细代码如下:

import requests
from lxml import etree
headers = {
 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
#创建会话对象,该会话对象可以调用get和post发起请求
session = requests.Session()
#使用会话对面对登录页面发起请求
page_text = session.get(url='https://github.com/login',headers=headers).text
#解析出动态的taken值
tree = etree.HTML(page_text)
t = tree.xpath('//*[@id="login"]/form/input[2]/@value')[0]
#指定模拟登录请求的url
url = 'https://github.com/session'
#参数封装(处理动态taken值)
data = {
 'commit': 'Sign in',
 'utf8': '✓',
 'authenticity_token': t,
 'login': 'bobo328410948@sina.com',
 'password': 'bobo@15027900535',
 'webauthn-support': 'supported',
}
#使用会话对象进行模拟登录请求发送(携带cookie)
page_text = session.post(url=url,headers=headers,data=data).text
#持久化存储
with open('./git.html','w',encoding='utf-8') as fp:
 fp.write(page_text)

查看git.html:登录成功

在这里插入图片描述

1.4 案例之古诗文网模拟登陆

1.4.1 项目需求

使用requests模块实现古诗文网https://so.gushiwen.org/user/login.aspx的模拟登录

1.4.2 模拟登陆流程

首先我们分析登录页面,发现除了用户名、密码外,增加了验证码和两个隐藏的字段:

在这里插入图片描述

通过抓包分析,登录请求为post请求,请求参数除了用户名、密码、验证码外,隐藏字段的值同时需要上传

在这里插入图片描述

同时请求头里还有cookie信息

在这里插入图片描述

通过分析,我们请求流程如下:

  • 通过session发起登录页面get请求
  • 对登录页面进行数据解析,获取隐藏字段的值和验证码图片
  • 对验证码图片使用超级鹰进行解析
  • 通过sessionf发起登录页面post请求
  • 通过页面数据验证是否登录成功
1.4.3 代码示例

超级鹰接口:

# chaojiying.py

import requests
from hashlib import md5

import requests
from hashlib import md5

class Chaojiying_Client(object):

    def __init__(self, username, password, soft_id):
        self.username = username
        password =  password.encode('utf8')
        self.password = md5(password).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
        return r.json()

    def ReportError(self, im_id):
        """
        im_id:报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()

爬虫代码:


import requests
from lxml import etree
from chaojiying import Chaojiying_Client


# 封装识别验证码图片的函数
def getCodeText(imgPath, codeType):
    # 普通用户用户名
    username = '超级鹰的用户名'
    # 普通用户密码
    password = '超级鹰的密码'
    # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
    soft_id = 超级鹰的软件id
    # 图片文件:即将被识别的验证码图片的路径
    filename = imgPath

    # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。
    # 在此查询所有类型 http://www.yundama.com/price.html
    codetype = codeType

    result = None
    # 检查
    if (username == 'username'):
        print('请设置好相关参数再测试')
    else:
        # 初始化
        chaojiying = Chaojiying_Client(username, password, soft_id)
        im = open(filename, 'rb').read()
        # 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果
        result = chaojiying.PostPic(im, codetype)
        print(result)
        pic_str = result['pic_str']
    return pic_str


def get_code(url, s, headers):
    # 将验证码图片下载到本地
    page_text = s.get(url=url, headers=headers).text
    # 解析验证码图片img中src属性值
    tree = etree.HTML(page_text)
    code_img_src = 'https://so.gushiwen.org' + tree.xpath('//*[@id="imgCode"]/@src')[0]
    print(code_img_src)
    img_data = s.get(url=code_img_src, headers=headers).content
    # 将验证码图片保存到了本地
    with open('code.jpg', 'wb') as fp:
        fp.write(img_data)

    # 解析动态参数
    __VIEWSTATE = tree.xpath('//input[@id="__VIEWSTATE"]/@value')[0]
    __VIEWSTATEGENERATOR = tree.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0]

    # 调用超级鹰验证码平台的示例程序进行验证码图片数据识别
    code_text = getCodeText('code.jpg', 1004)
    print('识别结果为:', code_text)
    info = {
        "code_text": code_text,
        "__VIEWSTATE": __VIEWSTATE,
        "__VIEWSTATEGENERATOR": __VIEWSTATEGENERATOR,
    }
    return info


def login(url, s, headers, info):
    # 模拟登录
    data = {
        # 前两个参数是动态参数
        '__VIEWSTATE': info["__VIEWSTATE"],
        '__VIEWSTATEGENERATOR': info["__VIEWSTATEGENERATOR"],
        'from': '',
        'email': '古诗文的用户名',
        'pwd': '古诗文网的密码',
        'code': info["code_text"],
        'denglu': '登录',
    }

    # s表示的session中已经存储了相关的cookie
    response = s.post(url=url, data=data, headers=headers)
    page_text = response.text
    print('响应状态:', response.status_code)
    with open('gushiwenwang.html', 'w', encoding='utf-8') as fp:
        fp.write(page_text)
    print('登录成功!')


if __name__ == '__main__':
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
    }
    url = 'https://so.gushiwen.org/user/login.aspx'
    s = requests.Session()
    info = get_code(url=url, s=s, headers=headers)
    login(url=url,s=s,headers=headers,info=info)

2.requests模块的代理IP操作

2.1 引子

我们在做爬虫的过程中经常会遇到这样的情况,最初爬虫正常运行,正常抓取数据,一切看起来都是那么美好,然而一杯茶的功夫可能就会出现错误,比如403,这时打开网页一看,可能会看到“您的IP访问频率太高”这样的提示。出现这种现象的原因是网站采取了一些反爬措施。比如,服务器会检测某个IP在单位时间内请求的次数,如果超过了某个阈值,就会直接拒绝服务,返回一些错误信息,这种情况可以称为封IP。

既然服务器检测的是某个IP单位时间的请求次数,那么借助某种方式来伪装我们的IP,让服务器识别不出是由我们本机发起的请求,不就可以成功防止封IP了吗?一种有效的方式就是使用代理。

2.2 什么是代理

代理实际上指的就是代理服务器,它的功能就是代理网络用户去取得网络信息。形象的说,它是网络信息的中转站。在我们正常请求一个网站时,是发送了请求给Web服务器,Web服务器把响应传回我们。如果设置了代理服务器,实际上就是在本机和服务器之间搭建了一个桥梁,此时本机不是直接向Web服务器发起请求,而是向代理服务器发出请求,请求会发送给代理服务器,然后代理服务器再发送给Web服务器,接着由代理服务器再把Web服务器返回的响应转发给本机。这样我们同样可以正常访问网页,但这个过程中Web服务器识别出的真实IP就不再是我们本机的IP了,就成功实现了IP伪装,这就是代理的基本原理。

2.3 代理的作用

  • 突破自身IP访问的限制,访问一些平时不能访问的站点。
  • 隐藏真实IP,免受攻击,防止自身IP被封锁

2.4 相关代理网站

  • 快代理
  • 西祠代理
  • www.goubanjia.com

2.4 案例

当我们在百度中搜索“ip”关键词后,对应搜索结果的页面显示中会有本次浏览器发起请求对应的IP信息:

在这里插入图片描述

如果我们使用requests模块相关操作应用了代理,则请求到该页面中显示的ip信息就是代理IP相关信息了。我们可以使用requests模块请求方法的proxies参数处理代理IP:


import requests
import random
if __name__ == "__main__":
    #不同浏览器的UA
    header_list = [
        # 遨游
        {"user-agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)"},
        # 火狐
        {"user-agent": "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"},
        # 谷歌
        {
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"}
    ]
    #不同的代理IP
    proxy_list = [
        {"http": "112.115.57.20:3128"},
        {'http': '121.41.171.223:3128'}
    ]
    #随机获取UA和代理IP
    header = random.choice(header_list)
    proxy = random.choice(proxy_list)
    url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'
    #参数3:设置代理
    response = requests.get(url=url,headers=header,proxies=proxy)
    response.encoding = 'utf-8'
    with open('daili.html', 'wb') as fp:
        fp.write(response.content)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值