基于 requests 的全能扫描王爬虫实践


投稿人:Alan

摘要:全能扫描王是文件扫描留档的重要工具,本文利用requests爬虫将手机客户端的扫描文件,同步至电脑端。

一、背景

在审计工作中,需要大批量扫描文件留档,全能扫描王成为主流的手机端扫描工具。由于网页端的全能扫描王无法大批量下载的扫描文件,在一定的限制环境下,使用将手机端同步至电脑成为一大需求,本文利用requests爬虫下载扫描文件,tkinter设计交互界面,pyinstaller打包封装exe实现脱机使用。

二、Fiddler抓包

Fiddler安装及证书信任设置不在此赘述。

(1)user/login请求

利用火狐浏览器访问扫描王网页(https://www.camscanner.com/user/login),并输入用户名和密码点击登录。在Fiddler中观察执行情况,发现第247号POST请求user/login下webform包含上传参数,返回结果json为URL,且在Fiddler抓取的URL中已经访问(第249号),故应为跳转的网址(如图2-1)。

图2-1

(2)files/holder请求

该请求为登录后的界面,在Fiddler中仅执行请求返回为0,未获取登录后网页,故可能需要模拟请求头部和Cookie。在执行带header和cookie的请求后成功获取登录界面,其中cookie包含_csc、 _csl、Hm_lvt_8f0191b1f1b207d4f6e0d42e771d6fde、_oa、_cdn、 JSESSID、Hm_lpvt_8f0191b1f1b207d4f6e0d42e771d6fde、_ct、 _isus、 _cslt、 S2、 _cssu、_csste等参数。登录后的界面包含了用户已经同步至云端的所有文件列表,但请求结果中未发现文件清单的信息,故猜测可能存在别的网址。在Fiddler抓取结果中,发现图2-1中第270号请求中返回文件列表(如图2-2)。

图2-2

(3)doc/list请求

该请求获取文件列表,在Fiddler中执行带header和cookie的doc/list请求,结果与抓取一致。

(4)doc/downloadpdf请求

在火狐浏览器中下载某一个文件,Fiddler中发现请求doc/downloadpdfstat/download与下载相关,其中doc/downloadpdf根据提供的文件的唯一标识doc_id返回对应的下载地址(如图2-3),而stat/download是确认文件下载状态,故主要关注doc/downloadpdf请求。

图2-3

三、requests爬虫实现

在第二节的抓包后,着手进行代码实现,考虑到程序为工作的需求,故脱离python环境也能执行,故暂不考虑selenium,而采用requests。程序主要分为登录(login_page)、获取文件列表(get_file_list)、下载文件(get_downloadfile_url_one)三块。

(1)程序初始化

设置requests.session、headers等变量,并应用logger包将在程序中打log,代码如下:

class downloadscan(object):
    def __init__(self,username,password):
        self.session = requests.session()
        self.url = "https://www.camscanner.com/user/login"
        self.username,self.password =username,password
        self.respage = None
        self.headers = {"Host": "www.camscanner.com",\
                        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:58.0) Gecko/20100101 Firefox/58.0",\
                        "Accept":"text/plain, */*; q=0.01",\
                        "Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",\
                        "Accept-Encoding":"gzip, deflate, br",\
                        "Content-Type": "application/x-www-form-urlencoded",\
                        "X-Requested-With":"XMLHttpRequest",\
                        "Connection":"keep-alive",\
                        "Cache-Contro":"max-age=0, no-cache",\
                        "Pragma":"no-cache"}
        self.file_list = None
        self.tmp_cookie = None
        self.download_res = {"filename":None,"address":None}
        self.status = True
        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(level = logging.INFO)
        handler = logging.FileHandler("./camscanner/log/log")
        handler.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

(2)登录login_page

利用requests.session.post实现user/login请求,并设置status获取登录状态(成功or失败),详见login_page函数, 在登录后实现files/holder请求跳转,详见redirect_to_holder函数,并更新登录状态,代码如下:

    def login_page(self):
        #登录
        params = {"act":"submit","redirect_uri":"","area_code":"86","username":self.username,"password":self.password,"rememberme":False}
        self.respage = self.session.post(self.url,data=params,headers = self.headers)
        self.logger.info(self.url+" status_code: "+ str(self.respage.status_code))
        self.generate_header("login")
        if str(self.respage.status_code) =="200" and self.status ==True:
            self.logger.info("++++++++oh my god, login succeed!++++++")
        else:
            self.status = False
    def redirect_to_holder(self):
        #跳转
        if "data" in json.loads(self.respage.text):
            self.url = json.loads(self.respage.text)["data"]
            self.respage = self.session.get(self.url,headers = self.headers)
            self.logger.info(self.url+" status_code: "+ str(self.respage.status_code))
            self.status = True
        else:
            self.status = False
        if str(self.respage.status_code) =="200" and self.status == True:
            self.logger.info("++++++++oh my god, redirect login succeed!++++++") 
        else:
            self.status = False

(3)请求headers设置

    def generate_header(self,action):
        if action=="login":        
            self.headers["Referer"] = self.url
            self.headers["Accept"]  ="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
            self.headers["Upgrade-Insecure-Requests"] = "1"
            self.tmp_cookie = requests.utils.dict_from_cookiejar(self.respage.cookies)
            if "_csc" in self.tmp_cookie and "JSESSID" in self.tmp_cookie and "S2" in self.tmp_cookie and "_cssu" in self.tmp_cookie:
                self.headers["Cookie"]  ="_csc=%s;_csl=zh-cn;_oa=86,%s;JSESSID=%s;Hm_lvt_8f0191b1f1b207d4f6e0d42e771d6fde=%s;_ct=1;_isus=1;_cslt=1;S2=%s;_cssu=%s;_csste=%s;Hm_lvt_8f0191b1f1b207d4f6e0d42e771d6fde=%s;_cssrd=1"%(self.tmp_cookie["_csc"],self.username,self.tmp_cookie["JSESSID"],int(time.time()),self.tmp_cookie["S2"],self.tmp_cookie["_cssu"],int(time.time()),int(time.time()))
            else:
                self.status = False
                self.logger.warning(self.url+"登陆失败,headers设置失败")
        else:
            self.headers["Cache-Contro"] ='no-cache'
            self.headers["Content-Type"] = "application/x-www-form-urlencoded;charset=utf-8"
            self.headers["Accept"]  = "text/plain, */*; q=0.01"
            self.headers["Referer"] = 'https://www.camscanner.com/files/holder'
            if "Upgrade-Insecure-Requests" in self.headers:
                self.headers.pop("Upgrade-Insecure-Requests")
            if "_csc" in self.tmp_cookie and "JSESSID" in self.tmp_cookie and "S2" in self.tmp_cookie and "_cssu" in self.tmp_cookie:
                self.headers["Cookie"]  ="_csc=%s;_csl=zh-cn;_oa=86,%s;JSESSID=%s;Hm_lvt_8f0191b1f1b207d4f6e0d42e771d6fde=%s;_ct=1;_isus=1;_cslt=1;S2=%s;_cssu=%s;_csste=%s;Hm_lvt_8f0191b1f1b207d4f6e0d42e771d6fde=%s;_cssrd=1"%(self.tmp_cookie["_csc"],self.username,self.tmp_cookie["JSESSID"],int(time.time()),self.tmp_cookie["S2"],self.tmp_cookie["_cssu"],int(time.time()),int(time.time()))
            else:
                self.status = False
                self.logger.warning(self.url+"登陆失败,headers设置失败")

(4)获取文件列表

    def get_file_list(self):
        self.url = "https://www.camscanner.com/doc/list"
        self.respage = self.session.post(self.url,headers = self.headers)
        #self.save_all()
        self.logger.info(self.url+" status_code: "+ str(self.respage.status_code))
        self.file_list = eval(self.respage.text)["data"]["list"]
        self.logger.info("++++++++enheng, get file list succeed+++++++++++")
           (5)下载文件
    def get_downloadfile_url_one(self,doc_id,title):
        self.generate_header("get download url")
        self.url = "https://www.camscanner.com/doc/downloadpdf"
        self.params = {"json_download":json.dumps({"docs":["%s.jdoc"%doc_id]})}
        times = 0
        tmp = None
        while (times<5 and tmp == None):
            self.respage = self.session.post(self.url,data = self.params,headers = self.headers)
            times +=1
            if "data" in self.respage.json():
                tmp = self.respage.json()["data"]
        self.download_res["address"] = tmp
        self.download_res["filename"] = title + tmp[tmp.rfind("."):]
        self.logger.info("get %s address is : %s"%(title,tmp))
        urllib.request.urlretrieve(self.download_res["address"],self.download_res["filename"])
        self.logger.info("%s,下载完成"%self.download_res["filename"])

四、tkniter交互、pyinstaller封装exe

为方便程序可以脱离python环境使用,利用tkniter包设计简单的交互界面,主要设置登录界面(图4-1),若登录网页版全能版扫描王后成功后,跳转同步界面(图4-2),并在本地生成user_info文件,下次直接自动登录,跳过登录界面,点击开始同步(图4-3),将本地已经下载的文件跳过,直接下载未下载的文件,并更新本地文件列表,针对同步失败、切换账号、密码更改的需求,设置重新登录功能,将删除原有user_info文件,重新生成(图4-4),运行结果如图4-1至4-4:

4-1登录界面

4-2登录成功

4-3开始同步

4-4重新登录

将测过的代码进行封装,本文运行较为简单的命令:

pyinstaller -F -w downloadscan.py -i camscanner.ico

五、结语

下载本文全部代码(downloadscan.py)实现及封装程序(downloadscan.exe),请扫描下方二维码关注公众号后回复“抓包”获取

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【OpenCV】基于Qt的“破产版”全能扫描王

功能介绍 图片打开和保存图片矫正&#xff08;证件扫描、文字纠正…&#xff09;图片锐化增强图片清空阈值设置 项目实现 基本思路&#xff08;证件扫描&#xff09; 抠图&#xff1a;提取轮廓矫正&#xff1a;透视变换锐化增强&#xff1a;二值化 算法设计&#xff08;证…

eNSP:ibgp的破水平切割练习

实验要求&#xff1a; 拓扑展示&#xff1a; 命令操作&#xff1a; R1&#xff1a; <Huawei>sys [Huawei]sys r1 [r1]int g 0/0/1 [r1-GigabitEthernet0/0/1]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/1]int lo0 [r1-LoopBack0]ip add 1.1.1.1 24 [r1-LoopBack0]osp…

css clip-path 画五角星

简介 使用裁剪方式创建元素的可显示区域&#xff0c;实现区域裁剪。 浏览器兼容性 常用的三种方法 1. clip-path: circle(50px at 50px 50px) 以 50px 50px 的坐标为圆心裁剪一个半径 50px 的圆&#xff1b; 2. clip-path: ellipse(30px 40px at 50px 50px) 以 50px 50px…

canvas绘图详解-06-绘制一个五角星-常用绘图原理

先讲如何画一个正规的五角星 在五角星的内外画两个圆&#xff0c;五角星有五个角&#xff0c;360/572度 所以得出这两个角的度数 然后算出这两个点坐标 角度转弧度 角度/180*Math.PI 所以外顶点坐标 x: Math.cos( (1872*i)/180*Math.PI) * R y: Math.sin((1872*i)/180*Math.P…

钓鱼网站原理实验

实验环境:一台Windows 2003&#xff08;WA&#xff09;作为客户机&#xff0c;一台Linux&#xff08;LB&#xff09;作为服务器。 1.LB设置网络适配器 2.LB配置eth0&#xff08;vim /etc/sysconfig/network-scripts/ifcfg-eth0&#xff09; 3.LB将eth0的内容copy一份到eth1 …

电子邮件如何追溯到他们的源IP地址

你要做的第一件事当你听到检查发送电子邮件通知,对吧? 这是最快的方法找出谁是电子邮件,以及可能的内容。 但是你知道每个邮件都有更多的信息比出现在大多数的电子邮件客户端? 有主机的信息发送者包括在邮件头信息可以用来跟踪电子邮件回源。 下面是如何跟踪,邮件回它是从哪…

红队攻击:轻松玩转邮件钓鱼

首发在合天智汇&#xff1a;https://mp.weixin.qq.com/s/sRXnwIdy3eQ0CJS58_bI1g 邮件钓鱼老生常谈&#xff0c;攻击手段也并不新颖。但对于网络安全工程师来说,“懂原理”和“会使用”是两个概念。会用&#xff0c;用得好不好&#xff0c;又是不同的层次。红队攻击要讲究攻击的…

seeker+ngrok 钓鱼获取目标位置

免责说明&#xff1a;本文章只是参考&#xff0c;一切后果自行承担&#xff0c;网络环境靠大家&#xff0c;遵守网络安全法是每个人应尽的责任 文章目录 一、工具介绍seekerngrok 二、使用步骤 一、工具介绍 seeker seeker工具是一款社会工程学的利器&#xff0c;可以通过see…

使用十六进制IP地址的网络钓鱼攻击活动

网络犯罪分子正在不断优化其网络攻击工具&#xff0c;策略和技术&#xff0c;以逃避垃圾邮件检测系统。 由于一些系统会直接提取邮件中内嵌的链接进行检测&#xff0c;而一种此类URL混淆技术采用了URL主机名部分中使用的编码十六进制IP地址格式来逃避检测。 由于IP地址可以用多…

1. 如何爬取自己的CSDN博客文章列表(获取列表)(博客列表)(手动+python代码方式)

文章目录 写在最前步骤打开chrome浏览器&#xff0c;登录网页按pagedown一直往下刷呀刷呀刷&#xff0c;直到把自己所有的博文刷出来然后我们按F12&#xff0c;点击选取元素按钮然后随便点一篇博文&#xff0c;产生如下所示代码然后往上翻&#xff0c;找到头&#xff0c;复制然…

Maven在IDEA2021版本中全局配置(一次配置处处生效)

前言 我们在开发中&#xff0c;Maven是必不可少的&#xff0c;但是每次都需要设置一遍Maven的仓库和settings.xml。真的是心累&#xff0c;今天教大家全局配置一下。再也不要每次项目都配了&#xff0c;Maven还经常出问题。 解决方案 友情提示&#xff1a;小编的IDEA版本为2…

Tableau 聚合计算 - 分组求和(sum、fixed、include的使用)

一、聚合计算例子 有以下数据&#xff1a; // 计算1 SUM(IF [shuxue]123 then [yingyu] END)// 计算2 IF [shuxue]123 then {fixed [shuxue]: SUM([yingyu])} END// 计算3 IF [shuxue]123 then {SUM([yingyu])} END// 计算4 {fixed [shuxue]: SUM([yingyu])}// 计算5 {include…

Kubernetes pod调度约束[亲和性 污点] 生命阶段 排障手段

调度约束 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面建立 Pod 和 Container。 APIServer…

[Pandas] 分组比例计算求和

美图欣赏2022/08/01 问题:在最近的需求开发中&#xff0c;有这么个分组比例计算求和问题&#xff0c;根据字段CPN进行分组&#xff0c;计算每一笔PO Line Actual CT2R * line 数量比重&#xff0c;取名为Weighted(QTY)CT2R&#xff0c;再根据相同的CPN对每行Weighted(QTY)CT2R值…

SQL多字段分组求和

表结构 表数据 按店铺名称、发货时间分组并求总数 select g.[店铺名称],g.[发货时间],sum(g.[数量]) as 总数 from t_goods g GROUP BY g.[店铺名称],g.[发货时间] ORDER BY g.[店铺名称]

oracle 分析函数之分组求和、连续求和

最近在《sql cookbook》书上发现了名叫 分析函数 的东西&#xff0c;之前学 oracle 时没有印象&#xff0c;现在感觉其分析函数的功能相当强大、神奇&#xff0c; 就特意去找到了 chm 文档研究了一下&#xff0c;想要的朋友在本文末尾自行下载。 本文的例子都来源于 chm 文档…

简单卡通水

参考 https://roystan.net/articles/toon-water/ 源码 https://github.com/IronWarrior/ToonWaterShader 两张噪声图 挂在相机上&#xff0c;开启深度模式&#xff0c;使shader可以拿到深度图 using UnityEngine;public class ChangeCameraDepth : MonoBehaviour {public De…

编写简单的动画

UIImageView提供了实现简单动画的功能。比如要实现这样的功能&#xff1a; 跳动的火焰。基本思路是&#xff0c;一个数组&#xff0c;包含火焰跳动所需的多张图片。然后设置播放时间&#xff0c;并开始播放。UIImageView已经提供了这方面的机制&#xff0c;只需把图片数组交给它…

干货!学会这些动漫绘画素材,小白也能学会动漫绘画,简单易学!

百头练习 只画头部,着重对五官进行精细刻画。 女性头像练习 1 定出五官的位置,注意不管是什么角度,人的眼睛、嘴巴都是和鼻子呈90的。 2 画出五官。 3 注意线条有实有虚,把五官细化描绘出来,然后大致勾勒出头发的走向。 4 画出头发丝和眼珠。 5 添加光影,使头发…