连续一个星期了,一直是在抓数据。我的水平从小初班到了小中班。好歹是积累了些干货。感受有必要总结总结了。php
在上干货以前,我先说明下提纲。不感兴趣前部分就能够跳过了。html
第一节,普及下网络数据请求过程vue
第二节,几种语言抓取数据的方式python
第三节,Python抓取数据的方法和常见问题,包括中文乱码,post传值,域名登陆,接口获取,数据解析处理等jquery
一, 网络数据请求过程linux
我能够作一个这样的比喻,浏览网页就像是在跟另外一我的用对讲机通话。是的。虽然咱们一提到上网冲浪好像是在看电影似的,彻底是不劳而获的感觉。可是实际上这是一种错误的认识。上网就是在通话,用户在一头说话,服务器在另外一头回应。若是用户没有任何操做,你想要的网页是不会跳出来的。而浏览器就至关于对讲机,展现数据的载体。http协议就至关于通话双方的频段。git
再提到域名就不能用对讲机举例了,就要拿电话机来比喻了。咱们全部访问的网站,都表明了一个彻底独立惟一的个体,就像电话号码。可是电话号码太长了,就须要用另外一种容易记忆的方式来标示,这就出现了各类www域名,就像咱们手机通信录存储的姓名。试想一下每次使用百度都输入’61.135.169.121’这样一串数字,谁能吃得消啊。实际上在我们往浏览器输入简单的www.baidu.com的时候,浏览器仍然在须要在庞大的域名数据库里查询对应的ip地址才能访问到目标网站。好比说你想找北京水利局长,即便你很明确你要找的人是谁也没法和他通话,由于你首先要经过查询找到他的电话号码才能拨通。若是你找不到他的号码,就会获得提示‘查无此人’,这种事情发生在网络上就会出现最经典的404报错。程序员
假设网站能够正常访问了,至关于电话拨通了。可是并不表明能够正常通话了。由于对方是水利局长天天受到的骚扰多了去了,首先你得代表身份是什么地方的谁谁谁,当电话那头容许了。你这边再说一句‘下面我将代表个人诉求‘,再接下来才是进入网络访问过程。这就是传说中的三次握手。github
既然通话网路已经创建,就进入到了通话过程。一头说,“我想让某某给我唱一首伤心太平洋“”我想让某某某号给我讲一个笑话“,而后对讲机那一头就按你的要求进行回复。即便这个时候也是不能保证有求必应的,好比403错误,你想看看对方老婆照片,对方确定是不容许的;再好比500错误,对方临时上厕所开小差抽风了无法正常通话了;还有一种属于正常300提示,对方转接到其它线路。ajax
我所认识的网络请求就是这个样子了。但愿能知足诸位某些疑惑,也欢迎提出在下认识中的不足之处。
二,几中编程语言抓取数据的方式
1)
本人是搞PHP的,其它领域就业余不少了。因此先拿php说吧,
最简单的就是fopen /file_get_contents函数了,这就特别像拿来主意了,至关于网页右键查看源代码再复制下来。可是实际上他的简单也说明了它的局限性,只能抓取现成的页面代码,对于post传值和须要登陆的就无能为力了,并且它不能keeplive,就像每次通话都要从新拨号,获得需求后再挂断。有些影响效率,比较适用于文本内容比较简单并且不做处理的页面。
另外一种就是curl模拟提交了,属于伪造浏览器访问。能够设置post参数,甚至能够传输文件,应该范围很广,尤为是公众号开发,第三方接口获取数据。缺点就是配置特别多,因此我猜大部分跟我同样使用复制大法进行套用。另外,curl对于登陆验证码这种高难度的操做就不太灵了。
2)
再接下来讲一说js,jquery,vue.js,jsonp,ajax,这几种不一样的名字在获取数据方法其实都是一种方式,就是ajax异步获取。固然其中确定是有差异的,后面再细说。
它的优势是没必要整个页面从新获取,而是只获取某一个数字或图片或单独一页内容再进行填充,用户体验效果好,而且节省网络资源。缺点是它并不是真正意义上的抓取数据,而是相似于接收数据。由于ajax获取的数据及数据格式都是预先定义好的,只须要按照预约参数去获取就行了。
其中jsonp是较特殊的一种了,优势是能够跨域,就是能够获取不一样域名站点的内容。缺点是只能使用get提交。至于使用非jsonp跨域获取数据的ajax相似的在网上还有大把方法可寻。世上无难事,只怕有心人。
3)
Node.js,听说这是近两年特别火爆的语言。我就感受作程序员就是个无底洞,三天两头出来个新语言,新升级,新类库。不学不行,学了也感受就那样。因此我就看了两个小时的教程,get 了点皮毛,貌似是功能挺强大,操做也挺简单。能够完成各类高难度的数据抓取。下面是一段简单的抓取数据的源码,包含抓取到解析的过程,自行体会吧。
1 var http = require('http') 2 3 var cheerio = require('./cheerio') 4 5 var url = 'http://www.imooc.com/learn/348' 6 7 function filterChapter(html) 8 9 { 10 11 var $ = cheerio.load(html) 12 13 chapters = $('.chapter') 14 15 var courseData = [] 16 17 chapters.each(function(item){ 18 19 var chapter = $(this) 20 21 var chapterTitle = chapter.find('strong').text() 22 23 var videos = chapter.find('.video').children('li') 24 25 var chapterData = { 26 27 chapterTitle:chapterTitle, 28 29 videos:[] 30 31 } 32 33 videos.each(function(item){ 34 35 var video = $(this).find('.J-media-item') 36 37 var videoTitle = video.text() 38 39 var id = video.attr('href').split('video/')[1] 40 41 chapterData.videos.push({ 42 43 title:videoTitle, 44 45 id:id 46 47 }) 48 49 }) 50 51 courseData.push(chapterData) 52 53 }) 54 55 return courseData 56 57 } 58 59 function printCourseInfo(courseData){ 60 61 courseData.forEach(function(item){ 62 63 var chapterTitle = item.chapterTitle 64 65 var chapterVideos = item.videos 66 67 console.log(chapterTitle+'\n') 68 69 chapterVideos.forEach(function(video){ 70 71 var videoTitle = video.title 72 73 var videoId = video.id 74 75 console.log('['+videoId+']'+videoTitle) 76 77 }) 78 79 }) 80 81 } 82 83 http.get(url,function(res){ 84 85 var html = '' 86 87 res.on('data',function(data){ 88 89 html+=data 90 91 }) 92 93 res.on('end',function(){ 94 95 courseData = filterChapter(html) 96 97 //console.log(courseData) 98 99 printCourseInfo(courseData) 100 101 }) 102 103 }).on('error',function(){ 104 105 console.log('获取数据出错') 106 107 })
4)
最后再说一说python了,这门语言算是抓取数据的老牌专业户了,一提到它的名字就会联想到’爬虫’二字。我对它接触了有一个来月吧,就感受python是个啥也不是,可是啥也能干的玩意。若是不加载模块好像啥也干不了了,可是全部想到的需求只要配合相关模块全都能作。好比能作网站,能作桌面应用程序,也能写用来执行爬虫的页面。
1 user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 2 3 headers = { 'User-Agent' : user_agent } 4 5 response = urllib2.urlopen(request) 6 7 if response.getcode()!=200: 8 9 return None 10 11 return response.read()
这一段是基本的抓网页内容的代码,是否是比较牛逼,它的优势是彻底假装成浏览器的样子,连什么样的浏览器,什么样的系统,用什么方式去访问,需不须要ip代理,彻底照办。因此我仍是推荐使用python来抓取数据,代码简单,模块丰富,功能强悍,linux还自带python,只要花两三个小时就能get到一套受用的抓取方法。每次写好程序看着终端窗口不断跳动抓取过程,爽得不要不要的。
以上是我所知领域的抓取方式,毕竟也是小白一枚,未见过大世面。大牛大咖们莫要耻笑。
三,python爬虫的常见方法和问题总结
总算是进入干货阶段了,方法一说出来就很简单了。可是这些方法和问题倒是实例开发中遇到且花长时间才找到解决方法的,好比说中文乱码的问题,我在网上查询解决方案,五花八门感受都说得有理,折腾了三个多小时就是不成功。最终是本身尝试才找到方法,后面会详细说明。这里就很少说了。
首先说说爬取数据的方法,前面已经贴了一部分。在这里我们再系统的罗列一遍。基本上用得都是urllib2和urllib模块。
a)最简单的不带参数的爬取。
f = urllib2.urlopen(url, timeout=5).read()
b)带header参数的
1 user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 2 3 headers = { 'User-Agent' : user_agent } 4 5 6 7 request = urllib2.Request(url, headers = headers) 8 9 response = urllib2.urlopen(request) 10 11 if response.getcode()!=200: 12 13 return None 14 15 return response.read()
c)带post参数的
1 user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 2 3 headers = { 'User-Agent' : user_agent } 4 5 value={ 6 7 'name':'BUPT', 8 9 'age':'60', 10 11 'location':'Beijing'#字典中的内容随意,不影响# 12 13 } 14 15 postdata=urllib.urlencode(value)#对value进行编码,转换为标准编码# 16 17 request = urllib2.Request(url,data=postdata,headers = headers) 18 19 response = urllib2.urlopen(request) 20 21 if response.getcode()!=200: 22 23 return None 24 25 return response.read()
d)须要登陆和验证码的页面,利用本地cookie文件爬取,须要使用cookielib模块
1 cookie=cookielib.MozillaCookieJar() 2 3 cookie.load('cookies.txt',ignore_expires=True,ignore_discard=True) 4 5 req=urllib2.Request('http://bbs.fobshanghai.com/qun.php?subcatid=576') 6 7 opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie)) 8 9 urllib2.install_opener(opener) 10 11 response=urllib2.urlopen(req) 12 13 print response.read()
PS:cookie文件能够利用firebug工具导出,要特别注意两点,1是在文本第一行加上‘# Netscape HTTP Cookie File’,这样一句话声明这是一个cookie文件,否则会报错,http.cookiejar.LoadError: 'cookies.txt' does not look like a Netscape format cookies file 2.检查一下每行的项目是否是齐全,里面包含了每条cookie的域名,布尔值,适用途径,是否使用安全协议,过时时间,名称,值共7个属性,缺一个就会报错。若是少的话,随便从其它行复制过来就能够。若是再报错,就用一个笨方法。复制一行正确的cookie,而后每一个属性值一一复制粘贴过来。关键时刻还就得这样整。
e)获取json数据
按说获取接口的jsono数据是最简单的了,可是习惯使用beautiful模块去解析页面的DOM元素,遇到json数据反而傻眼了。因此在这里特别说明下json数据解析使用到的模块和方法。
解析json数据,我使用的是demjson模块,操做也是很简单的。使用datas = demjson.decode(soup),就能够像使用字典那样去取值了。
f)涉及用户登陆及验证码的网页
针对验证码的爬取方法,网上一样有不少教程。可是本人没有成功完成的经验因此就不敢妄言了,目前来看掌握这几种方法就已经够用了。
说完了获取接下来就谈一谈解析了,毕竟拿到手的不是json数据而是一堆零乱的html文件,怎么取本身想的数据还要有不少路要走。我常用的beautiful模块,下面也是就这个模块来进行讲解。
a),获取id,class,以及标签的DOM元素
cont = soup.find('div',id="d_list") #获取id名称为‘d_list’的div元素
link.find('span',class_="c_tit") #获取class名称为’c_tit’的span元素,注意class_不一样于’class’
b)获取元素的文本内容和属性值
soup.find('a').get_text() #获取a标签的文本内容
soup.find('a')['href'] #获取a标签的连接地址
soup.find('img')['src'] #获取图片地址
c)去除DOM元素中的部分元素
cont = soup.find('div',id="d_list")
cont.find('div',class_="ppp").extract() #去除id为’d_list’元素中的class为‘ppp’的div元素
d)去除a标签的href属性
del link.find('a')[‘href’] #实际开发中须要遍历操做
e)去除某DOM元素中的第N个某标签元素,或倒数第N个某标签元素
看似伤脑筋的题目,其实只要循环遍历其中全部元素,而后按照下标作选择判断就能够了。若是是去除倒数第N个元素,能够循环两次,第一次取到元素总数,第二次再相应操做就能够了。对于这种问题关键是思路。
f)图片地址问题
由于爬取数据的同时,也须要把图片抓取到本地,这就面临一个图片地址的问题。页面内容的图片地址和获取到本身服务器上的图片地址必须相对应。我在这里提供两个思路,一是若是抓取的网站里面的图片都是放在一个服务器上,那么就远程获取图片下载到本身服务器的相同目录下。这样图片抓到了,内容也抓到了,还不影响页面展现。因此把页面里的全部图片地址传到一个列表里,再一一远程下载到本地服务器。二是面对要抓取的网站里的图片来自不一样服务器,这就有些棘手。即便是抓取到本身服务器相同的目录,也要使用正则表达把页面里图片的地址替换掉。我估计大部门程序员对正则仍是有些怵的。因此我就采用了迂回战术,遍历出页面里全部的图片地址,分别下载到本地服务器后分别替换新地址。代码比较长就不在这里展现了,放个github库地址给你们观摩吧,https://github.com/zuoshoupai/code/tree/master/wxfunyparse。
常见的招数也就这些了,最后再说一说使人头疼的中文乱码问题。
网上对于乱码的说法有不少,大概也是因为形成乱码的缘由有不少吧。不能所有适用。我就先把通常出现的缘由罗列一下,a)页面自己是非utf8编码 ;b)程序开发环境也就是编辑器非utf8编码 c)服务响应的Accept-Encoding采用了gzip压缩 d)其它缘由,好比cmd不支持显示中文或者设置不正确
由于形成乱码的缘由有不少,因此咱们首先要排查是哪一种问题形成的。个人建议是在获取到页面的内容后立刻输出,并导出到本地文件,也就是response.read()后立刻打印。若是输出显示或是本地文件显示正常,就表示页面编码是没有问题的,问题出在解析上。因此使用response.read().encode('utf-8')解决问题。我上次遇到的问题非常奇葩,页面获取内容后打印输入正常,而后解析处理后就乱码了,我就使用各类decode,encode咋都很差使。最终没辙了,使用chardet模块把每一步的编码格式都打印出来,发如今传输过程当中,gbk变成了utf8,也就是说明明是gbk的文本当成是utf8展现,并且还转不过来。最后我把最开始获取数据的地方转换成utf8就OK了。
但愿这个小例子能给你带来一些启发,遇到bug千万不能乱,必须一步一步整理思路,肯定问题所在,才能一击而中,成功解决。
好了,关于爬数据的知识总结就到这了。如今忽然有种顿悟,所谓编程就是数据的交互,正确的传给别人和正确的拿到别人的。说来简单,中间的过程就是千辛万苦了。