Javascript如何实现简单爬虫(微软小冰读心术)

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()

————
前端·小龙
纸上学来终觉浅,绝知此事要躬行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容