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输入框粘贴我们的代码,敲回车就好了;
返回的结果:
网页地址:("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()
————
前端·小龙
纸上学来终觉浅,绝知此事要躬行