1.何為網絡爬蟲
網絡爬蟲(又被稱為網頁蜘蛛,網絡機器人),是一種按照一定的規則,自動地抓取萬維網信息的程序或者腳本。
2.為什么使用爬蟲
一般需要人工處理的大量重復性的勞動很耗時費力,這樣重復性,無意義的事情可以交給爬蟲去做,好處是:
1. 減少了大量的人工操作,避免重復勞動;
2. 減少了給人主觀因素對數據篩選,取舍不當造成的影響;
3. 減少了個人因為疲勞導致的異常錯誤或者疏漏;
3.爬蟲需要了解哪些知識
3.1 網絡請求
1. URL
2. 請求方法(POST, GET)
3. 請求包headers
4. 請求包內容
5. 返回包headers
3.2 數據解析
1. 非結構化數據
1.1 HTML文本(包含JavaScript代碼)
1.2 一段文本
這里可以采用beautifulSoup4,xpath等去方式解析;
2. 結構化數據
類似JSON格式的字符串,直接解析JSON數據就可以了,提取JSON的關鍵字段即可。
4.如何實現簡單的數據爬取
4.1 分析頁面類型和結構
通過點擊頁面鏈接和查看瀏覽器調試器Network選項可知道數據類型是通過ajax返回還是通過頁面跳轉然后渲染頁面的形式;
4.2 頁面的數據分析
這里的數據分析是我們要對接口數據的測試,模擬真正的接口調用參數,因為我們不知道真正的接口參數都包含那些,數據有沒有經過加密或者特殊轉義處理之類的,所以這一步還是去分析測試;
4.3 模擬測試接口
游戲規則:
用戶通過點擊頁面按鈕,共計5種方式:開始,是,不是,不知道,再來一局;每次只能選擇一種方式點擊,至少要點擊10次機會,最多15次機會完成一輪數據,接著進入下一輪;
這里我們需要模擬測試的數據,結構和參數大致如下:
var text = u"開始";
var data = {
"content": {
"text": text, //純文本
"imageUrl": "" //微信端支持圖片輸入,pc端暫不考慮這種情況,暫且先默認為空
}
};
這里接口支持了post方式獲取數據,下面我采用了ajax方式獲取,這里不需要考慮跨域問題,因為我們就是下他的域名之下爬取數據,很容易跳過了瀏覽器的一些限定,當然后臺獲取還是要考慮的;
$.ajax({
url: 'http://webapps.msxiaobing.com/api/simplechat/getresponse?workflow=Q20',
type: 'post',
data: data,
success: function(res){
console.log(res);
//自己的數據過濾存儲處理
}
});
我們只需要打開瀏覽器調試模式,在console輸入框粘貼我們的代碼,敲回車就好了;
模擬請求.png
返回的結果:
結果.png
網頁地址:("http://webapps.msxiaobing.com/MindReader/")
返回的參數:
[
{
Content: {
Avatar : "/Images/chat/xiaoice_avatar.png",
ContentType : 1,
Text : "那么,我就開始提問了,哼哼~聽好,第一個問題是 ——他是虛擬的嗎?", //純文本答案
Metadata : {
AnswerFeed : "Q20GameH5",
Page.Share.Desc: "快跑啊 [掩面]!我被人工智能完爆了。。。快來救我!",
Page.Share.Title: "人工智能讀心術:后怕!你心里想的人竟能被微軟小冰輕松猜到",
//下面的UI是下一條問題的按鈕,每個答案返回的不一樣,可以以此作為參考一條完成答案是否結束
UI.ButtonID.no: { //按鈕(不是)相關信息
displayText: "\u4e0d\u662f",
replyText: "\u4e0d\u662f",
actionMeta: {}
},
UI.ButtonID.notsure:{ //按鈕(不確定)相關信息
displayText: "\u4e0d\u77e5\u9053",
replyText: "\u4e0d\u77e5\u9053",
actionMeta: {}
},
UI.ButtonID.yes:{ //按鈕(是)相關信息
displayText: "\u662f",
replyText: "\u662f",
actionMeta": {}
},
UI.ButtonIDList: ["UI.ButtonID.yes", "UI.ButtonID.notsure", "UI.ButtonID.no"] //按鈕相關列表
},
ThumbnailUrl : null,
VideoUrl : null, //視頻鏈接
},
DelayMilliseconds: 0 //每條答案顯示延遲時間,單個答案默認為0,多個答案之間一次返回,延遲加載,給用戶感覺是多次返回,給人感覺機器人很智能
}
]
返回參數說明:
1. 整體是返回的一個數組,因為里面有的是多個答案的情況;
2. 針對每個答案內容也有區別,有的是圖片,有的是文字;
3. 數據是所有的數據,可能對你不一定全部有用,個人根據需要進行取舍;
到這里,我們基本的爬蟲獲取數據就說完了,接下來就是數據的過濾和存儲了。
5.數據的過濾和存儲
5.1 過濾:
數據的過濾頁腳數據的清洗,是把數據按照我們自己定制的數據結構來進行整合的過程,每個項目中數據的格式可能不一樣,跟人根據實際情況去過濾,不需要的可以舍棄;
5.2 數據的存儲:
5.2.1 數據庫存儲;
目前比較通用的數據庫有mysql,mongodb,redis等,大數據方向熟悉的可以使用hadoop,hdfs等方式;
5.2.2 本地文件存儲:
為了考慮安全性,javascript禁止讀寫本地文件,這樣的好處是防止通過網頁腳本修改用戶本地文件;
所以在這里我們需要自己去寫后臺的腳本去讀寫本地文件,對nodejs,python,java不懂的去想想別的辦法,我后臺采用了nodejs和Python兩種方式實現了讀寫功能;
總結:
上面通過測試抓取微軟小冰讀心術數據抓取和過濾及存儲,也包含也跟爬蟲相關的一些基本的概念,真正的爬蟲需要在后臺去做更好,速度也更快一些,沒有技術的限制,相對比較復雜;
前端有些限制是比較棘手的,更詳盡的關于爬蟲的信息可以找我,也可以找度娘問問;
最后我這邊貼出用Python實現的完整的數據爬取及存儲的源碼,里面考慮到了反爬機制,爬取結果實時的日志統計,方便查看爬蟲的進度,選用的方式是本地文件存儲;
這里我沒有采用現有的scrapy框架去做,而是自己通過requests等實現了爬蟲的整個抓取存儲過程,數據完全自己解析,不完善的地方還請大家批評指正,個人也可根據自己情況作參考:
## Python 實現微軟小冰讀心術數據抓取和存儲
# -*- coding:utf8 -*-
import requests
import json
import random
import time
import sys
sys.setrecursionlimit(100000000)
number = 0
fileName = 'F:\\python\\duxinshu\\data\\dxs1.txt'
logfile = 'F:\\python\\duxinshu\\log.txt'
def GetCode(num, butStr=None):
print "運行次數為:", num+1
#發送的數據
params1 = {"senderId": "1c5e0e31-da20-48a5-1161-0f6edcfc8d6d", "content": {"text": u"玩", "imageUrl": "", "metadata": {"Q20H5Enter": "true"}}}
text2 = ''
if(butStr == 1):
text2 = u"不是"
elif(butStr == 2):
text2 = u"是"
elif(butStr == 3):
text2 = u"不知道"
elif(butStr == 4):
text2 = u"開始"
elif(butStr == 5):
text2 = u"再來一局"
params2 = {"senderId": "1c5e0e31-da20-48a5-1161-0f6edcfc8d6d", "content": {"text": text2, "imageUrl": ""}}
#http請求頭
headers1 = {
"Accept": "*/*",
"Accept-Language": "zh-Cn,zh;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Contention": "keep-alive",
"Cache-Control": "max-age=0",
"Content-length": "123",
"Content-Type": "application/json",
"Cookie": "ai_user=ZDqgj|2016-03-03T01:19:59.052Z; cpid=YDMiTV62QDJeSlC2f7FRSMi0ELLjNNowIU0-s1g1KEpJAA; salt=CE134ACD710FBFD79D26F94B69B8CE1E; ARRAffinity=156a6969786353112179d7a168f62f80d2aa2be36d566a73987a623c804e2516",
"Host": "webapps.msxiaobing.com",
"Origin": "http://webapps.msxiaobing.com",
"Referer": "http://webapps.msxiaobing.com/MindReader",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
"X-Requested-With": "XMLHttpRequest"
}
#http請求頭
headers2 = {
"Accept": "*/*",
"Accept-Language": "zh-Cn,zh;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Contention": "keep-alive",
# "Cache-Control": "max-age=0",
"Content-length": "93",
"Content-Type": "application/json",
"Cookie": "ai_user=ZDqgj|2016-03-03T01:19:59.052Z; cpid=YDMiTV62QDJeSlC2f7FRSMi0ELLjNNowIU0-s1g1KEpJAA; salt=CE134ACD710FBFD79D26F94B69B8CE1E; ARRAffinity=156a6969786353112179d7a168f62f80d2aa2be36d566a73987a623c804e2516",
"Host": "webapps.msxiaobing.com",
"Origin": "http://webapps.msxiaobing.com",
"Referer": "http://webapps.msxiaobing.com/MindReader",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
"X-Requested-With": "XMLHttpRequest"
}
if num == 0:
params = params1
headers = headers1
else:
params = params2
headers = headers2
sendData = params
params = json.dumps(params)
# print "發送的數據是:", params
#請求地址
url = 'http://webapps.msxiaobing.com/api/simplechat/getresponse?workflow=Q20'
#發送請求
response = requests.post(url, data=params, headers=headers)
#獲取返回數據
text = response.text
#計算次數
num += 1
try:
#一條答案結束添加一個換行回車
if(butStr == 5):
dText = u"\n" + u" 第%s條答案結束" % (number+1)
print u" 第%s條答案結束" % (number+1)
fileHandle = open(fileName, 'a')
fileHandle.write(dText.encode("utf-8") + '\r\n')
fileHandle.close()
#記錄日志查看當前完成的條數 logfile
fileHandle = open(logfile, 'a')
fileHandle.write(dText.encode("utf-8") + '\n')
fileHandle.close()
number += 1
global number
if number > 5:
number = 0
number = number
#讓程序暫停幾秒鐘,防止被封掉ip
pauseTime = random.choice([3, 4, 5, 6, 7])
time.sleep(pauseTime)
if(number%1000 == 0):
fileName = 'F:\\python\\duxinshu\\data\\dxs%s.txt' % (number/1000 + 1)
global fileName
fileName = fileName
# print "fileName", fileName
dict_mid = json.loads(text)
for i in dict_mid:
i["content"]["answer"] = sendData["content"]["text"]
text1 = json.dumps(dict_mid, ensure_ascii=False)
#正常存儲文件
fileHandle = open(fileName, 'a')
# fileHandle.write(text.encode("utf-8") + '\n')
fileHandle.write(text1.encode("utf-8") + '\n')
fileHandle.close()
if(dict_mid[0] and dict_mid[0]["content"] and dict_mid[0]["content"]["metadata"]):
dictButton = dict_mid[0]["content"]["metadata"]
else:
dictButton = []
nextButton = []
for i in dictButton:
if(i == "UI.ButtonID.no"):
nextButton.append(1)
elif(i == "UI.ButtonID.yes"):
nextButton.append(2)
# elif(i == "UI.ButtonID.notsure"):
# nextButton.append(3)
elif(i == "UI.ButtonID.Start"):
nextButton.append(4)
elif(i == "UI.ButtonID.again"):
nextButton.append(5)
elif(i == "UI.ButtonID.ok"):
nextButton.append(2)
butStr = random.choice(nextButton)
if(text):
GetCode(num, butStr)
except Exception, e:
print "error", e
def getDXSdata():
GetCode(0)
if __name__ == "__main__":
getDXSdata()
————
前端·小龍
紙上學來終覺淺,絕知此事要躬行