Python爬虫初学(三)—— 模拟登录知乎
模拟登录知乎
这几天在研究模拟登录, 以知乎 - 与世界分享你的知识、经验和见解为例。实现过程遇到不少疑问,借鉴了知乎xchaoinfo的代码,万分感激!
知乎登录分为邮箱登录和手机登录两种方式,通过浏览器的开发者工具查看,我们通过不同方式登录时,网址是不一样的。邮箱登录的地址email_url = 'https://www.zhihu.com/login/email'
,手机登录网址是phone_url = 'http://www.zhihu.com/login/phone_num'
。
1. 建立一个可以传Cookie的opener
Cookie用来跟踪用户是否已经登录的状态信息。一旦网站认证了我们的登录,就会将cookie存到浏览器中,里面包含了服务器生成的令牌、登录有效时长、状态跟踪信息。当登陆有效时长达到,我们的登录状态就被清空,想要访问其他需要登录后才能访问的页面也就不能成功了。意思就是我们能保持登录状态不掉线,是因为服务器端核对了我们访问时携带的cookie,核对成功后服务器端就认为我们时“已经登录了”的状态。urllib.requst.urlopen()
可传入的参数中,都不能携带cookie信息。http.cookiejar
的FileCookieJar
可以帮到我们,FileCookieJar
可以将cookie存到本地文件中。 其中FileCookieJar
的子类LWPCookieJar
,可以存Set-Cookie3
类型的文件,而MozillaCookieJar
子类是存为.txt
格式的文件。这里我使用LWPCookieJar
。
filename = 'cookie'
cookie = http.cookiejar.LWPCookieJar(filename)
这样就建立了一个可以保存cookie的实例对象,它还有一个方法load()
可以从本地加载已存的cookie数据,这样我们就可以携带着cookie(相当于带了一块令牌)访问服务器,服务器核对成功后,就可以访问那些登录后才能访问的页面。
try:cookie.load(ignore_discard=True)
except IOError:print('Cookie未加载!')
其中参数
ignore_discard=True
表示即使cookies将被丢弃也把它保存下来,它还有另外一个参数igonre_expires
表示当前数据覆盖(overwritten)原文件。
现在建立一个可以处理cookie的opener。
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 ''(KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36',"Host": "www.zhihu.com","Referer": "https://www.zhihu.com/",}
# 创建一个可以处理cookies的opener
opener = request.build_opener(request.HTTPCookieProcessor(cookie))
# 给openner添加headers, addheaders属性接受元组而非字典
opener.addheaders = [(key, value) for key, value in headers.items()]
接下来我们可以使用opener.open()
来传入url和data了。
2. 获取登录所需关键参数
模拟登录知乎,除了要POST自己的账号密码,还有两个动态生成的参数,一个是_xsrf
,还有一个就是讨厌的验证码了。我们输入账号密码后,通过开发者工具找到一个名为email的json文件,请求方法是POST,可以看到其域名是https://www.zhihu.com/login/email
.我用火狐的开发者工具,点开这个文件看到里面的参数。
与chrome对应的好像是Headers下面的Form Data,在Network里面勾选preserve log就可以看到。
这里就把本人邮箱和密码的给打码了哈,嘿嘿。
获取_xsrf
这个参数是动态变化了,所以不能获取一次后就一劳永逸。从html的body里面搜索下_xsrf
,然后用正则表达式匹配出来就行。
def get_xsrf():"""获取参数_xsrf"""req = opener.open('https://www.zhihu.com')html = req.read().decode('utf-8')get_xsrf_pattern = re.compile(r'<input type="hidden" name="_xsrf" value="(.*?)"')# 这里会返回多个值,不过都是一样的内容,取第一个就行_xsrf = re.findall(get_xsrf_pattern, html)[0]return _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过去应该就可以登录成功了,好激动。
def get_captcha():"""获取验证码本地显示返回你输入的验证码"""t = str(int(time.time() * 1000))# 验证码完整网址captcha_url = 'http://www.zhihu.com/captcha.gif?r=' + t + "&type=login"# 下载验证码图片,用下面注释掉的方法一样的效果request.urlretrieve(captcha_url, 'cptcha.gif')# image_data = request.urlopen(captcha_url).read()# with open('cptcha.gif', 'wb') as f:# f.write(image_data)# 用Pillow库显示图片,免去手动去文件夹打开的麻烦im = Image.open('cptcha.gif')im.show()captcha = input('本次登录需要输入验证码: ')return captcha
3. 尝试模拟登录知乎
关键的两个东西我们都获取到了,现在登录一下试试吧!要将登录方式考虑进去,如果检测到用于输入手机号,则我们应该访问手机登录网址;否则就是邮箱登录。
def login(username, password):"""输入自己的账号密码,模拟登录知乎"""# 检测到11位数字则是手机登录if re.match(r'\d{11}$', username):url = 'http://www.zhihu.com/login/phone_num'data = {'_xsrf': get_xsrf(),'password': password,'remember_me': 'true','phone_num': username}else:url = 'https://www.zhihu.com/login/email'data = {'_xsrf': get_xsrf(),'password': password,'remember_me': 'true','email': username}# 若不用验证码,直接登录post_data = parse.urlencode(data).encode('utf-8')r = opener.open(url, post_data)result = r.read().decode('utf-8')# 打印返回的响应,r = 1代表响应失败,msg里是失败的原因# 要用验证码,post后登录if (json.loads(result))["r"] == 1:data['captcha'] = get_captcha()post_data = parse.urlencode(data).encode('utf-8')r = opener.open(url, post_data)result = r.read().decode('utf-8')print((json.loads(result))['msg'])# 保存cookie到本地cookie.save(ignore_discard=True, ignore_expires=True)
关于那句print((json.loads(result))['msg'])
,opener携带数据post过去,请求网址得到的响应会返回登录信息,该数据是json类型,刚开始我一直是登录失败的,返回这样的玩意儿!
{
"r": 1,
"errcode": 1991829,"data": {"captcha":"\u9a8c\u8bc1\u7801\u9519\u8bef"},
"msg": "\u9a8c\u8bc1\u7801\u9519\u8bef"
}
r为1表示登录失败,0表示登录成功。msg里反映的是登录失败的原因是”验证码会话失效“。
这里一定注意,登录是一个连贯的过程。这个过程我们总共有三次访问网址,一定保证包括获取动态参数,获取验证码、最终模拟登陆都使用同一个opener。这也是登录失败的原因之一,因为刚开始获取_xsrf
和验证码时用的是urlopen()
。urllib
标准库并不很强大,可以尝试requests
库,会让这个过程变得简单。
看下结果吧。如果显示登录成功,可以访问以下个人资料的网址,这个如果登录失败了是不能查看的,会返回到登录界面的网址。
好了,这次模拟登录感觉与前两次学习比起来难度加深了,好多问题还得靠搜索才能解决。加油吧。
这里贴上全部代码。
import re
from urllib import parse, request
import http.cookiejar
from PIL import Image
import time
import json# 建立LWPCookieJar实例,可以存Set-Cookie3类型的文件。
# 而MozillaCookieJar类是存为'.txt'格式的文件
cookie = http.cookiejar.LWPCookieJar('cookie')
# 若本地有cookie则不用再post数据了
try:cookie.load(ignore_discard=True)
except IOError:print('Cookie未加载!')headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 ''(KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36',"Host": "www.zhihu.com","Referer": "https://www.zhihu.com/",}
opener = request.build_opener(request.HTTPCookieProcessor(cookie))
# 给openner添加headers, addheaders方法接受元组而非字典
opener.addheaders = [(key, value) for key, value in headers.items()]def get_xsrf():"""获取参数_xsrf"""response = opener.open('https://www.zhihu.com')html = response.read().decode('utf-8')get_xsrf_pattern = re.compile(r'<input type="hidden" name="_xsrf" value="(.*?)"')_xsrf = re.findall(get_xsrf_pattern, html)[0]return _xsrfdef get_captcha():"""获取验证码本地显示返回你输入的验证码"""t = str(int(time.time() * 1000))captcha_url = 'http://www.zhihu.com/captcha.gif?r=' + t + "&type=login"# 获取验证码也用同一个openerimage_data = opener.open(captcha_url).read()with open('cptcha.gif', 'wb') as f:f.write(image_data)im = Image.open('cptcha.gif')im.show()captcha = input('本次登录需要输入验证码: ')return captchadef login(username, password):"""输入自己的账号密码,模拟登录知乎"""# 检测到11位数字则是手机登录if re.match(r'\d{11}$', username):url = 'http://www.zhihu.com/login/phone_num'data = {'_xsrf': get_xsrf(),'password': password,'remember_me': 'true','phone_num': username}else:url = 'https://www.zhihu.com/login/email'data = {'_xsrf': get_xsrf(),'password': password,'remember_me': 'true','email': username}# 若不用验证码,直接登录post_data = parse.urlencode(data).encode('utf-8')r = opener.open(url, post_data)result = r.read().decode('utf-8')# 打印返回的响应,r = 1代表响应失败,msg里是失败的原因# 要用验证码,post后登录if (json.loads(result))["r"] == 1:data['captcha'] = get_captcha()post_data = parse.urlencode(data).encode('utf-8')r = opener.open(url, post_data)result = r.read().decode('utf-8')print((json.loads(result))['msg'])# 保存cookie到本地cookie.save(ignore_discard=True, ignore_expires=True)def isLogin():# 通过查看用户个人信息来判断是否已经登录url = 'https://www.zhihu.com/settings/profile'# 获得真实网址,可能重定向了actual_url = opener.open(url).geturl()if actual_url == 'https://www.zhihu.com/settings/profile':return Trueelse:return Falseif __name__ == '__main__':if isLogin():print('您已经登录')else:account = input('输入账号:')secret = input('输入密码:')login(account, secret)
更新: 使用requests库重写程序
Date:2016.8.23
今天翻看了下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返回html内容
response = requests.get('https://www.zhihu.com', headers=headers)html = response.text
# urllib返回html内容
req = urllib.request.Request(url)
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
然后就是requests.Session()
或者requests.session()
,两种写法都可以。类型都是一个class 'requests.sessions.Session'
,看下源码其实session
返回的就是一个Session
对象。requests.Session()
会新建一个会话,可以把同一用户的不同请求联系起来,直到会话结束都会自动处理cookies,这比urllib
方便多了。如果只使用requests.get()
或者requests.post()
每次访问网页都是独立进行的,并没有把当前用户的多次访问关联起来,故而模拟登录需要用到requests.Session()
。然后再用新建的session使用post()
,get()
等函数。如下。
session = requests.Session()
session.get(url, headers)
session.post(url, headers, data)
这只是requests
的冰山一角!它强大着呢。号称是HTTP FOR HUMAN。不过掌握以上基本的东西,足够重写模拟登录知乎的程序。进一步学习请看requests文档,那里有详细介绍。
再放requests版本的。
import re
import requests
import http.cookiejar
from PIL import Image
import time
import jsonheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 ''(KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36',"Host": "www.zhihu.com","Referer": "https://www.zhihu.com/",}
# 建立一个会话,可以把同一用户的不同请求联系起来;直到会话结束都会自动处理cookies
session = requests.Session()
# 建立LWPCookieJar实例,可以存Set-Cookie3类型的文件。
# 而MozillaCookieJar类是存为'/.txt'格式的文件
session.cookies = http.cookiejar.LWPCookieJar("cookie")
# 若本地有cookie则不用再post数据了
try:session.cookies.load(ignore_discard=True)
except IOError:print('Cookie未加载!')def get_xsrf():"""获取参数_xsrf"""response = session.get('https://www.zhihu.com', headers=headers)html = response.textget_xsrf_pattern = re.compile(r'<input type="hidden" name="_xsrf" value="(.*?)"')_xsrf = re.findall(get_xsrf_pattern, html)[0]return _xsrfdef get_captcha():"""获取验证码本地显示返回你输入的验证码"""t = str(int(time.time() * 1000))captcha_url = 'http://www.zhihu.com/captcha.gif?r=' + t + "&type=login"response = session.get(captcha_url, headers=headers)with open('cptcha.gif', 'wb') as f:f.write(response.content)# Pillow显示验证码im = Image.open('cptcha.gif')im.show()captcha = input('本次登录需要输入验证码: ')return captchadef login(username, password):"""输入自己的账号密码,模拟登录知乎"""# 检测到11位数字则是手机登录if re.match(r'\d{11}$', username):url = 'http://www.zhihu.com/login/phone_num'data = {'_xsrf': get_xsrf(),'password': password,'remember_me': 'true','phone_num': username}else:url = 'https://www.zhihu.com/login/email'data = {'_xsrf': get_xsrf(),'password': password,'remember_me': 'true','email': username}# 若不用验证码,直接登录result = session.post(url, data=data, headers=headers)# 打印返回的响应,r = 1代表响应失败,msg里是失败的原因# loads可以反序列化内置数据类型,而load可以从文件读取if (json.loads(result.text))["r"] == 1:# 要用验证码,post后登录data['captcha'] = get_captcha()result = session.post(url, data=data, headers=headers)print((json.loads(result.text))['msg'])# 保存cookie到本地session.cookies.save(ignore_discard=True, ignore_expires=True)def isLogin():# 通过查看用户个人信息来判断是否已经登录url = "https://www.zhihu.com/settings/profile"# 禁止重定向,否则登录失败重定向到首页也是响应200login_code = session.get(url, headers=headers, allow_redirects=False).status_codeif login_code == 200:return Trueelse:return Falseif __name__ == '__main__':if isLogin():print('您已经登录')else:account = input('输入账号:')secret = input('输入密码:')login(account, secret)
以上使用到的浏览器UA都是电脑端的,现在知乎出了倒立文字验证码。如果因此而导致登录失败,可以将浏览器UA改成Android端或者IOS端。
by sunhaiyu
2016.8.21
转载于:https://www.cnblogs.com/sun-haiyu/p/7026957.html
Python爬虫初学(三)—— 模拟登录知乎相关推荐
- python爬虫初学实战——免登录爬取easyicon里的vip图标(2)
python爬虫初学实战-免登录爬取easyicon里的vip图标(2) 实验日期:2020-08-09 tips:没看过前面(1)的可以康康,指路 -> 爬取easyicon里的png图标 成 ...
- python爬虫初学实战——免登录爬取easyicon里的vip图标(1)
python爬虫初学入门实战 --无需登录无需vip获得各种大小的png图标,爬取easyicon里的图标并储存 首先附上爬取的网址地址 实验环境 操作系统:windows 10 版本:python ...
- Python 爬虫实战5 模拟登录淘宝并获取所有订单
经过多次尝试,模拟登录淘宝终于成功了,实在是不容易,淘宝的登录加密和验证太复杂了,煞费苦心,在此写出来和大家一起分享,希望大家支持. 本篇内容 python模拟登录淘宝网页 获取登录用户的所有订单详情 ...
- python爬虫实现豆瓣模拟登录
这是豆瓣登录界面URL https://www.douban.com/accounts/login 用pychrm编写爬虫代码 import requests url = 'https://www.d ...
- Python爬虫初学三(网络数据解析)
目录 1.学习正则表达式的原因 2.什么是正则表达式 3.正则表达式匹配规则 4.正则实现步骤 5.Pattern 对象 6.正则模块常量 7.Match 对象 8.search 9.findall和 ...
- python爬虫(四)——模拟登录丁香园
实战 实战小项目:模拟登录丁香园,抓取论坛页面人员基本信息与回复帖子.丁香园论坛:http://www.dxy.cn/bbs/thread/626626#626626 思路 首先把登陆方式由扫码切换为 ...
- python爬虫4.1模拟登录古诗词网并通过Cookie处理获取个人信息
1.前言 好久没有写过笔记了....本来这节内容是模拟登录人人网,但是人人网现在无法注册了...就换了家网站.这个代码是可以模拟登录古诗词网,进入登录成功后跳转的页面后,再进入个人收藏界面,获取个人收 ...
- Python爬虫实战:模拟登录淘宝网站
前言 在完成爬虫任务的时候,我们总是会遇到用户账号登陆的问题,如果自己手动登陆的话,那么工作量就很大了,如何解决登陆问题呢? 今天老师带领大家使用selenium完成淘宝账号登陆 本篇文章知识点: s ...
- Python爬虫实战(三) 免登录爬取东野圭吾超话——看看你喜欢的书上榜没?
微博爬虫可以不借助selenium,直接用Chrome下的手机端模式打开,找到其封装的json数据,即可爬取,具体步骤如下. 分析过程 以东野圭吾超话为例,网址为微博超话.进入页面后,使用Chrome ...
- python爬虫学习之模拟登录淘宝
使用教程 下载chrome浏览器 查看chrome浏览器的版本号,对应版本号的chromedriver驱动 pip安装下列包 pip install selenium 登录微博,并通过微博绑定淘宝账号 ...
最新文章
- 人工智能这条小船何时才能变成航母?
- STL 之includes,set_intersection,set_union,set_difference,set_symmetric_difference
- jvm(13)-线程安全与锁优化
- arm java 性能怎么样,ARM v6上使用java的Number to String转换性能
- 使用html() undefined_SweetAlert2使用教程
- Linux命令(8):headtail命令
- 使用 jsfl 发布flash IDE 插件
- Requests库常用方法及其详解
- html中免费的四级联动,四级联动.html
- 淘宝店铺用ps+dw装修的步骤
- 读书笔记-《Head First 软件开发》
- 服务器经常被攻击怎么办?这7个重要因素要做好!
- CPU运行功耗和什么相关?消耗的电能都去哪了?
- BSON及BSON和JSON的区别
- Spring Boot、Spring MVC、Spring:它们有什么区别?
- 永辉系统服务器,永辉超市供应商服务系统.doc
- SICP-Huffman
- 学计算机必备软件,电脑必备6个黑科技软件,每个都是顶尖,学习、工作必不可少!...
- java怎么输出保留两位小数_剖析Java输出怎么保留两位小数
- 五分钟讲明白DAG(有向无环图)的优缺点
热门文章
- Python 学习 —— Numpy 、Pandas 傻傻分不清楚
- 详解C中volatile关键字
- Linker Tools Error LNK2001 报错
- php extension 安装,php + clucene extension的安装
- redis是单线程的吗?为什么执行速度这么快?
- 034_Unicode标准
- oss 视频 转码_播放oss问题的搜索结果-阿里云开发者社区
- python管理技巧_8个经典的Python列表技巧,让你数据处理更简单!
- MVC,MVP,MVVM设计模式的比较
- SpringBoot开发最佳实践