我们在前面的文章中已经学习了如果使用python进行数据抓取。
但我们常常会遇到一种场景,就是想要获取的页面内容或者接口内容是需要我们登陆后才能看到的。
这时候 就需要进行模拟登录。
使用python来实现跟在界面上操作登录一样的效果。然后进行登录后的数据抓取。
怎么实现这个目的呢,本篇文章会记录相关原理和实际操作。
爬虫的本质是模拟浏览器访问服务器,所以我们首先要了解浏览器和服务器的交互以及登录实现的过程。
在用户访问网页时,不论是通过URL输入域名或IP,还是点击链接,浏览器向WEB服务器发出了一个HTTP请求(Http Request)
WEB服务器接收到客户端浏览器的请求之后,响应客户端的请求,发回相应的响应信息(Http Response)
浏览器解析引擎,排版引擎分析返回的内容,呈现给用户。
WEB应用程序在于服务器交互的过程中,HTTP请求和响应时发送的都是一个消息结构。
当浏览器向服务器发送请求的时候,其实就是发送http请求消息报文。
服务器返回数据时,发送http响应消息报文。
这两种类型的消息都是由一个起始行,消息头,一个指示消息头结束的空行和可选的消息体组成。
http请求消息中
起始行包括 请求方法,请求的资源, HTTP协议的版本号
消息头包含各种属性
消息体包含数据
GET请求并没有消息主体,因此在消息头后的空白行中没有其他数据。
Http响应消息中,起始行包括HTTP协议版本,http状态码和状态,消息头包含各种属性,消息体包含服务器返回的数据内容。
如下图从fiddler抓取的http请求和http响应,GET请求内容为空,故消息头之后的空行和消息体都为空。
服务器发送的响应消息如下,浏览器正常接收到服务器发回的http报文
浏览器通过http来跟服务器进行交互,那么我们只要模拟浏览器 的http消息,造成一模一样的请求,就可以实现跟登录后一样的身份进行访问。
那么一般来说,服务端是根据什么来进行用户身份的识别呢,答案是cookie。
为了实现这种用户标记,服务器就采用了cookie这种机制来识别具体是哪一个用户的访问。
cookie在http请求和http响应的头信息中,cookie是消息头的一种很重要的属性。
为了实现用户标记,在Http无状态请求的基础之上,我们需要在请求中携带一些用户信息,这就是cookie机制。
我们的登录操作,其实就是给用户名和密码给服务端验证,如果身份符合,服务端就会返回cookie的信息给浏览器,浏览器把cookie存在本地后以后每次访问都带着cookie去进行访问,这样就实现了 有用户身份的访问。
可以在浏览器中看到浏览器缓存的cookie
请求时cookie如下:
几乎现在所有的网站都会发送一些 cookie信息过来,当用户请求中携带了cookie信息,服务器就可以知道是哪个用户的访问了,从而不需要再使用账户和密码登录。
但是,刚才也提到了,cookie信息是直接放在Http协议的header中进行传输的,看得出来,这是个隐患!一旦别人获取到你的cookie信息(截获请求,或者使用你的电脑),那么他很容易从cookie中分析出你的用户名和密码。为了解决这个隐患,所以有了session机制。
刚才提到了cookie不安全,所以有了session机制。简单来说(每个框架都不一样,这只是举一个通用的实现策略)
session的机制,很简单,就是根据用户名和密码,生成了一段随机的字符串,这段字符串是有过期时间的。
session是服务器生成的,存储在服务器的数据库或者文件中,然后把sessionID发送给用户,用户存储在本地cookie中。每次请求时,把这个session ID带给服务器,服务器根据session ID到数据库中去查询,找到是哪个用户,就可以对用户进行标记了。
与原生的cookie机制对比区别在于
原生cookie方案是记录了用户名帐号和密码的
session则只记录一个对应的id,服务端再根据id去数据库中查询用户信息。
session机制的整过过程是这样:
服务器根据用户名和密码,生成一个session ID,存储到服务器的数据库中。
用户登录访问时,服务器会将对应的session ID发送给用户(本地浏览器)。
浏览器会将这个session ID存储到cookie中,作为一个键值项。
以后,浏览器每次请求,就会将含有session ID的cookie信息,一起发送给服务器。
服务器收到请求之后,通过cookie中的session ID,到数据库中去查询,解析出对应的用户
第一步:对用户登录信息进行加密,生成一个sessionID,存储到数据库中。
第二步,当用户登录时,服务器会给本地浏览器返回一些cookie信息,包括session ID。
第三步:以后浏览器每次访问时,浏览器都会把 session ID带过来,这样服务器不需要知道你的用户名,就知道是哪个用户的访问了。
服务器是如何把sessionID转换成用户名的?
如上图所示,在Django中,需要对session进行配置。这个INSTALLED_APPS 是会对每次request和response进行拦截,拦截到浏览器发送过来的request时,找到其中的session信息,然后到数据库中进行查询,找到session_data,再做解密,就知道所有的用户信息了,取出user信息。新建完Django项目之后,这个sessions信息就配置好了。如果注释掉这一个session配置,自动登录机制就会失效,无法使用。
我们已经了解了模拟登录的实现原理,具体的实现其实有很多种方案。
列举如下:
一、模拟登录后再携带得到的cookie访问–POST 请求方法
需要在后台获取登录的 URL并填写请求体参数,然后 POST 请求登录,获取到cooike信息,放入session或者文件中,下次访问时带上cookie;
二、直接使用已知的cookie访问
先登录将获取到的 Cookies 加入 Headers 中,最后用 GET 方法请求登录,这种最为方便,但是cookie有过期的风险,需要再重新手动获取。
三、Selenium 模拟登录 (模拟登陆万能法)
代替手工操作,自动完成账号和密码的输入,简单但速度比较慢。
通过selenium进行模拟登陆,然后将Cookies传入requests,最终用requests进行网站的抓取。
首先,我们要找到 POST 请求的 URL。
在Fiddler中操作一次登录,看看提交的信息如下:
同时可以看到提交的参数有哪些。
url如下:
https://yzapi.yazio.com/v1/oauth/token
参数如下:
这些就是我们需要post时需要携带的参数,或者可以尝试一下只post关键字段,比如username和password。
参数构造非常简单,接下来只需要利用 Requests.post 方法请求登录网站,然后就可以爬取内容了。
代码如下:
上面一种方法,我们需要去后台获取 POST 请求链接和参数,比较麻烦。下面,我们可以尝试先登录,获取 Cookie,然后将该 Cookie 添加到 Headers 中去,然后用 GET 方法请求即可,过程简单很多。
从上面Fiddler捕获到的登录返回值中有需要Cookie值。
如下:
使用代码如下:
可以看到,添加了 Cookie 后就不用再 POST 请求了,直接 GET 请求目标网页即可。可以看到,也能成功获取到网页内容。
这个方法很直接,利用 Selenium 代替手动方法去自动输入账号密码然后登录就行了。
关于 Selenium 的使用,在之前的一篇文章中有详细介绍,如果你不熟悉可以回顾一下:
https://www.makcyun.top/web_scraping_withpython5.html
参考网站:
Selenium官网: https://selenium-python.readthedocs.io/
SeleniumPython文档(英文版):http://selenium-python.readthedocs.org/index.html
SeleniumPython文档(中文版):https://selenium-python-zh.readthedocs.io/en/latest/faq.html
Selenium 基本操作:https://www.yukunweb.com/2017/7/python-spider-Selenium-PhantomJS-basic/
Selenium爬取淘宝信息实战:https://cuiqingcai.com/2852.html
只需要记住重要的一点就是:Selenium能做到"可见即可爬"。也就是说网页上你能看到的东西,Selenium基本上都能爬取下来。包括上面我们提到的东方财富网的财务报表数据,它也能够做到,而且非常简单直接,不用去后台查看用了什么JavaScript技术或者Ajax参数。下面我们就实际来操练下吧。
这里,我们在网页中首先定位了账号节点位置:’//[@id=“create_account_email”]’,然后用 input.send_keys 方法输入账号,同理,定位了密码框位置并输入了密码。接着定位 登录 按钮的位置://[@id=“login_btn”],然后用 submit.click() 方法实现点击登录按钮操作,从而完成登录。可以看到,也能成功获取到网页内容。
注意Selenium只支持网页版的,如果是要抓取app,需要使用Appium。
Appium官方文档
https://github.com/DoctorQ/appium/blob/master/docs/en/about-appium/intro.md
https://zhuanlan.zhihu.com/p/41311503
解决方法:
单机采集:
1.在启动采集后自己手动输入验证码;
2.在本地的浏览器先登录好账号在进行采集;
云采集:在登录账号后,记录登录账号cookie,再启动云采集。
登录流程
第一步获取验证码和相应cookies,
验证码的地址通过观察fiddler获得,这里不再赘述。
此处cookies已经保存在valcode.cookies中,接下来我们需要将valcode保存为图片。
保存图片之后,鉴于技术问题暂时还没有做机器识别,只能用人眼识别了。得知验证码结果后我们Input进来。
第二步发送登录请求
通过观察network我们发现登录请求所带的参数格式如下:
这里用户名和密码已经通过base64加密,python中有现成的base64解码编码模块,直接import进来用就可以,笔者不再赘述。
完整代码参考:
参考连接:
自动验证码识别参考
因为很多公司一直也在做防抓取的优化,代码早晚会失效,解析思路才是永恒。所以最好多看看别人分析的例子。增加经验。