說明:個人練手python用。
操作系統(tǒng):window10 x64
IDE:Pycharm 2017.2.2
Python版本:3.6.2
目標(biāo)
- 爬淙埽客網(wǎng)是一個IT筆試面試的平臺,提供了很多題庫尊浓,今天我們使用python爬取其中的Java專項練習(xí)庫逞频。
步驟
1、接口抓榷俺荨:如果是爬取網(wǎng)頁苗胀,前后端分離的項目可以抓取接口,如果沒有分離瓦堵,則需要爬取整個網(wǎng)頁然后使用正則篩選基协。這里我們直接抓取客戶端接口即可。
2菇用、模擬網(wǎng)絡(luò)請求澜驮,獲取數(shù)據(jù)(這里是json)
3、json解析惋鸥,題目格式調(diào)整杂穷、寫入文件
一、接口抓取
- 工具:Fiddler
- 對象:咆孕澹客網(wǎng)Android客戶端2.21.3.3091
通過Fiddler發(fā)現(xiàn)耐量,獲取專項練習(xí)題的接口為:
http://m.nowcoder.com/test/get-all-question?t=02436CC60E649584D5C4BBF57709E5CA&fm=android_app_2.21.3.3091&tid=10716711
這里:
- t=02436CC60E649584D5C4BBF57709E5CA應(yīng)該是用戶身份標(biāo)識
- tid=10716711是本次練習(xí)的編號(Java專項練習(xí)一共900多道題,每次練習(xí)會隨機組卷滤港,組卷數(shù)量為5廊蜒、10、20蜗搔、30這些劲藐,這個tid就是組出的試卷的編號)
使用Fiddler模擬請求八堡,就可以得到該tid對應(yīng)10道題目的json數(shù)據(jù)了樟凄,由于每次請求tid沒有變化,可以發(fā)現(xiàn)多次請求的json結(jié)果是一樣的,類似下面的:
{
"data": {
"paper": {
......省略部分
"diffcult": 3,
"questionCount": 10,
......省略部分
},
"allQuestion": [
{
"score": 10,
"shielded": false,
"question": {
"content": "<p>\n <span>下列關(guān)于構(gòu)造方法不正確的是:( </span>\n <span> )</span>\n</p>\n<p style=\"text-indent: 15.8pt;\">\n <br>\n</p>\n<p>\n <br>\n</p>",
"id": 69561,
"title": "下列關(guān)于構(gòu)造方法不正確的是:( )",
"answer": [
{
"content": "類的構(gòu)造方法和類同名",
"id": 111297,
"type": 0
},
......省略部分
],
......省略部分
},
"pos": 1
},
......省略部分
],
"userAnswers": [
{
"userAnswer": "111300",
"pos": 1
},
......省略部分
]
},
"code": 0,
"msg": "OK"
}
要想不一樣兄渺,就需要不一樣的tid了缝龄,發(fā)現(xiàn)生成tid的接口如下:
POST http://m.nowcoder.com/itest/request-make-paper HTTP/1.1
......省略部分request header
questionCount=10&tagIds=570&t=02436CC60E649584D5C4BBF57709E5CA&fm=android_app_2.21.3.3091&source=1
這是一個post請求,可以發(fā)現(xiàn)在request body中的questionCount就是表明了本次組卷試題的數(shù)量。
這樣思路就清晰了:
- 根據(jù)request-make-paper接口獲取tid叔壤,這里我們指定questionCount為30
- 根據(jù)get-all-question接口瞎饲,傳入tid參數(shù),獲取此次組卷的30道題的json數(shù)據(jù)
- 然后分析這30道題中的字段的含義炼绘,解析嗅战、存儲即可
二、網(wǎng)絡(luò)請求
定義一個方法俺亮,用于post請求驮捍,返回響應(yīng)的內(nèi)容,如下:
def post_json_data(url, request_body):
req = request.Request(url)
# 根據(jù)需要設(shè)置請求頭脚曾,比如模擬瀏覽器請求設(shè)置UA东且、一些身份權(quán)限認(rèn)證字段等都會放到header里
req.add_header('OS', 'Android')
req.add_header('VERSION', '82')
req.add_header('CHANNEL', '360')
req.add_header('User-Agent', 'nowcoder android 2.21.3.3091')
# post請求,添加request body即可
with request.urlopen(req, data=request_body.encode('utf-8')) as f:
if f.status == 200:
result_json = json.loads(f.read())
return result_json
再定義一個方法本讥,用于get請求珊泳,返回響應(yīng)的內(nèi)容,如下:
def get_json_data(url):
req = request.Request(url)
req.add_header('OS', 'Android')
req.add_header('VERSION', '82')
req.add_header('CHANNEL', '360')
req.add_header('User-Agent', 'nowcoder android 2.21.3.3091')
with request.urlopen(req) as f:
if f.status == 200:
result_json = json.loads(f.read())
return result_json
如果把添加到request header中的這些key-value的參數(shù)組成dict拷沸,就可以通過外部傳入色查,方法中遍歷添加,上面兩個方法就可以作為工具方法了撞芍。
接下來組裝post請求的request body中的參數(shù)
data_make_paper = parse.urlencode([
('questionCount', '30'),
('tagIds', '570'),
('t', '02436CC60E649584D5C4BBF57709E5CA'),
('fm', 'android_app_2.21.3.3091'),
('source', '1')
])
就可以發(fā)送網(wǎng)絡(luò)請求综慎,獲取響應(yīng)數(shù)據(jù)了:
result = post_json_data('http://m.nowcoder.com/itest/request-make-paper', data_make_paper)
url_get_questions = "http://m.nowcoder.com/test/get-all-question" + \
"?t=02436CC60E649584D5C4BBF57709E5CA&fm=android_app_2.21.3.3091&tid=" + \
str(result['data'])
需要注意的是,需要引入request和parse模塊:
from urllib import request,parse
三勤庐、json解析示惊,題目格式調(diào)整、寫入文件
先定義一個寫入文件的方法愉镰,這里我們需要追加的形式寫米罚,也即后面寫入的內(nèi)容不能覆蓋前面已經(jīng)寫入的:
def write_text(path, text, mode='a'):
with open(path, mode=mode, encoding="utf-8") as f:
f.write(text)
f.write("<br>")
mode = 'a'就表示追加的形式寫入。
接下來就是json解析丈探、添加一下格式(如題目編號录择,選項編號,題與題之間的空行等)碗降、然后寫入文件了隘竭,直接看代碼:
all_questions = get_json_data(url_get_questions)['data']['allQuestion']
# 題號信息,由于獲取的題目沒有編號
n = 1
# 提取出來方便修改讼渊,如果有明確的題目數(shù)量动看,可以嵌套一層循環(huán)來循環(huán)獲取n套題。這里我們獲取1套題作為演示
# 之所以寫入到html文件爪幻,是因為讀取的題目中含有html的格式信息
questions_name = "第1套.html"
questions_answer_name = "第1套答案.html"
for item_question in all_questions:
# 獲取題干信息
question = item_question['question']
question_type = ['(單選題)', '(不定項選擇題)']
# 寫入題目信息
write_text("C://python_test/"+questions_name, str(n)+". "+question_type[question['type']-1]+question['content'], 'a')
answer = question['answer']
answer_option = ''
index = 0
for item_answer in answer:
# 獲取選項信息
answer_content = item_answer['content']
answer_index_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
# 寫入選項信息菱皆,加入ABCD等選項編號
write_text("C://python_test/"+questions_name, answer_index_list[index]+". "+answer_content, 'a')
# 獲取type字段的值须误,為1表示該選項為正確答案
answer_type = item_answer['type']
if answer_type == 1:
# 獲取正確答案
answer_option += answer_index_list[index]
index += 1
# 每題之間留空行
write_text("C://python_test/"+questions_name, '', 'a')
# 寫入答案到另外一個文件中
write_text("C://python_test/"+questions_answer_name, str(n)+"."+'答案: ' + answer_option, 'a')
# 答案之間留空行
write_text("C://python_test/"+questions_answer_name, '', 'a')
# 編號自增
n += 1
注意引入json模塊
import json
python中的json和dict直接對應(yīng),非常方便仇轻。
留個作業(yè):
- 通過抓取答題接口京痢,完成題目自動答題,保證每套題都得滿分~