Javascript如何實現(xiàn)簡單爬蟲(微軟小冰讀心術(shù))

1.何為網(wǎng)絡(luò)爬蟲

網(wǎng)絡(luò)爬蟲(又被稱為網(wǎng)頁蜘蛛,網(wǎng)絡(luò)機器人),是一種按照一定的規(guī)則,自動地抓取萬維網(wǎng)信息的程序或者腳本。

2.為什么使用爬蟲

一般需要人工處理的大量重復(fù)性的勞動很耗時費力,這樣重復(fù)性,無意義的事情可以交給爬蟲去做,好處是:
1. 減少了大量的人工操作,避免重復(fù)勞動;
2. 減少了給人主觀因素對數(shù)據(jù)篩選,取舍不當(dāng)造成的影響;
3. 減少了個人因為疲勞導(dǎo)致的異常錯誤或者疏漏;

3.爬蟲需要了解哪些知識

3.1 網(wǎng)絡(luò)請求

  1. URL
  2. 請求方法(POST, GET)
  3. 請求包headers
  4. 請求包內(nèi)容
  5. 返回包headers

3.2 數(shù)據(jù)解析

1. 非結(jié)構(gòu)化數(shù)據(jù)
   1.1 HTML文本(包含JavaScript代碼)
   1.2 一段文本
  這里可以采用beautifulSoup4,xpath等去方式解析;
2. 結(jié)構(gòu)化數(shù)據(jù)
    類似JSON格式的字符串,直接解析JSON數(shù)據(jù)就可以了,提取JSON的關(guān)鍵字段即可。

4.如何實現(xiàn)簡單的數(shù)據(jù)爬取

4.1 分析頁面類型和結(jié)構(gòu)

通過點擊頁面鏈接和查看瀏覽器調(diào)試器Network選項可知道數(shù)據(jù)類型是通過ajax返回還是通過頁面跳轉(zhuǎn)然后渲染頁面的形式;

4.2 頁面的數(shù)據(jù)分析

這里的數(shù)據(jù)分析是我們要對接口數(shù)據(jù)的測試,模擬真正的接口調(diào)用參數(shù),因為我們不知道真正的接口參數(shù)都包含那些,數(shù)據(jù)有沒有經(jīng)過加密或者特殊轉(zhuǎn)義處理之類的,所以這一步還是去分析測試;

4.3 模擬測試接口

游戲規(guī)則:
用戶通過點擊頁面按鈕,共計5種方式:開始,是,不是,不知道,再來一局;每次只能選擇一種方式點擊,至少要點擊10次機會,最多15次機會完成一輪數(shù)據(jù),接著進入下一輪;
這里我們需要模擬測試的數(shù)據(jù),結(jié)構(gòu)和參數(shù)大致如下:

 var text = u"開始";
 var data = {
   "content": {
     "text": text,   //純文本
     "imageUrl": ""  //微信端支持圖片輸入,pc端暫不考慮這種情況,暫且先默認(rèn)為空
   }
 };

這里接口支持了post方式獲取數(shù)據(jù),下面我采用了ajax方式獲取,這里不需要考慮跨域問題,因為我們就是下他的域名之下爬取數(shù)據(jù),很容易跳過了瀏覽器的一些限定,當(dāng)然后臺獲取還是要考慮的;

 $.ajax({
     url: 'http://webapps.msxiaobing.com/api/simplechat/getresponse?workflow=Q20',
     type: 'post',
     data: data,
     success: function(res){
         console.log(res);
         //自己的數(shù)據(jù)過濾存儲處理
     }
 });

我們只需要打開瀏覽器調(diào)試模式,在console輸入框粘貼我們的代碼,敲回車就好了;

模擬請求.png

返回的結(jié)果:


結(jié)果.png
    網(wǎng)頁地址:("http://webapps.msxiaobing.com/MindReader/")
    返回的參數(shù):
    [
      { 
        Content: {
          Avatar : "/Images/chat/xiaoice_avatar.png",
          ContentType : 1,
          Text :  "那么,我就開始提問了,哼哼~聽好,第一個問題是 ——他是虛擬的嗎?",  //純文本答案
          Metadata : {
            AnswerFeed :  "Q20GameH5",
            Page.Share.Desc: "快跑啊 [掩面]!我被人工智能完爆了。。。快來救我!",
            Page.Share.Title: "人工智能讀心術(shù):后怕!你心里想的人竟能被微軟小冰輕松猜到",
            //下面的UI是下一條問題的按鈕,每個答案返回的不一樣,可以以此作為參考一條完成答案是否結(jié)束
            UI.ButtonID.no: {   //按鈕(不是)相關(guān)信息
              displayText: "\u4e0d\u662f", 
              replyText: "\u4e0d\u662f",
              actionMeta: {}
            },
            UI.ButtonID.notsure:{  //按鈕(不確定)相關(guān)信息
              displayText: "\u4e0d\u77e5\u9053",
              replyText: "\u4e0d\u77e5\u9053", 
              actionMeta: {}
          },
          UI.ButtonID.yes:{ //按鈕(是)相關(guān)信息
              displayText: "\u662f",
              replyText: "\u662f", 
              actionMeta": {}
          },
          UI.ButtonIDList: ["UI.ButtonID.yes", "UI.ButtonID.notsure", "UI.ButtonID.no"]  //按鈕相關(guān)列表
          },
          ThumbnailUrl : null,
          VideoUrl : null,  //視頻鏈接
        },
      DelayMilliseconds:  0   //每條答案顯示延遲時間,單個答案默認(rèn)為0,多個答案之間一次返回,延遲加載,給用戶感覺是多次返回,給人感覺機器人很智能
     }
  ]

返回參數(shù)說明:
    1. 整體是返回的一個數(shù)組,因為里面有的是多個答案的情況;
    2. 針對每個答案內(nèi)容也有區(qū)別,有的是圖片,有的是文字;
    3. 數(shù)據(jù)是所有的數(shù)據(jù),可能對你不一定全部有用,個人根據(jù)需要進行取舍;

到這里,我們基本的爬蟲獲取數(shù)據(jù)就說完了,接下來就是數(shù)據(jù)的過濾和存儲了。

5.數(shù)據(jù)的過濾和存儲

5.1 過濾:

數(shù)據(jù)的過濾頁腳數(shù)據(jù)的清洗,是把數(shù)據(jù)按照我們自己定制的數(shù)據(jù)結(jié)構(gòu)來進行整合的過程,每個項目中數(shù)據(jù)的格式可能不一樣,跟人根據(jù)實際情況去過濾,不需要的可以舍棄;

5.2 數(shù)據(jù)的存儲:

5.2.1 數(shù)據(jù)庫存儲;
    目前比較通用的數(shù)據(jù)庫有mysql,mongodb,redis等,大數(shù)據(jù)方向熟悉的可以使用hadoop,hdfs等方式;
5.2.2 本地文件存儲:
    為了考慮安全性,javascript禁止讀寫本地文件,這樣的好處是防止通過網(wǎng)頁腳本修改用戶本地文件;
    所以在這里我們需要自己去寫后臺的腳本去讀寫本地文件,對nodejs,python,java不懂的去想想別的辦法,我后臺采用了nodejs和Python兩種方式實現(xiàn)了讀寫功能;

總結(jié):

上面通過測試抓取微軟小冰讀心術(shù)數(shù)據(jù)抓取和過濾及存儲,也包含也跟爬蟲相關(guān)的一些基本的概念,真正的爬蟲需要在后臺去做更好,速度也更快一些,沒有技術(shù)的限制,相對比較復(fù)雜;
前端有些限制是比較棘手的,更詳盡的關(guān)于爬蟲的信息可以找我,也可以找度娘問問;
最后我這邊貼出用Python實現(xiàn)的完整的數(shù)據(jù)爬取及存儲的源碼,里面考慮到了反爬機制,爬取結(jié)果實時的日志統(tǒng)計,方便查看爬蟲的進度,選用的方式是本地文件存儲;
這里我沒有采用現(xiàn)有的scrapy框架去做,而是自己通過requests等實現(xiàn)了爬蟲的整個抓取存儲過程,數(shù)據(jù)完全自己解析,不完善的地方還請大家批評指正,個人也可根據(jù)自己情況作參考:
## Python 實現(xiàn)微軟小冰讀心術(shù)數(shù)據(jù)抓取和存儲
# -*- 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 "運行次數(shù)為:", num+1
    #發(fā)送的數(shù)據(jù)
    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 "發(fā)送的數(shù)據(jù)是:", params

    #請求地址
    url = 'http://webapps.msxiaobing.com/api/simplechat/getresponse?workflow=Q20'
    #發(fā)送請求
    response = requests.post(url, data=params, headers=headers)
    #獲取返回數(shù)據(jù)
    text = response.text
    #計算次數(shù)
    num += 1
    try:
        #一條答案結(jié)束添加一個換行回車
        if(butStr == 5):
            dText = u"\n" + u" 第%s條答案結(jié)束" % (number+1)
            print u" 第%s條答案結(jié)束" % (number+1)
            fileHandle = open(fileName, 'a')
            fileHandle.write(dText.encode("utf-8") + '\r\n')
            fileHandle.close()

            #記錄日志查看當(dāng)前完成的條數(shù) 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()

————
前端·小龍
紙上學(xué)來終覺淺,絕知此事要躬行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容