python 爬虫 入门 三、登录以及代理。

目录

一、登录

(一)、登录4399

1.直接使用Cookie

2.使用账号密码进行登录 

        可选观看内容,使用python对密码进行加密(无结果代码,只有过程分析)

二、代理

免费代理

后续:协程,抓取视频


        这节我们来尝试一下登录和代理。

一、登录

        很多网站登录和不登陆显示的内容是不一样的,这主要和Cookie有关。用户先向网站发送账号密码以获取Cookie作为凭证,之后用户发送请求时,携带着Cookie就能让网页知道你是谁了,经过一段时间后,Cookie失效(过有效时段之类的)就需要重新登陆。

(一)、登录4399

        4399童年回忆(应该有账号吧?这个账号注册可能要实名了,不想注册的换个其他的不需要验证码登录的网站,差不多),今天我们就试试进入登录4399新用户-4399用户中心_4399.com这个网址:

但你没Cookie肯定进不去,会跳转到登录界面,咱们先来整点简单的方法

1.直接使用Cookie

        登陆后,我们可以抓到一些请求,其中有个 profile/ 请求,得到它就代表我们成功了。(如果获取到的依然是‘登录4399新用户’代表没登录成功)

        直接看它的标头,其中Cookie这一段就是我们需要的信息,直接全部复制

        之后直接在请求头中加入复制的Cookie就能够登录成功了。 用记事本看一眼打开的结果能看到“我的信息”几个字就代表成功了。

import requests

url = "https://u.4399.com/profile/"
headers = {
    # 用户代理,某些网站验证用户代理,微微改一下,如果提示要验证码之类的,使用它
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0",
    "Cookie":''# 这里用你自己的Cookie
}
session = requests.session()
with session.post(url=url, headers=headers,) as resp:
    resp.encoding = "utf-8"
    print(resp)
    with open("4399_profile.html", mode="w",encoding="utf-8") as f:
        f.write(resp.text)  # 读取到网页的页面源代码"

2.使用账号密码进行登录 

        咱们可以去登录4399新用户-4399用户中心_4399.com这个网站进行登录。我先用i道i做用户名,123456做密码打个样。

        先不要急不可耐的登录抓包,你尝试了就会发现所有抓到的包里面没有跟登录相关的部分。 因为这里跳转了其他的界面,自动清除了之前的抓包结果。咱们先选中装包工具上面的保留日志再开始抓包。(4399采用表单登录,提交表单后跳转到其他界面)

        最后我们可以成功找到 login.do的请求,他的负载里面刚好有username(用户名)和password(密码),但是用户名能看到,密码却不是123456,这是因为有的网站会将密码加密后再发送,或者发送哈希值。(提一句,这种加密啊,哈希啊,很多是不可逆的,所以许多服务器也不知道你的密码具体是什么。)

         接下来,我们就可以根据这条请求来编写我们的代码了。这里使用了session开启了一个会话,第一次请求,会话获取了cookie的值,后面的会话便会自动携带获取的cookie值,以保证连续。可以打断点看看 resp.cookies的变化。

import requests

url = "https://ptlogin.4399.com/ptlogin/login.do?v=1"
url_2 = "https://u.4399.com/profile/"
headers = {
    # 用户代理,某些网站验证用户代理,微微改一下,如果提示要验证码之类的,使用它
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0",
}
data = {
    'loginFrom': 'uframe',
    'postLoginHandler': 'refreshParent',
    'layoutSelfAdapting': 'false',
    'externalLogin': 'qq',
    'displayMode': 'embed',
    'layout': 'vertical',
    'bizId': '',
    'appId': 'u4399',
    'gameId': '',
    'css': 'https://uc.img4399.com/root/css/ptlogin.css?a3993b7',
    'redirectUrl': '',
    'sessionId': '',
    'mainDivId': 'embed_login_div',
    'includeFcmInfo': 'false',
    'level': '0',
    'regLevel': '4',
    'userNameLabel': '4399用户名',
    'userNameTip': '请输入4399用户名',
    'welcomeTip': '欢迎回到4399',
    'sec': '1',
    'password': 'U2FsdGVkX181bGhjYtZJJrfI3NjzazeojBKK+KVCcn4='.encode('utf-8'),  # 这里用你自己的密码
    'username': 'i道i',
}
session = requests.session()
with session.post(url=url, data=data, ) as resp:
    resp.encoding = "utf-8"
    print(resp)
    with open("4399.html", mode="w", encoding="utf-8") as f:
        f.write(resp.text)  # 读取到网页的页面源代码"
    resp = session.get(url=url_2)
    print(resp)
    with open("4399_profile.html", mode="w", encoding="utf-8") as f:
        f.write(resp.text)  # 读取到网页的页面源代码"

 

        可选观看内容,使用python对密码进行加密(无结果代码,只有过程分析)

        接下来,我们就要确定输入的密码是如何变成这段字符的。我们再尝试登陆一次,发现这段字符有变化。这表明它是跟时间有关的加密。(可能有时间种子随机数,所以一般加密都会让线程睡一小会,保证种子随机性足够)

        第一想法是找到登录按钮(或者回车)的回调函数,向后寻找肯定能够发现包装请求的地方。第二想法是请求的url有login.do,我们就先看看网页源代码里有没有login.do存在,那里肯定和发送请求有关,向后找到请求的负载怎么产生的就知道如何加密了。

        通过页面元素检查,找到登录按钮的位置,发现登录按钮没有什么有意义的回调函数,但有id和class,搜索id的值,可以发现上面有该按钮的事件处理器,这就是我们要找的地方。

        或者直接在全部代码中搜索 login.do,刚好只发现一处有login.do,同样是这个代码。

         找到位置后,我们在39行设置一个断点,(点击39数字,前面出现小红点即可)然后再次登录即可在断点处暂停,

        然后选择右上角步入,进入check_login函数中。找到了一处注释有“密文传输的地方”(好注释),通过变量值的变化推断,红框标注的就是加密部分。

 

        接下来,我们就需要通过 python实现上面的 encryptAES 加密,发送请求了。

        AES是一种对称加密技术,即加密解密密钥相同,而所需密钥长度不及16倍数需要补齐,而且补齐方式比较特殊,比如少4个,需要补四个chr(4)。CryptoJS.AES.encrypt默认aes-256,加密模式CBC,填充方式Pkcs7,也就是说,上面的字符不是真正的密钥,而只是密钥“种子而已”。

        这就比较麻烦了,我们需要用python模拟这一复杂过程,为了避免这一麻烦,我们继续往深处走,看看CryptoJS.AES.encrypt函数内部怎么执行的,能不能从中套出真正的密钥。

 通过debug模式的步入,我们找到了这里,这段代码的b就是我们的密码 123456 ,c是密钥短文 'lzYW5qaXVqa' 最终对里面代码分析结果如下:

# encrypt: function(c, d, e) {return a(d).encrypt(b, c, d, e)       # -》3行

# encrypt: function(a, b, c, d) {   # b-》密码 c-》密钥短语 d-》无
#     var e, f;
#     return d = this.cfg.extend(d),
#     e = d.kdf.execute(c, a.keySize, a.ivSize),        # -》13行
#     d.iv = e.iv,  # b是密码,e.key是32位密钥,d是16位偏移量
#     f = x.encrypt.call(this, a, b, e.key, d),-》43行
#     f.mixIn(e),-》75行
#     f
# },

# execute: function(a, b, c, d) { a-》密钥短语 b-》8 c-》4 d-》无
#     var e, g;
#     return d || (d = f.random(8)),    # 生成8位随机数
#     e = l.create({
#         keySize: b + c
#     }).compute(a, d),     # -》28行  最后e长度48
#     g = f.create(e.words.slice(b), 4 * c),    # g是e最后16位
#     e.sigBytes = 4 * b,   # 相当于截断到32位
#     u.create({
#         key: e,
#         iv: g,
#         salt: d
#     })
# }

# compute: function(a, b) { a-》密钥短语 b->8位随机数
#     for (var j, k, c = this.cfg,  # 密钥长度h-》12,迭代次数i-》1
#     d = c.hasher.create(), f = e.create(), g = f.words, h = c.keySize, i = c.iterations; g.length < h; ) {
#         for (j && d.update(j),若j存在,使用j(不执行下几行代码,短路),否则使用哈希器d包含j
#         j = d.update(a).finalize(b),  使用哈希器包含a,并用b计算新哈希值,结果放在j
#         d.reset(),    重置哈希器
#         k = 1; i > k; k++)
#             j = d.finalize(j),
#             d.reset();
#         f.concat(j)将计算得到的哈希值合并起来,简单拼接
#     }
#     return f.sigBytes = 4 * h,
#     f
# }

# encrypt: function(a, b, c, d) {   # cfg,密码,密钥,偏移量
#     var e, f, g;
#     return d = this.cfg.extend(d),
#     e = a.createEncryptor(c, d),  #创建加密器实例
#     f = e.finalize(b),    # 进行加密 -》61行
#     g = e.cfg,
#     u.create({
#         ciphertext: f,    # 16位加密结果
#         key: c,   # 32位密钥
#         iv: g.iv, # 16位偏移量
#         algorithm: a,
#         mode: g.mode,
#         padding: g.padding,
#         blockSize: a.blockSize,
#         formatter: d.format
#     })
# },

# finalize: function(a) {
#                 a && this._append(a);
#                 var b = this._doFinalize();   -》67行
#                 return b
#             },

# _doFinalize: function() {
#     var b, a = this.cfg.padding;
#     return this._xformMode == this._ENC_XFORM_MODE ? (a.pad(this._data, this.blockSize),  对数据进行填充
#     b = this._process(!0)) : (b = this._process(!0),  # 获得最终加密结果
#     a.unpad(b)),
#     b -》16位
# },

# mixIn: function(a) {
#     for (var b in a)
#         a.hasOwnProperty(b) && (this[b] = a[b]);  # 如果b是a的属性,直接复制
#     a.hasOwnProperty("toString") && (this.toString = a.toString)
# },

# stringify: function(a) {  # toString内部,
#     var d, b = a.ciphertext, c = a.salt;  # ciphertext加密结果16位,salt 8位随机数
#     return d = c ? f.create([1398893684, 1701076831]).concat(c).concat(b) : b,
#     d.toString(j)
# },

        也就是说,我们仿照上面的过程,产生最后一个函数的结果:d.toString(j)即可,

        如果认为麻烦,可以尝试固定execute的随机数,这样密钥和偏移量就固定了,只需根据43行的函数,对密码使用固定密钥加密(AES,CBC,Pkcs7填充方式)之后参照81行的函数,使用加密结果和偏移量合并出完整参数即可。

        有挺多常用的网站,发送消息都加密,所以自己看看,以后遇到加密的负载知道怎么做。这个login.do没有启动器,所以无法查看堆栈,需要自己找,有启动器的请求可以从启动器的链接里快速定位改变的位置。

        这里挖个坑,等说完Selenium后,会单出一篇使用Selenium登录知乎的文章。没填的话提醒我一下。

总而言之,直接使用Cookie比较简单,不过时间久了会失效,账号密码自动化能够持久一点。

二、代理

        有一些网站,会限制ip的访问频率,比如很多登录页面,一天内不能登录超过五次。超过次数了,你这个ip就无法访问这个网站了,也就是有人说的封ip。这个时候,我们就需要通过ip代理,让拥有其他ip的计算机代替我们发送或者转发请求。

免费代理

        这里推荐一个网站:免费代理IP [ 实时更新 ] - 站大爷

        有一些免费代理供临时使用,到时候选择响应时间短的一些代理。

        类型有普匿 高匿 透明之分,网站可以通过REMOTE_ADDR、HTTP_VIA、HTTP_X_FORWARDED_FOR这三个值知道你的ip地址。透明和普匿能够让网站知道你在使用代理,高匿不会。普匿和高匿都可以让网站无法知道你的真实IP地址,所以很多人都喜欢用高匿。

REMOTE_ADDRHTTP_VIAHTTP_X_FORWARDED_FOR
真实ip
透明代理ip真实ip
普匿代理ip代理ip代理ip
高匿代理ip

        现在来尝试通过代理访问百度,很多免费代理不支持https,所以访问http://baidu.com

        将ip:端口号拼一起就是代理地址,(便宜没好货,多换几个,总有一个能用)。使用proxies参数即可使用ip

import random

import requests

url = "http://www.baidu.com/"
proxies_all = ["221.6.139.190:9002"]    # 列表中可以放多个代理


def get_proxies(proxies_all):   # 随机获取列表中的一个代理
    ip = random.choice(proxies_all)
    return {  # 有的代理不支持https有的不支持http,注意
        "https": "https://" + ip,
        "http": "http://" + ip,
    }


with requests.get(url=url, proxies=get_proxies(proxies_all)) as resp:
    resp.encoding = "utf-8"  # 当页面乱码改这里
    print(resp)
    print(resp.text)

 

后续:线程,进程,协程

思来想去,还是把协程和抓取视频分开写吧,下一篇写协程。四、线程,进程,协程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/898913.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TitanIDE:解锁编程教学新范式

在高校软件工程类课程教育中&#xff0c;传统编程教学方式正面临着多重痛点&#xff1a; 环境配置繁琐&#xff1a;软件工程类课程往往需要学生自行配置复杂的开发环境。但是&#xff0c;学校硬件设备条件差异、软件兼容性问题等因素&#xff0c;导致学生学习效率低下&#xf…

热销王西圣H1头戴式耳机—全平台售罄断货:揭秘抢购潮究其原因?

西圣xisem作为国内平价享轻奢的领军品牌&#xff0c;就在今年它家的头戴式蓝牙耳机性价比标杆—西圣H1&#xff0c;凭借其发烧级的千元音质、降噪与满级的旗舰配置性能&#xff0c;不仅惊艳了整个耳机圈&#xff0c;还在仅仅的几个月内&#xff0c;西圣H1头戴式耳机已经火爆断货…

python 使用gradio启动程序报错

问题一&#xff1a;localhost is not accessible 解决办法&#xff1a; export no_proxy"localhost,127.0.0.1,::1"

C#学习笔记(三)

C#学习笔记&#xff08;三&#xff09; 第 二 章 命名空间和类、数据类型、变量和代码规范二、类的组成和使用分析1. 基本概念2. 类的内容组成3. 方法的初步理解 第 二 章 命名空间和类、数据类型、变量和代码规范 二、类的组成和使用分析 1. 基本概念 类是程序的基本单元&a…

PostgreSQL中触发器递归的处理 | 翻译

许多初学者在某个时候都会陷入触发器递归的陷阱。通常&#xff0c;解决方案是完全避免递归。但对于某些用例&#xff0c;您可能必须处理触发器递归。本文将告诉您有关该主题需要了解的内容。如果您曾经被错误消息“超出堆栈深度限制”所困扰&#xff0c;那么这里就是解决方案。…

Javascript算法——二分查找

1.数组 1.1二分查找 1.搜索索引 开闭matters&#xff01;&#xff01;&#xff01;[left,right]与[left,right) /*** param {number[]} nums* param {number} target* return {number}*/ var search function(nums, target) {let left0;let rightnums.length-1;//[left,rig…

大话网络协议:从OSI七层模型说开去

时至今日,互联网已经是大家日常生活中不可或缺的一部分,购物、点餐、刷剧、网课,已经融入了我们生活的方方面面。但网络具体是怎么工作的呢? 特别是我们具体从事软件研发、ICT行业的同学,理解和掌握这个我们产品运行的基础设施尤为必要。 本文,我们会力争用最简单易懂的…

秋季猫咪疯狂掉毛,宠物空气净化器有用吗?性价比高的该怎么选?

我家猫真的是换季就变掉毛怪&#xff0c;整只猫“虚胖”了一大圈不止&#xff0c;在阳光下可以看见非常多飘在空气中的浮毛。浮毛到处乱飞&#xff0c;沉积在黑色的衣服上&#xff0c;就形成白色的薄膜。自从养猫后&#xff0c;我再也没穿过深色的衣服。 现在每天都给它梳毛&am…

Linux文件的查找和打包以及压缩

文件的查找 文件查找的用处&#xff0c;在我们需要文件但却又不知道文件在哪里的时候 文件查找存在着三种类型的查找 1、which或whereis&#xff1a;查找命令的程序文件位置 2、locate&#xff1a;也是一种文件查找&#xff0c;但是基于数据库的查找 3、find&#xff1a;针…

Vue.js 学习总结(9)—— Vue 3 组件封装技巧

1、需求说明 需求背景&#xff1a;日常开发中&#xff0c;我们经常会使用一些UI组件库诸如and design vue、element plus等辅助开发&#xff0c;提升效率。有时我们需要进行个性化封装&#xff0c;以满足在项目中大量使用的需求。错误示范&#xff1a;基于a-modal封装一个自定…

【AIGC半月报】AIGC大模型启元:2024.10(下)

【AIGC半月报】AIGC大模型启元&#xff1a;2024.10&#xff08;下&#xff09; (1) Janus&#xff08;两面神&#xff09;&#xff08;DeepSeek 1.3B多模态大模型&#xff09;(2) Stable Diffusion 3.5&#xff08;StabilityAI文生图大模型&#xff09;(3) Mochi 1&#xff08;…

Python文件操作(读取、写入、修改和删除)

目录 一、文件的读取 二、文件的写入 三、文件的修改 四、文件的删除 Python是一种功能强大的编程语言&#xff0c;文件操作是编程中常见的需求。本文将详细介绍Python中的文件操作&#xff0c;包括文件的读取、写入、修改和删除&#xff0c;帮助读者掌握Python文件操作的基…

分布式系统之异步与消息队列(MQ)(原理+代码实战一文讲清!)

异步 什么是异步 异步编程是一种编程范式&#xff0c;它允许程序在等待操作完成&#xff08;如等待网络响应、文件读写等&#xff09;时继续执行其他任务。这种编程方式对于提高程序的性能和响应性至关重要&#xff0c;尤其是在处理耗时操作或在资源受限的环境中。下面我将更…

山东以“八策并举”确保人民满意学前教育“普惠落地”

10月19日-22日&#xff0c;2024年中国学前教育研究会学术年会在山东国际会展中心召开。年会围绕“优质普惠可持续——加强学前教育高质量发展的法治保障”主题&#xff0c;通过5场主旨报告、28个园所观摩、10个分论坛交流研讨&#xff0c;为2200余名嘉宾提供智慧盛宴。成为近年…

URP学习四

一.Bilt To RTHandle feature代码&#xff1a; 二.DistortTunnel 只有个飞机却有很多太空场景。因为设置了其他pass来渲染背景 队列添加3个Pass&#xff1a; 第一个Pass把颜色图进行输出 第二个Pass&#xff1a;创建了个纹理 加了个扰动&#xff0c;把纹理进行输出 第三个pas…

Postman使用-基础篇

前言 本教程将结合业界广为推崇和使用的RestAPI设计典范Github API&#xff0c;详细介绍Postman接口测试工具的使用方法和实战技巧。 在开始这个教程之前&#xff0c;先聊一下为什么接口测试在现软件行业如此重要&#xff1f; 为什么我们要学习Postman&#xff1f; 现代软件…

电子木鱼小游戏小程序源码系统 带完整的安装代码包以及搭建部署教程

系统概述 在快节奏的生活中&#xff0c;人们越来越注重内心的平静与放松。电子木鱼小游戏小程序正是基于这一需求而诞生的&#xff0c;它将传统的木鱼文化与现代科技相结合&#xff0c;为用户提供了一个简单、方便、有趣的冥想与放松工具。通过敲击屏幕上的虚拟木鱼&#xff0…

Windows 下 golang 多版本管理

三年前的旧文&#xff0c;最新要切版本&#xff0c;翻了出来&#xff0c;现在依然有用&#xff0c;分享出来~ 当前 golang 的各个版本还有些不兼容的问题&#xff0c;最近遇到 go-micro 框架只能运行在 go1.13~1.14 的版本情况&#xff0c;而我本地 windows 环境安装的 Golang …

C++ [项目] 愤怒的小鸟

现在才发现C游戏的支持率这么高&#xff0c;那就发几篇吧 零、前情提要 此篇为 制作,由于他没有CSDN,于是由我代发 一、基本介绍 支持Dev-C5.11版本(务必调为英文输入法),基本操作看游戏里的介绍,怎么做的……懒得说,能看懂就看注释,没有的自己猜,如果你很固执……私我吧 …

蘑菇书(EasyRL)学习笔记(1)

1、强化学习概述 强化学习&#xff08;reinforcement learning&#xff0c;RL&#xff09;讨论的问题是智能体&#xff08;agent&#xff09;怎么在复杂、不确定的环 境&#xff08;environment&#xff09;里面去最大化它能获得的奖励。如下图所示&#xff0c;强化学习…