美文网首页
Javascript如何实现简单爬虫(微软小冰读心术)

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

作者: 壮哉我大前端 | 来源:发表于2017-04-06 21:30 被阅读0次

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

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

相关文章

网友评论

      本文标题:Javascript如何实现简单爬虫(微软小冰读心术)

      本文链接:https://www.haomeiwen.com/subject/gncqattx.html