最近在準(zhǔn)備上岸构蹬,備考中需做大量的練習(xí),手機(jī)APP讓練習(xí)無處不在悔据,但有個(gè)缺點(diǎn)庄敛,就是每次只能練習(xí)10-20道題目,不能用題海戰(zhàn)術(shù)科汗,海量做題藻烤。能不能把網(wǎng)站的題庫及解析都弄下來,在本地電腦上做題呢头滔?以下是我的思路:1怖亭、分析并爬取手機(jī)軟件的題庫;2坤检、調(diào)整好格式存入xls文件中兴猩;3、導(dǎo)入PC版可自定義的考試系統(tǒng)中(如教之初考試系統(tǒng)免費(fèi)版)早歇;4倾芝、自行設(shè)置考試模式,海量練習(xí)箭跳。
倒騰了兩天晨另,終于搞定,后續(xù)再更新文章衅码!
涉及到幾個(gè)點(diǎn)拯刁。
1、關(guān)于手機(jī)網(wǎng)絡(luò)數(shù)據(jù)的分析逝段,我是用Fiddler 4進(jìn)行的分析。
2割捅、關(guān)于excel表格的操控奶躯,之前想用xlwings模塊,后因沒裝office亿驾,用的是wps2013政府閹割版嘹黔,無法操控,后改用xlrd、xlutils儡蔓、shutil郭蕉、os模塊自己寫了一個(gè)excel操縱類對象,發(fā)現(xiàn)還是有問題喂江,后卸載閹割版wps召锈,換了wps2016最新版的,就可以操作了获询。后來也沒換xlwings模塊了涨岁,將就著用自寫模塊。大坑:xlrd模塊好像不支持中文路徑吉嚣,全部換用英文路徑梢薪,最后再把文件夾及文件名轉(zhuǎn)換成中文的。
3尝哆、題庫分類保存秉撇。
1、Fiddler抓包手機(jī)APP的網(wǎng)絡(luò)請求
Fiddler是一款非常流行并且實(shí)用的http抓包工具秋泄,它的原理是在本機(jī)開啟了一個(gè)http的代理服務(wù)器畜疾,然后它會轉(zhuǎn)發(fā)所有的http請求和響應(yīng),不僅如此印衔,它還可以支持請求重放等一些高級功能啡捶。顯然它是可以支持對手機(jī)應(yīng)用進(jìn)行http抓包的。
設(shè)置在同一局域網(wǎng)下電腦端Fiddler抓取手機(jī)APP應(yīng)用網(wǎng)絡(luò)請求的步驟:
(1)電腦端啟動Fiddler奸焙,打開菜單欄中的 Tools > Fiddler Options瞎暑,打開“Fiddler Options”對話框。
(2)在Fiddler Options”對話框切換到“Connections”選項(xiàng)卡与帆,然后勾選“Allow romote computers to connect”后面的復(fù)選框了赌,然后點(diǎn)擊“OK”按鈕。(小技巧:HTTPS標(biāo)簽中可以設(shè)置下拉框?yàn)?..from remote clients only 玄糟,貌似就只接收遠(yuǎn)程端的網(wǎng)絡(luò)請求了)
(3)在電腦端的命令行輸入:ipconfig勿她,找到本機(jī)的ip地址。(我的局域網(wǎng)IP:192.168.1.10)
(4)在手機(jī)端阵翎,打開android設(shè)備的“設(shè)置”->“WLAN”逢并,找到你要連接的網(wǎng)絡(luò),在上面長按郭卫,然后選擇“修改網(wǎng)絡(luò)”砍聊,彈出網(wǎng)絡(luò)設(shè)置對話框,然后勾選“顯示高級選項(xiàng)”贰军。(其實(shí)蘋果手機(jī)也是類似的)
(5)在“代理”后面的輸入框選擇“手動”玻蝌,在“代理服務(wù)器主機(jī)名”后面的輸入框輸入電腦的ip地址,在“代理服務(wù)器端口”后面的輸入框輸入8888,然后點(diǎn)擊“保存”按鈕俯树。
(6)然后啟動android設(shè)備中的APP應(yīng)用帘腹,在fiddler中可以看到完整的請求和響應(yīng)數(shù)據(jù)。
2许饿、手機(jī)APP應(yīng)用網(wǎng)絡(luò)數(shù)據(jù)分析
手機(jī)上打開某筆公考軟件后阳欲,從Fiddler左側(cè)欄中可以看到大量的請求鏈接。
點(diǎn)擊上圖黃色區(qū)域的鏈接米辐,可看到右側(cè)詳細(xì)數(shù)據(jù)胸完。
在右側(cè)詳細(xì)數(shù)據(jù)欄中,我們可以看到請求的網(wǎng)址:POST http://xxxbi.com/android/sydw/exercises?&platform=android22&version=6.4.3&vendor=Tencent&app=gwy&deviceId=F4J/K8kXx6+C24yqFuzAiA==&av=8&kav=3 HTTP/1.1
post請求的參數(shù)keypointId=621638&type=3&limit=15
返回的數(shù)據(jù)為Json格式翘贮,從返回?cái)?shù)據(jù)中我們可以找出questionIds對應(yīng)的題目編號赊窥,如2084744.
點(diǎn)擊上上圖(Fiddler網(wǎng)絡(luò)請求響應(yīng))中紅色框中的鏈接地址,我們可以看到右側(cè)詳細(xì)數(shù)據(jù)欄的數(shù)據(jù)
從上圖中我們可以找到ID號為2084744的題目的題干狸页、選項(xiàng)锨能、題型、答案等有用數(shù)據(jù)芍耘。
分析基本結(jié)束址遇,此手機(jī)APP應(yīng)用的模式基本是這樣:先請求15道題的ids,再根據(jù)meidaoti的ids請求題目相關(guān)數(shù)據(jù)斋竞。后續(xù)測試發(fā)現(xiàn)倔约,請求questionIds時(shí),修改參數(shù)坝初,能一次最多請求到100道題的ids浸剩。
另外,從左側(cè)請求鏈接可以找出對應(yīng)的章節(jié)分類請求鏈接鳄袍,根據(jù)分類號再查找對應(yīng)的questionids绢要,就可以把所有的題目抓取出來。
3拗小、xls操控
將就著基于xlrd模塊編寫自用模塊重罪。
源碼在此,可能格式錯(cuò)亂哀九。需整理剿配。
'''python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on 2018-05-14 12:48:07
@author: wangzheng
Sys_Env : Windows_AMD64 Python3.5.2
Wechat : hrcl2015(微信)
Filename: MyXls.py
Description : xls文件操控類MyXls
? ? ? ? ? ? 注意:文件路徑中不能有中文名勾栗,否則出錯(cuò)
"""
from xlrd import open_workbook
from xlutils.copy import copy
import os,shutil
class MyXls:
? ? def __init__(self,fpath=None,modelfpath=None,sheetindex=None,protectrow=None,overwrite=True):
? ? ? ? self.openxlspath=''#已打開的xls文件
? ? ? ? self.sheetindex=None#當(dāng)前工作表索引
? ? ? ? self.rb=None#
? ? ? ? self.wb=None#workbook工作簿
? ? ? ? self.ws=None#worksheet工作表
? ? ? ? self.headrow=0#開頭保護(hù)行數(shù)
? ? ? ? if (fpath is not None) and (modelfpath is not None):
? ? ? ? ? ? self.open_copy_xls(modelfpath,fpath,True)
? ? ? ? elif fpath is not None:self.open_xls(fpath)
? ? ? ? if sheetindex is not None:self.get_sheet(sheetindex)
? ? ? ? if protectrow is not None:self.headrow=protectrow
? ? def open_xls(self,fpath):
? ? ? ? path=fpath
? ? ? ? try:
? ? ? ? ? ? rb = open_workbook(path)
? ? ? ? except Exception as err:
? ? ? ? ? ? print("File not exists: "+str(err))
? ? ? ? ? ? return False
? ? ? ? #通過sheet_by_index()獲取的sheet沒有write()方法
? ? ? ? #rs = rb.sheet_by_index(0)
? ? ? ? #rs = rb.sheet_by_name('sheet 1')
? ? ? ? self.wb = copy(rb)
? ? ? ? self.openxlspath=path
? ? ? ? return True
? ? def get_sheet(self,sheet_index=0):
? ? ? ? sheets=sheet_index
? ? ? ? #通過get_sheet()獲取的sheet有write()方法
? ? ? ? self.ws = self.wb.get_sheet(sheets)
? ? ? ? self.sheetindex=sheets
? ? def write_xls(self,row_index,column_index,data_str):
? ? ? ? row,col,data=row_index,column_index,data_str
? ? ? ? #寫入數(shù)據(jù)
? ? ? ? self.ws.write(row,col,data)#write(行,列,內(nèi)容)惨篱,索引從0開始
? ? def write_xls_bycolname(self,row_index,column_name,data_str):
? ? ? ? column_index=self.colname_to_num(column_name)
? ? ? ? row,col,data=row_index,column_index,data_str
? ? ? ? #寫入數(shù)據(jù)
? ? ? ? self.ws.write(row,col,data)#write(行,列,內(nèi)容),索引從0開始
? ? def write_xls_bysheet(self,sheetindex,row_index,column_name,data_str):
? ? ? ? '''在指定工作表的指定行列表格中寫入數(shù)據(jù)'''
? ? ? ? if self.sheetindex != sheetindex :self.get_sheet(sheetindex)
? ? ? ? self.write_xls_bycolname(row_index,column_name,data_str)
? ? def save_xls(self,fpath=None):
? ? ? ? if fpath is None:
? ? ? ? ? ? self.wb.save(self.openxlspath)
? ? ? ? ? ? print('saved '+self.openxlspath)
? ? ? ? else:
? ? ? ? ? ? path=fpath
? ? ? ? ? ? self.wb.save(path)#保存xls文件
? ? ? ? ? ? print('saved '+path)
? ? ? ? return True
? ? def open_copy_xls(self,model_fpath,new_fpath,overwrite=True):
? ? ? ? '''參照模板文件围俘,復(fù)制并打開xls文件'''
? ? ? ? if os.path.exists(model_fpath) :
? ? ? ? ? ? new_fpath=self.auto_mkdir(new_fpath)
? ? ? ? ? ? if (not os.path.exists(new_fpath)) or overwrite:
? ? ? ? ? ? ? ? #print('copy[%s]to[%s]'%(model_fpath,new_fpath))
? ? ? ? ? ? ? ? shutil.copy(model_fpath,new_fpath)
? ? ? ? ? ? ? ? return self.open_xls(new_fpath)
? ? ? ? ? ? else:print('新文件已存在,請修改新文件名!');return False
? ? ? ? else:print('模板文件不存在界牡,不能復(fù)制到新文件簿寂!');return False
? ? def num_to_colname(self,col_index,start=0):
? ? ? ? #列索引轉(zhuǎn)列名,基數(shù)start從0開始,0-->A
? ? ? ? if type(col_index) != int:
? ? ? ? ? ? return col_index
? ? ? ? if start==0:
? ? ? ? ? ? x=col_index+1
? ? ? ? elif start==1:
? ? ? ? ? ? x=col_index
? ? ? ? s=''
? ? ? ? flag=False#借位標(biāo)志
? ? ? ? while x>26:
? ? ? ? ? ? y=x%26#取余0-25
? ? ? ? ? ? if y==0:y=26;flag=True;
? ? ? ? ? ? d=chr(y+64)#低位
? ? ? ? ? ? s=d+s
? ? ? ? ? ? x=x//26#整除取商
? ? ? ? ? ? if flag:x=x-1;flag=False;#如果借位宿亡,商要先-1
? ? ? ? g=chr(x+64)#高位
? ? ? ? s=g+s
? ? ? ? return s
? ? def colname_to_num(self,colname,start=0):
? ? ? ? #列名轉(zhuǎn)列索引 A-->0常遂,B->1,開始基數(shù)start為0
? ? ? ? if type(colname) is not str:
? ? ? ? ? ? return colname
? ? ? ? colname=colname.upper()#轉(zhuǎn)成大寫
? ? ? ? col = 0
? ? ? ? power? = 1
? ? ? ? #print(len(colname))#位數(shù)
? ? ? ? for i in range(len(colname) - 1, -1, -1):#range(start=0,stop,step=1)
? ? ? ? ? ? ch = colname[i] #倒序取字母
? ? ? ? ? ? #print(ch)#所在位上的字母
? ? ? ? ? ? col += (ord(ch) - ord('A') +? 1 ) * power
? ? ? ? ? ? power *= 26
? ? ? ? #print(col-1)
? ? ? ? if start==0:return col-1
? ? ? ? if start==1:return col
? ? def auto_mkdir(self,fpath):
? ? ? ? '''自動補(bǔ)全目錄,目錄不存在就創(chuàng)建目錄'''
? ? ? ? #fpath='D:\\MyPython\\粉筆公考題庫提取\\678\\980\\test.txt'
? ? ? ? fpath=fpath.replace('\\','/')
? ? ? ? if not os.path.exists(fpath):
? ? ? ? ? ? plst=fpath.split(sep='/')
? ? ? ? ? ? path=''
? ? ? ? ? ? if not fpath.endswith('/'):plst=plst[:-1]
? ? ? ? ? ? for p in plst:
? ? ? ? ? ? ? ? path=path+p+'/'
? ? ? ? ? ? ? ? if not os.path.exists(path):os.mkdir(path)
? ? ? ? ? ? return fpath
if __name__=='__main__':
? ? model_fpath='D:\\MyPython\\model.xls'
? ? new_fpath='D:\\MyPython\\abc\\123/out.xls'
#? ? myxls=MyXls()
#? ? myxls.open_copy_xls(model_fpath,new_fpath)
#? ? myxls.get_sheet(0)
? ? myxls=MyXls(new_fpath,model_fpath,2,3)
? ? myxls.write_xls_bycolname(6,'K','K列6行數(shù)據(jù)')
#? ? myxls.save_xls(new_fpath)
? ? myxls.save_xls()
? ? pass
'''
4挽荠、文件保存
保存成xls文件克胳,和txt文件,txt文件再轉(zhuǎn)換成word文檔打印出來圈匆。大概有7個(gè)大類漠另,100+小類,12737道題跃赚。
5笆搓、導(dǎo)入題庫軟件
xls文件導(dǎo)入題庫中。
6纬傲、大功告成
PS:某筆app更新后满败,加入了防抓取數(shù)據(jù)的功能,貌似安卓5.1以上的都不能抓取了叹括。
據(jù)資深網(wǎng)友反應(yīng)算墨,使用安卓模擬器(系統(tǒng)版本4.4)還可以抓到明文數(shù)據(jù),有興趣的讀者可以試一試汁雷。