Python爬虫:模拟登录知乎完全详解

專 欄

sunhaiyu,Python中文社区专栏作者

专栏地址:

http://www.jianshu.com/u/4943cb2c6ea4

这几天在研究模拟登录, 以知乎 - 与世界分享你的知识、经验和见解为例。实现过程遇到不少疑问,借鉴了知乎xchaoinfo的代码,万分感激!

知乎登录分为邮箱登录手机登录两种方式,通过浏览器的开发者工具查看,我们通过不同方式登录时,网址是不一样的。邮箱登录的地址email_url = 'https://www.zhihu.com/login/email',手机登录网址是phone_url = 'http://www.zhihu.com/login/phone_num'。

1. 建立一个可以传Cookie的opener

当登录某个网站时,浏览器会提醒你是否保存账号密码的信息以便下次自动登录,免除了手动输入的麻烦。这个过程实际就是把cookie保存到了本地,下次登录时浏览器加载cookie数据,就实现了自动登录。默认的urllib.requst.urlopen()只有url, data,timeout三个参数,是不携带cookie等信息的。http.cookiejar库可以很好的帮助我们,有CookieJar,FileCookieJar两大类,其中CookieJar可以把cookie信息传给一个变量,而如其名FileCookieJar可以将cookie存到本地文件。 其中FileCookieJar的子类LWPCookieJar,可以存Set-Cookie3类型的文件,而MozillaCookieJar子类是存为/.txt格式的文件。这里我使用LWPCookieJar。

这样就建立了一个可以保存cookie的实例对象,它还有一个方法load()可以从本地加载已存的cookie数据,这跟浏览器帮我们自动登录所做的事差不多,这样我们就不用向网站POST登录所需的数据了。

其中参数ignore_discard=True表示即使cookies将被丢弃也把它保存下来,它还有另外一个参数igonre_expires表示当前数据覆盖(overwritten)原文件。

现在建立一个可以处理cookies的opener。

接下来我们可以使用opener.open()来传入url和data了。

2. 获取登录所需关键参数

模拟登录知乎,除了要POST自己的账号密码,还有两个动态生成的参数,一个是_xsrf,还有一个就是讨厌的验证码了。我们输入账号密码后,通过开发者工具找到一个名为email的json文件,请求方法是POST,可以看到其域名是https://www.zhihu.com/login/email.我用火狐的开发者工具,点开这个文件看到里面的参数。

与chrome对应的好像是Headers下面的Form Data,找到了,在Network里面勾选preserve log就行了。

post的数据查看

这里就把本人邮箱和密码的给打码了哈,嘿嘿。

获取_xsrf

这个参数是动态变化了,所以不能获取一次后就一劳永逸。从html的body里面搜索下_xsrf,然后用正则表达式匹配出来就行。

获取验证码

虽然现在知乎登录很少遇到要输入验证码的时候(反正我很少),但是遇到时你没有post这个参数过去,是不能成功登录的。

知乎的验证码开始把我给坑了,在html内容的页面里搜索能看到验证码图片的网址,但是实际用xxx.read().decode('utf-8')获取到的网页内容是没有这个网址的,它被隐藏了!好狡诈。研究无果只好搜索,从知乎上这个问题xchaoinfo的回答找到答案,结果是这个图片网址中的一串数字就是时间戳。

验证码的链接

时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。

通过time.time()可以查看当前时间戳。比如我当前是1471771678.5400066,看是不是和上面红框中的很接近!通过过换算关系t = time.time() * 1000可以得到图片链接的关键部分,其余部分都是不变的。验证码的完整地址为captcha_url = 'http://www.zhihu.com/captcha.gif?r=' + t + "&type=login"。好了,链接知道了,下载下来查看并手动输入就行了。再把这个post过去应该就可以登录成功了,好激动。

3. 尝试模拟登录知乎

关键的两个东西我们都获取到了,现在登录一下试试吧!要将登录方式考虑进去,如果检测到用于输入手机号,则我们应该访问手机登录网址;否则就是邮箱登录。

关于那句print((json.loads(result))['msg']) ,opener携带数据post过去,请求网址得到的响应会返回登录信息,该数据时json类型,若要查看则用以上语句,eval()函数也是可以的。刚开始我一直是登录失败的,返回这样的玩意儿!

这里一定注意,登录是一个连贯的过程。这个过程我们总共有三次访问网址,一定保证包括获取动态参数,获取验证码、最终模拟登陆都使用同一个opener。这也是登录失败的原因之一,因为刚开始获取_xsrf和验证码时用的是urlopen()。urllib标准库并不很强大,可以尝试requests库,会让这个过程变得简单。

看下结果吧。如果显示登录成功,可以访问以下个人资料的网址,这个如果登录失败了是不能查看的,会返回到登录界面的网址。

个人资料网页

好了,这次模拟登录感觉与前两次学习比起来难度加深了,好多问题还得靠搜索才能解决。加油吧。

这里贴上全部代码。

更新

今天翻看了下requests的文档,学了点urllib库再看这个不算很难。我用requests最基本的函数重新实现以上功能,当然大部分代码是重复的。

requests.get()类似urllib.request.urlopen()。如其名是以get方式请求的,接收url,字典形式的headers,timeout,allow_redirects等参数,当然还有requests.post(),可以传入data参数,不像urllib一样需要对字典形式的data进行编码,requests它会自动处理并且data可以传入json数据。

allow_redirects这是参数可选True、False,默认True,若选False则表示禁止重定向,按我的理解即禁止自动跳转。

看下两个库的区别,返回来的response可选text和content,其中text以文本形式返回,content以二进制数据形式返回,比如我们请求的网址是图片,就返回content,便可以以wb方式写入文件了。看下这两个库在实现返回网页内容的区别。对了返回的对象如response还有一个属性是status_code访问成功了当然就返回的200啦。

然后就是requests.Session()或者requests.session(),大小写一样的,我也不知道这样有啥区别。看类型都是一个class 'requests.sessions.Session'这样的类。requests.Session()会新建一个会话,可以把同一用户的不同请求联系起来,直到会话结束都会自动处理cookies,这比urllib方便多了。如果只使用requests.get()或者requests.post()每次访问网页都是独立进行的,并没有把当前用户的多次访问关联起来,故而模拟登录需要用到requests.Session()。然后再用新建的session使用post(),get()等函数。如下。

这只是requests的冰山一角!它强大着呢。号称是HTTP FOR HUMAN。不过掌握以上基本的东西,足够重写模拟登录知乎的程序。进一步学习请看requests文档,那里有详细介绍。

再放requests版本的。

本文为作者原创作品,未经作者授权同意禁止转载

原文发布于微信公众号 - Python中文社区(python-china)

原文发表时间:2017-07-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏MongoDB中文社区

MongoDB 新功能介绍-Change Streams

MongoDB 3.6已经GA有一段时间,网络上对于该版本新特性的详细介绍文章比较少为此借机会对部分新特性做一个相对详细的介绍。基于早期MongoDB版本实现如...

43620
来自专栏小樱的经验随笔

BugkuCTF web基础$_POST

32530
来自专栏美团技术团队

RestQL:现代化的 API 开发方式

koa-restql 已经在 github 开源并在 npm 发布。感兴趣的同学可以前往围观一下。欢迎 Pull Request,同时热烈欢迎 Star。 在现...

41050
来自专栏酷玩时刻

微信公众号开发之模板消息

其实这个问题很简单,在【微信公众平台】现在已经可以直接查看自己的公众号都能使用那些接口。

38430
来自专栏技术栈大杂烩

Python爬虫: CU shell 板块

身为运维岗的小屌丝, 对于shell, 甚至文本三剑客, 总是不可分割的, 因为工作上, 特别是日志处理查找等, 更能体现出三剑客的强大功力. 虽然说教程网上都...

16330
来自专栏yang0range

内存溢出和内存泄露

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了...

22810
来自专栏玄魂工作室

老司机教你下载tumblr上视频和图片的正确姿势

本文面向初学者。 很多同学问我:“我非常想学Python编程,但是找不到兴趣点”。 还有的同学呢,找到了很好的兴趣点,但是无从下手,“玄魂老师,我想下载tumb...

1.4K70
来自专栏FreeBuf

Flash XSS检测脚本的简单实现

前言 这里主要是讲如何快速扫描到有问题的flash文件,用于批量,有时候很笨的方法也会有奇效,下面记录一下在实现过程中的一些思路和遇到的一些坑。 ? 第三方插件...

32550
来自专栏大内老A

《WCF技术剖析》博文系列汇总[持续更新中]

近半年以来,一直忙于我的第一本WCF专著《WCF技术剖析(卷1)》的写作,一直无暇管理自己的Blog。在《WCF技术剖析(卷1)》写作期间,对WCF又有了新的感...

18180
来自专栏坚毅的PHP

进程、线程、轻量级进程、协程和go中的Goroutine 那些事儿

电话面试被问到go的协程,曾经的军伟也问到过我协程。虽然用python时候在Eurasia和eventlet里了解过协程,但自己对协程的概念也就是轻量级线程,还...

52330

扫码关注云+社区

领取腾讯云代金券