requests进阶(cookie、防盗链、代理)

打码简介

反爬机制:验证码——识别验证码中的数据,用于模拟登录操作

识别验证码的操作:第三方自动识别

云打码使用流程:下载python对应的api接口后,修改其中用户的信息,换成自己的。

使用打码平台识别网页验证码的流程

  1. 将验证码图片进行本地下载
  2. 调用平台提供的示例代码进行图片的数据识别

打码登录古诗词网

网站url:https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx
想要登录,问题就是验证码,这里的思路是将其保存到本地,然后调用打码平台对其进行识别。

先信息搜集,看验证码在网页源码中的哪一个位置
在这里插入图片描述
发现验证码存储在<img id="imgCode">中,那么就可以用xpath定位到这里,然后爬取到本地。

第二个点是对平台下载的py代码需要在自己写的代码中被引用到,这里可以使用from 文件名 import 函数名来实现

平台下载的代码:

# coding=utf-8
import os, sys
import hashlib
import time
import json
import requests

FATEA_PRED_URL = "http://pred.fateadm.com"


def LOG(log):
    # 不需要测试时,注释掉日志就可以了
    print(log)
    log = None


class TmpObj():
    def __init__(self):
        self.value = None


class Rsp():
    def __init__(self):
        self.ret_code = -1
        self.cust_val = 0.0
        self.err_msg = "succ"
        self.pred_rsp = TmpObj()

    def ParseJsonRsp(self, rsp_data):
        if rsp_data is None:
            self.err_msg = "http request failed, get rsp Nil data"
            return
        jrsp = json.loads(rsp_data)
        self.ret_code = int(jrsp["RetCode"])
        self.err_msg = jrsp["ErrMsg"]
        self.request_id = jrsp["RequestId"]
        if self.ret_code == 0:
            rslt_data = jrsp["RspData"]
            if rslt_data is not None and rslt_data != "":
                jrsp_ext = json.loads(rslt_data)
                if "cust_val" in jrsp_ext:
                    data = jrsp_ext["cust_val"]
                    self.cust_val = float(data)
                if "result" in jrsp_ext:
                    data = jrsp_ext["result"]
                    self.pred_rsp.value = data


def CalcSign(pd_id, passwd, timestamp):
    md5 = hashlib.md5()
    md5.update((timestamp + passwd).encode())
    csign = md5.hexdigest()

    md5 = hashlib.md5()
    md5.update((pd_id + timestamp + csign).encode())
    csign = md5.hexdigest()
    return csign


def CalcCardSign(cardid, cardkey, timestamp, passwd):
    md5 = hashlib.md5()
    md5.update(passwd + timestamp + cardid + cardkey)
    return md5.hexdigest()


def HttpRequest(url, body_data, img_data=""):
    rsp = Rsp()
    post_data = body_data
    files = {
        'img_data': ('img_data', img_data)
    }
    header = {
        'User-Agent': 'Mozilla/5.0',
    }
    rsp_data = requests.post(url, post_data, files=files, headers=header)
    rsp.ParseJsonRsp(rsp_data.text)
    return rsp


class FateadmApi():
    # API接口调用类
    # 参数(appID,appKey,pdID,pdKey)
    def __init__(self, app_id, app_key, pd_id, pd_key):
        self.app_id = app_id
        if app_id is None:
            self.app_id = ""
        self.app_key = app_key
        self.pd_id = pd_id
        self.pd_key = pd_key
        self.host = FATEA_PRED_URL

    def SetHost(self, url):
        self.host = url

    #
    # 查询余额
    # 参数:无
    # 返回值:
    #   rsp.ret_code:正常返回0
    #   rsp.cust_val:用户余额
    #   rsp.err_msg:异常时返回异常详情
    #
    def QueryBalc(self):
        tm = str(int(time.time()))
        sign = CalcSign(self.pd_id, self.pd_key, tm)
        param = {
            "user_id": self.pd_id,
            "timestamp": tm,
            "sign": sign
        }
        url = self.host + "/api/custval"
        rsp = HttpRequest(url, param)
        if rsp.ret_code == 0:
            pass
        else:
            LOG("query failed ret: {} err: {}".format(rsp.ret_code, rsp.err_msg.encode('utf-8')))
        return rsp

    #
    # 查询网络延迟
    # 参数:pred_type:识别类型
    # 返回值:
    #   rsp.ret_code:正常返回0
    #   rsp.err_msg: 异常时返回异常详情
    #
    def QueryTTS(self, pred_type):
        tm = str(int(time.time()))
        sign = CalcSign(self.pd_id, self.pd_key, tm)
        param = {
            "user_id": self.pd_id,
            "timestamp": tm,
            "sign": sign,
            "predict_type": pred_type,
        }
        if self.app_id != "":
            #
            asign = CalcSign(self.app_id, self.app_key, tm)
            param["appid"] = self.app_id
            param["asign"] = asign
        url = self.host + "/api/qcrtt"
        rsp = HttpRequest(url, param)
        if rsp.ret_code == 0:
            LOG("query rtt succ ret: {} request_id: {} err: {}".format(rsp.ret_code, rsp.request_id, rsp.err_msg))
        else:
            LOG("predict failed ret: {} err: {}".format(rsp.ret_code, rsp.err_msg.encode('utf-8')))
        return rsp

    #
    # 识别验证码
    # 参数:pred_type:识别类型  img_data:图片的数据
    # 返回值:
    #   rsp.ret_code:正常返回0
    #   rsp.request_id:唯一订单号
    #   rsp.pred_rsp.value:识别结果
    #   rsp.err_msg:异常时返回异常详情
    #
    def Predict(self, pred_type, img_data, head_info=""):
        tm = str(int(time.time()))
        sign = CalcSign(self.pd_id, self.pd_key, tm)
        param = {
            "user_id": self.pd_id,
            "timestamp": tm,
            "sign": sign,
            "predict_type": pred_type,
            "up_type": "mt"
        }
        if head_info is not None or head_info != "":
            param["head_info"] = head_info
        if self.app_id != "":
            #
            asign = CalcSign(self.app_id, self.app_key, tm)
            param["appid"] = self.app_id
            param["asign"] = asign
        url = self.host + "/api/capreg"
        files = img_data
        rsp = HttpRequest(url, param, files)
        if rsp.ret_code == 0:
            pass
        else:
            LOG("predict failed ret: {} err: {}".format(rsp.ret_code, rsp.err_msg))
            if rsp.ret_code == 4003:
                # lack of money
                LOG("cust_val <= 0 lack of money, please charge immediately")
        return rsp

    #
    # 从文件进行验证码识别
    # 参数:pred_type;识别类型  file_name:文件名
    # 返回值:
    #   rsp.ret_code:正常返回0
    #   rsp.request_id:唯一订单号
    #   rsp.pred_rsp.value:识别结果
    #   rsp.err_msg:异常时返回异常详情
    #
    def PredictFromFile(self, pred_type, file_name, head_info=""):
        with open(file_name, "rb") as f:
            data = f.read()
        return self.Predict(pred_type, data, head_info=head_info)

    #
    # 识别失败,进行退款请求
    # 参数:request_id:需要退款的订单号
    # 返回值:
    #   rsp.ret_code:正常返回0
    #   rsp.err_msg:异常时返回异常详情
    #
    # 注意:
    #    Predict识别接口,仅在ret_code == 0时才会进行扣款,才需要进行退款请求,否则无需进行退款操作
    # 注意2:
    #   退款仅在正常识别出结果后,无法通过网站验证的情况,请勿非法或者滥用,否则可能进行封号处理
    #
    def Justice(self, request_id):
        if request_id == "":
            #
            return
        tm = str(int(time.time()))
        sign = CalcSign(self.pd_id, self.pd_key, tm)
        param = {
            "user_id": self.pd_id,
            "timestamp": tm,
            "sign": sign,
            "request_id": request_id
        }
        url = self.host + "/api/capjust"
        rsp = HttpRequest(url, param)
        if rsp.ret_code == 0:
            pass
        else:
            LOG("justice failed ret: {} err: {}".format(rsp.ret_code, rsp.err_msg.encode('utf-8')))
        return rsp

    #
    # 充值接口
    # 参数:cardid:充值卡号  cardkey:充值卡签名串
    # 返回值:
    #   rsp.ret_code:正常返回0
    #   rsp.err_msg:异常时返回异常详情
    #
    def Charge(self, cardid, cardkey):
        tm = str(int(time.time()))
        sign = CalcSign(self.pd_id, self.pd_key, tm)
        csign = CalcCardSign(cardid, cardkey, tm, self.pd_key)
        param = {
            "user_id": self.pd_id,
            "timestamp": tm,
            "sign": sign,
            'cardid': cardid,
            'csign': csign
        }
        url = self.host + "/api/charge"
        rsp = HttpRequest(url, param)
        if rsp.ret_code == 0:
            LOG("charge succ ret: {} request_id: {} pred: {} err: {}".format(rsp.ret_code, rsp.request_id,
                                                                             rsp.pred_rsp.value, rsp.err_msg))
        else:
            LOG("charge failed ret: {} err: {}".format(rsp.ret_code, rsp.err_msg.encode('utf-8')))
        return rsp

    ##
    # 充值,只返回是否成功
    # 参数:cardid:充值卡号  cardkey:充值卡签名串
    # 返回值: 充值成功时返回0
    ##
    def ExtendCharge(self, cardid, cardkey):
        return self.Charge(cardid, cardkey).ret_code

    ##
    # 调用退款,只返回是否成功
    # 参数: request_id:需要退款的订单号
    # 返回值: 退款成功时返回0
    #
    # 注意:
    #    Predict识别接口,仅在ret_code == 0时才会进行扣款,才需要进行退款请求,否则无需进行退款操作
    # 注意2:
    #   退款仅在正常识别出结果后,无法通过网站验证的情况,请勿非法或者滥用,否则可能进行封号处理
    ##
    def JusticeExtend(self, request_id):
        return self.Justice(request_id).ret_code

    ##
    # 查询余额,只返回余额
    # 参数:无
    # 返回值:rsp.cust_val:余额
    ##
    def QueryBalcExtend(self):
        rsp = self.QueryBalc()
        return rsp.cust_val

    ##
    # 从文件识别验证码,只返回识别结果
    # 参数:pred_type;识别类型  file_name:文件名
    # 返回值: rsp.pred_rsp.value:识别的结果
    ##
    def PredictFromFileExtend(self, pred_type, file_name, head_info=""):
        rsp = self.PredictFromFile(pred_type, file_name, head_info)
        return rsp.pred_rsp.value

    ##
    # 识别接口,只返回识别结果
    # 参数:pred_type:识别类型  img_data:图片的数据
    # 返回值: rsp.pred_rsp.value:识别的结果
    ##
    def PredictExtend(self, pred_type, img_data, head_info=""):
        rsp = self.Predict(pred_type, img_data, head_info)
        return rsp.pred_rsp.value

# 记得修改成自己的
def image_identify(data, flag=False):
    pd_id = "xxxxxxx"  # 用户中心页可以查询到pd信息
    pd_key = "xxxxxxxxxxxxxxxxxx"
    app_id = "xxxxx"  # 开发者分成用的账号,在开发者中心可以查询到
    app_key = "xxxxxxxxxxxxxxxx"
    # 识别类型,
    # 具体类型可以查看官方网站的价格页选择具体的类型,不清楚类型的,可以咨询客服
    pred_type = "30400"
    api = FateadmApi(app_id, app_key, pd_id, pd_key)
    # 查询余额
    try:
        balance = api.QueryBalcExtend()  # 直接返余额
        if flag:
            LOG("当前余额: " + str(balance))

        # 通过文件形式识别:
        # file_name = "code.jpg"
        # 多网站类型时,需要增加src_url参数,具体请参考api文档: http://docs.fateadm.com/web/#/1?page_id=6
        rsp = api.Predict(pred_type, data)  # 直接返回识别结果
        # rsp = api.PredictFromFile(pred_type, file_name)  # 返回详细识别结果
        result = rsp.pred_rsp.value
        if flag:
            LOG("识别结果: " + result)
        '''
        # 如果不是通过文件识别,则调用Predict接口:
        # result 			= api.PredictExtend(pred_type,data)   	# 直接返回识别结果
        rsp             = api.Predict(pred_type,data)				# 返回详细的识别结果
        '''

        just_flag = False
        if just_flag:
            if rsp.ret_code == 0:
                # 识别的结果如果与预期不符,可以调用这个接口将预期不符的订单退款
                # 退款仅在正常识别出结果后,无法通过网站验证的情况,请勿非法或者滥用,否则可能进行封号处理
                api.Justice(rsp.request_id)
        return result
    except:
        print("识别失败")
        return False
    # card_id         = "123"
    # card_key        = "123"
    # 充值
    # api.Charge(card_id, card_key)


if __name__ == "__main__":
    # image_identify()
    pass

自己写的将验证码保存本地并识别+登录的代码:

import requests
from lxml import etree
from fateadm_api import image_identify

if __name__ == '__main__':
    url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 '
                      '(KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
    }
    page_text = requests.get(url=url, headers=headers).text
    tree = etree.HTML(page_text)
    code_img_src = 'https://so.gushiwen.cn' + tree.xpath('//*[@id="imgCode"]/@src')[0]
    session = requests.session()
    img_data = session.get(url=code_img_src, headers=headers).content
    # # 将验证码图片保存到本地
    # with open('./code.jpg','wb') as fp:
    #     fp.write(img_data)

    # 调用打码平台对验证码图片进行识别
    result = image_identify(img_data, True)
    param = {
        'from': 'http://so.gushiwen.cn/user/collect.aspx',
        'email': 'xxxxxxx',
        'pwd': 'xxxxxxx',  # 记得换成自己的用户名和密码
        'code': result,
        'denglu': '登录'
    }

    r = session.post(url=url, data=param, headers=headers)
    print(r.status_code) # 打印出相应状态码,如果是200则表示成功
    if '修改密码' in r.text:
        print(r.text)
    else:
        print('unsuccess')

要注意把代码中的xxx部分换成自己的。

模拟登录cookie操作

在上述代码中要注意用到了session,这相当于一个会话,因为登录和打印登陆后的页面信息是两个操作,python会将其当成两个独立的操作去进行,那么最后必定会失败;只有使用会话session后,python程序才能明白这两个操作要在一起做,他会保存前一个步骤的cookie,然后放在下一个步骤中继续使用这个cookie去进行相关的操作。

在网站登录后会获得cookie,带着cookie去请求如书架上的url,就可以获取书架上的内容。可以使用session去进行请求,session可以理解为一连串的请求,在这个过程中cookie不会丢失。下面是一个例子:

网站url:https://www.17k.com
首先信息搜集,抓登录时的包(我这里不知道咋回事,浏览器始终抓不到,我就用burp了)
在这里插入图片描述
重点的地方都在图中圈起来了,这些信息需要在写代码时用到。

import requests

if __name__ == '__main__':
    # 会话
    session = requests.session()
    url = 'https://passport.17k.com/ck/user/login'
    data = {
        'loginName': 'xxxx',
        'password': 'xxxx'  # 这里填写自己的用户名和密码
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                      'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36'
    }
    r = session.post(url=url, data=data, headers=headers)
    # print(r.text) # 可以先看一看此时的文本内容是什么样子的
    print(r.cookies)

输出的cookie值:
在这里插入图片描述

获取书架上的书名

目的是获取书架上的书名,如果像前面一样直接请求,会因为没有cookie而导致请求失败,这里用session就可以请求成功。

import requests

if __name__ == '__main__':
    # 会话
    session = requests.session()
    url = 'https://passport.17k.com/ck/user/login'
    data = {
        'loginName': 'shy_shy19',
        'password': 'xay666'
    }
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                      'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36'
    }
    r = session.post(url=url, data=data, headers=headers)
    # print(r.text)
    # print(r.cookies)

    # 2.拿书架上的书名
    # 刚才的那个session中是有cookie的
    name_url = 'https://user.17k.com/ck/author/shelf?page=1&appKey=2406394919'
    r1 = session.get(url=name_url, headers=headers)
    print(r1.json())

爬取有防盗链的视频

网站url:https://www.pearvideo.com/

打开网站后随意选取一个视频,进去后发现在开发者工具(f12)中是有这个视频src的,但是,打开查看网页源码后发现并不能找到,这说明该网站的视频是动态加载出来的,
在这里插入图片描述
在这里插入图片描述
再了解一个关于格式化字符串的小知识:

从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。
使用方法:
1、在字符串前面加上 f。
2、在字符串内部,将需要连接的变量用 {} 括起来即可。

这时可以先拿到视频的contid,然后用上面的方法将contid拼接到url中,实现访问视频

import requests
from lxml import etree

if __name__ == '__main__':
    # 1.拿到contid
    url = 'https://www.pearvideo.com/video_1722063'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                      'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36'
    }
    contid = url.split("_")[1]
    # print(contid)
    # 2.拿到videostatus返回的json -》srcurl
    # 3.srcurl里的内容进行修改
    videostatus = f'https://www.pearvideo.com/videoStatus.jsp?contId={contid}&mrd=0.3315848796652432'
    # 4.下载视频
    r = requests.get(url=videostatus, headers=headers)
    print(r.text)

但是这时候运行发现会显示视频已下线,这说明该网站设置了防盗链,在headers字典里加一个Referer就可以解决,这个头是表示用户从哪一个网页进来的,下面的是正确代码:

import requests
from lxml import etree

if __name__ == '__main__':
    # 1.拿到contid
    url = 'https://www.pearvideo.com/video_1722063'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                      'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36',
        'Referer': 'https://www.pearvideo.com/video_1722063'
    }
    contid = url.split("_")[1] # 以下划线为分隔,取从0开始的列表的第一个内容
    # print(contid)
    # 2.拿到videostatus返回的json -》srcurl
    # 3.srcurl里的内容进行修改
    videostatus = f'https://www.pearvideo.com/videoStatus.jsp?contId={contid}&mrd=0.3315848796652432'
    # 4.下载视频
    r = requests.get(url=videostatus, headers=headers)
    dic = r.json() # 将含有视频地址的json数据保存为一个字典
    srcUrl = dic['videoInfo']['videos']['srcUrl'] # 定位到字典中srcurl处
    systemTime = dic['systemTime'] # 定位到要被替换的systemTime处
    # print(systemTime)
    srcUrl = srcUrl.replace(systemTime, f'cont-{contid}') # 将systemTime替换为contid
    print(srcUrl)
    # 保存视频到本地
    with open('./a.mp4', 'wb') as fp:
        fp.write(requests.get(url=srcUrl).content)

代理

原理:通过第三方机器去发送请求

# 代理ip:49.89.103.216:9999
import requests

if __name__ == '__main__':
    proxies = {
    	'http':'',
        'https': 'https://xxx.xxx.xxx.xxx:xxxx' # 代理的ip地址和端口号
    }
    url = 'https://www.baidu.com'
    r = requests.get(url=url, proxies=proxies)
    r.encoding = 'utf-8'
    print(r.text)

但我运行的时候发现一堆报错,最多的是拒绝连接,百度也没百度到啥正确的解决方法,就没有往后面搞了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
`requests`是一个非常流行的Python库,用于发送HTTP请求,非常适合进行API交互和数据抓取。以下是一些requests库的进阶内容: 1. **会话管理(Session)**:requests中的Session对象可以保存cookies和auth信息,这样可以在整个会话中保持登录状态,提高效率。例如: ```python import requests s = requests.Session() s.get('https://example.com/login', auth=('username', 'password')) response = s.get('https://protected_area.com') ``` 2. **参数编码和文件上传**:你可以使用`data`或`files`参数来发送POST请求,其中`data`用于URL编码的数据,`files`用于上传二进制文件: ```python params = {'key': 'value'} response = requests.post('https://upload-api.example.com', params=params, files={'file': open('file.txt', 'rb')}) ``` 3. **重定向处理**:默认情况下,requests会自动处理HTTP状态码为3xx的重定向。如果需要自定义重定向策略,可以使用`allow_redirects`参数: ```python response = requests.get('http://example.com/redirect', allow_redirects=False) ``` 4. **异常处理**:requests返回的是一个`Response`对象,可以通过检查其`status_code`属性来判断请求是否成功。如果失败,可以捕获`requests.exceptions.RequestException`来处理错误: ```python try: response = requests.get('http://nonexistent.url') except requests.exceptions.RequestException as e: print(f"Request failed: {e}") ``` 5. **设置超时和代理**:可以使用`timeout`参数设置连接和读取超时时间,`proxies`参数设置代理服务器: ```python response = requests.get('http://example.com', timeout=10, proxies={'http': 'http://proxy.example.com'}) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lum1n0us

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值