常常在简书上写做,写完后再发布到其余网站,很是麻烦,因此准备搞一下自动发布文章的工具。那么第一步先要模拟登录几个网站。今天先从知乎开始。python
Python:python3.6 IDE:pycharm 抓包工具:Charles 系统环境:Mac 浏览器:Chrome
首先进行网站登陆抓包,打开Chrome浏览器无痕窗口,而后清空全部缓存,打开Charles,在Chrome浏览器地址栏输入www.zhihu.com,打开知乎登陆界面
web
我是手机号登陆,邮箱没有试过,不知道请求流程是否同样,输入帐号密码,若是须要验证码的话会自动出现验证码输入框,登陆成功后chrome
通常状况下咱们能够输错一次密码或者验证码,来多看看请求流程。另外我用chrome浏览器抓取了好几回登陆流程,发现都不同。因此最后就是综合了一下几回抓包的信息进行分析了。json
登陆请求通常是POST,这个不多有例外,有些网站通常会是第一个post请求,但是知乎的post请求有点颇多,这个请求中有username和password,那咱们就以这里为基准开始分析。看一下红框中出现了两个Authentication和Multipart,在其余的登陆网站中我还没碰到过这种状况,这个Multipart好像以前的Form,Authentication应该是一种认证。api
先搜了一下这个Authentication信息
浏览器
看到是在main.app.xxxxx.js脚本中,打开脚本看一下,基本上是固定值,其实经过屡次请求能够发现这个值是固定的。同时咱们能够肯定其余的一些固定参数。看过我以前文章的同窗应该知道,肯定参数最简单的方式就是屡次请求观察,很容易就能肯定固定值。缓存
经过观察只有signature参数比较麻烦,timestamp是时间戳,captcha是咱们输入的验证码,那么搞定这个参数咱们就离成功很近了,为何是很近而不是成功?这个咱们得记住,通常在分析请求的时候参数只是一部分,咱们要关注headers、cookies、提交参数。首先参数咱们确认了只须要分析signature。那么再来看看headers微信
这里有两种抓包状况,针对这种状况要单独分析,咱们接下来再分析。
而后再说说cookies,有时候咱们并不须要一开始就分析cookies,并且不是cookies中的每一项都是必须的。咱们先把cookies信息截取出来。后面再分析cookie
这里咱们先分析提交参数signature。首先在charles中查询参数值session
没搜到,这种通常都是经过计算生成的,搜不到很正常。既然搜不到值,那咱们来搜一下参数名。
看一下搜索结果,main.app.xxxx.js中的比较像
其余的参数也出如今这里,那基本就是这个地方了。仔细观察一下,SHA-一、setHMACKey、几个update、getHMAC,第一反应是sha1加密,经过固定字符串+grantType+clientId+com.zhihu.web+时间戳进行加密,结果试了一下好像不是。度娘了一下HMAC / sha1,原来还有这种操做,OK这个参数搞定了,贴下代码。
在抓包请求中咱们能够看到,验证码请求应该是https://www.zhihu.com/api/v3/oauth/captcha?lang=en,在有些抓包结果中后面的lang=cn,不知道这俩是怎么区分的。
咱们看到这个请求有三种访问方式,GET、PUT、POST,分别看一下结果。
GET请求访问结果会显示是否须要输入验证码。
PUT请求的返回值是图片的base64加密数据。获得base64数据后转换为图片显示便可
import base64 imgdata = base64.b64decode(result.get("img_base64")) with open("cap.png", "wb") as fp: fp.write(imgdata)
POST请求会返回验证码验证结果。
经过图15的截图(固然POST返回错误是我故意输错验证码的结果)咱们知道验证码并非在登陆请求的时候才进行验证的,须要提早验证。
authorization的值和client_id值相等,那就都是固定值,不用管了。X-Xsrftoken:先搜一下这个值
在上面的请求response中设置的cookies值,那咱们直接从cookies中拿出_xsrf值,设置到headers中便可
X-UDID:跟X-Xsrftoken同样,也是在cookies中设置的
拆分一下便可
既然咱们须要取cookies中的_xsrf和d_c0的值,那么就得保证cookies中必须有这个值,稍微往上翻一下能够看到Set Cookie在最初访问www.zhihu.com中设置的
那么访问一下首页就能够了。分析到这里就差很少了。接下来就是发送请求了。
这种表单也没提交过,有点懵逼。可是看起来跟这个Content-Type: multipart/form-data有关系,请求发送类型。搜一下requests multipart
,找到一个请求库requests_toolbelt,使用方式以下:
from requests_toolbelt import MultipartEncoder data = { "client_id":"xxxx", "grant_type":"password" } boundary = "----WebKitFormBoundaryJKBWBR54EIz5oTAY" encode_data = MultipartEncoder(data, boundary=boundary) headers["Content-Type"] = encode_data.content_type requests.post(url, data=encode_data.to_string())
这里有个boundary须要关注一下,首先几回请求的boundary信息不同,那确定是不同的,因此我大概搜了一下boundary这个值,没发现这个值。度娘了一下WebKitFormBoundary,好像说这个boundary其实不发送也没什么关系。主要是我刚开始写的时候这个boundary默认是空的,结果居然登陆成功了。我以为既然值是随意的,那么有总归比没有好点吧,防止被ban码。WebKitFormBoundary后面那16位大概是大小写字母和数字随机吧。那我就伪造了一下。
def random_boundary(): factor = "ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" boundary = "----WebKitFormBoundary{}".format("".join(random.choices(factor, k=16))) return boundary
好啦,这回算是全都搞定了,最后要验证一下登陆是否成功,访问一下我的中心便可:https://www.zhihu.com/people/wan-rou-2
最后咱们总结一下登陆过程:
def get_captcha(self): captcha = "" url = "https://www.zhihu.com/api/v3/oauth/captcha?lang=en" # 请求头中增长Authorization self.session.headers["Authorization"] = "oauth c3cef7c66a1843f8b3a9e6a1e3160e20" r = self.session.get(url) result = r.json() if not result.get("show_captcha"): # 不须要输入验证码 return "" else: while True: # 获取验证码图片base64 r = self.session.put(url) result = r.json() imgdata = base64.b64decode(result.get("img_base64")) with open("cap.png", "wb") as fp: fp.write(imgdata) im = Image.open("cap.png") im.show() captcha = input("输入验证码>>") #验证码 boundary = self.random_boundary() cap_data = MultipartEncoder(fields={"input_text":captcha}, boundary=boundary) self.session.headers["Content-Type"] = cap_data.content_type # 发送验证码进行验证 r = self.session.post(url, data=cap_data.to_string()) result = r.json() if not result.get("error", None): print("验证码正确") break else: # 验证码不正确,从新请求 print(result.get("error").get("message")) return captcha
若是你以为个人文章还能够,能够关注个人微信公众号:Python爬虫实战之路
也能够扫描下面二维码,添加个人微信号