Python爬虫实战_第1页
Python爬虫实战_第2页
Python爬虫实战_第3页
Python爬虫实战_第4页
Python爬虫实战_第5页
已阅读5页,还剩75页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Python爬虫实战Python爬虫实战(1):爬取糗事百科段子大家好,前面入门已经说了那么多基础知识了,下面我们做几个实战项目来挑战一下吧。那么这次为大家带来,Python爬取糗事百科的小段子的例子。首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来。本篇目标1.抓取糗事百科热门段子2.过滤带有图片的段子3.实现每按一次回车显示一个段子的发布时间,发布人,段子内容,点赞数。糗事百科是不需要登录的,所以也没必要用到Cookie,另外糗事百科有的段子是附图的,我们把图抓下来图片不便于显示,那么我们就尝试过滤掉有图的段子吧。好,现在我们尝试抓取一下糗

2、事百科的热门段子吧,每按下一次回车我们显示一个段子。1.确定URL并抓取页面代码首先我们确定好页面的URL是 我们初步构建如下的代码来打印页面代码内容试试看,先构造最基本的页面抓取方式,看看会不会成功12345678910111213141516# -*- coding:utf-8 -*-import urllibimport urllib2  page = 1url = ' + str(page)try:    request = urllib2.Request(url)    res

3、ponse = urllib2.urlopen(request)    print response.read()except urllib2.URLError, e:    if hasattr(e,"code"):        print e.code    if hasattr(e,"reason"):    

4、    print e.reason运行程序,哦不,它竟然报错了,真是时运不济,命途多舛啊123line 373, in _read_status raise BadStatusLine(line)httplib.BadStatusLine: ''好吧,应该是headers验证的问题,我们加上一个headers验证试试看吧,将代码修改如下1234567891011121314151617# -*- coding:utf-8 -*-import urllibimport urllib2 page = 1url = 

5、9; + str(page)user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'headers = 'User-Agent' : user_agent try:    request = urllib2.Request(url,headers = headers)    response = urllib2.urlopen(request)    print response

6、.read()except urllib2.URLError, e:    if hasattr(e,"code"):        print e.code    if hasattr(e,"reason"):        print e.reason嘿嘿,这次运行终于正常了,打印出了第一页的HTML代码,大家可以

7、运行下代码试试看。在这里运行结果太长就不贴了。2.提取某一页的所有段子好,获取了HTML代码之后,我们开始分析怎样获取某一页的所有段子。首先我们审查元素看一下,按浏览器的F12,截图如下我们可以看到,每一个段子都是<div class=”article block untagged mb15 id=”></div>包裹的内容。现在我们想获取发布人,发布日期,段子内容,以及点赞的个数。不过另外注意的是,段子有些是带图片的,如果我们想在控制台显示图片是不现实的,所以我们直接把带有图片的段子给它剔除掉,只保存仅含文本的段子。所以我们加入如下正则表达式来匹配

8、一下,用到的方法是 re.findall 是找寻所有匹配的内容。方法的用法详情可以看前面说的正则表达式的介绍。好,我们的正则表达式匹配语句书写如下,在原来的基础上追加如下代码123456content = response.read().decode('utf-8')pattern = pile('<div.*?class="author.*?>.*?<a.*?</a>.*?<a.*?>(.*?)</a>.*?<div.*?class'+    

9、0;                '="content".*?title="(.*?)">(.*?)</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)items = re.findall(pattern,content)for item

10、 in items:    print item0,item1,item2,item3,item4现在正则表达式在这里稍作说明1).*? 是一个固定的搭配,.和*代表可以匹配任意无限多个字符,加上?表示使用非贪婪模式进行匹配,也就是我们会尽可能短地做匹配,以后我们还会大量用到 .*? 的搭配。2)(.*?)代表一个分组,在这个正则表达式中我们匹配了五个分组,在后面的遍历item中,item0就代表第一个(.*?)所指代的内容,item1就代表第二个(.*?)所指代的内容,以此类推。3)re.S 标志代表在匹配时为点任意匹配模式,点 . 也可以代表换行符。

11、现在我们可以看一下部分运行结果儒雅男神 2015-02-17 14:34:42小时候一个一个拆着放的举个爪<div class=”thumb”><a href=”/article/100705418?list=hot&s=4747301 target=”_blank” onclick=”_hmt.push(_trackEvent, post, click, signlePost)”><imgsrc=” /></a></div>7093奇怪的名字啊 2015-02-17 14:49:16回家的路,你追我赶,回家的心情和

12、窗外的阳光一样灿烂。一路向前,离亲人越来越近了。哪里有爸妈哪里才是家,希望所有糗友的爸爸妈妈都身体健康.4803这是其中的两个段子,分别打印了发布人,发布时间,发布内容,附加图片以及点赞数。其中,附加图片的内容我把图片代码整体抠了出来,这个对应item3,所以我们只需要进一步判断item3里面是否含有img这个字样就可以进行过滤了。好,我们再把上述代码中的for循环改为下面的样子1234for item in items:        haveImg = re.search("img",ite

13、m3)        if not haveImg:            print item0,item1,item2,item4现在,整体的代码如下12345678910111213141516171819202122232425# -*- coding:utf-8 -*-import urllibimport urllib2import re page = 1url = '

14、; + str(page)user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'headers = 'User-Agent' : user_agent try:    request = urllib2.Request(url,headers = headers)    response = urllib2.urlopen(request)    content = respo

15、nse.read().decode('utf-8')    pattern = pile('<div.*?class="author.*?>.*?<a.*?</a>.*?<a.*?>(.*?)</a>.*?<div.*?class'+                  

16、0;      '="content".*?title="(.*?)">(.*?)</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)    items = re.findall(pattern,content)    for item in items:&#

17、160;       haveImg = re.search("img",item3)        if not haveImg:            print item0,item1,item2,item4except urllib2.URLError, e:   

18、60;if hasattr(e,"code"):        print e.code    if hasattr(e,"reason"):        print e.reason运行一下看下效果恩,带有图片的段子已经被剔除啦。是不是很开森?3.完善交互,设计面向对象模式好啦,现在最核心的部分我们已经完成啦,剩下的就是修一下边边角角的东西,我们想达到的

19、目的是:按下回车,读取一个段子,显示出段子的发布人,发布日期,内容以及点赞个数。另外我们需要设计面向对象模式,引入类和方法,将代码做一下优化和封装,最后,我们的代码如下所示1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071

20、08109110_author_ = 'CQC'# -*- coding:utf-8 -*-import urllibimport urllib2import reimport threadimport time #糗事百科爬虫类class QSBK:     #初始化方法,定义一些变量    def _init_(self):        self.pageIndex = 1  

21、;      self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'        #初始化headers        self.headers = 'User-Agent' : self.user_agent     

22、0;   #存放段子的变量,每一个元素是每一页的段子们        self.stories =         #存放程序是否继续运行的变量        self.enable = False    #传入某一页的索引获得页面代码    def

23、 getPage(self,pageIndex):        try:            url = ' + str(pageIndex)            #构建请求的request      &#

24、160;     request = urllib2.Request(url,headers = self.headers)            #利用urlopen获取页面代码            response = urllib2.urlopen(request)   

25、         #将页面转化为UTF-8编码            pageCode = response.read().decode('utf-8')            return pageCode    

26、;     except urllib2.URLError, e:            if hasattr(e,"reason"):                print u"连接糗事百科失败,错误原因",e.reason

27、                return None      #传入某一页代码,返回本页不带图片的段子列表    def getPageItems(self,pageIndex):        pageCode = self.getPage(pageIn

28、dex)        if not pageCode:            print "页面加载失败."            return None        patt

29、ern = pile('<div.*?class="author.*?>.*?<a.*?</a>.*?<a.*?>(.*?)</a>.*?<div.*?class'+                         '="content"

30、;.*?title="(.*?)">(.*?)</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)        items = re.findall(pattern,pageCode)        #用来存储每页的段子们   

31、0;    pageStories =         #遍历正则表达式匹配的信息        for item in items:            #是否含有图片         

32、   haveImg = re.search("img",item3)            #如果不含有图片,把它加入list中            if not haveImg:          

33、60;     #item0是一个段子的发布者,item1是发布时间,item2是内容,item4是点赞数                pageStories.append(item0.strip(),item1.strip(),item2.strip(),item4.strip()        retu

34、rn pageStories     #加载并提取页面的内容,加入到列表中    def loadPage(self):        #如果当前未看的页数少于2页,则加载新一页        if self.enable = True:         &#

35、160;  if len(self.stories) < 2:                #获取新一页                pageStories = self.getPageItems(self.pageIndex)  

36、;              #将该页的段子存放到全局list中                if pageStories:              

37、60;     self.stories.append(pageStories)                    #获取完之后页码索引加一,表示下次读取下一页               

38、60;    self.pageIndex += 1         #调用该方法,每次敲回车打印输出一个段子    def getOneStory(self,pageStories,page):        #遍历一页的段子        for story in

39、pageStories:            #等待用户输入            input = raw_input()            #每当输入回车一次,判断一下是否要加载新页面    

40、        self.loadPage()            #如果输入Q则程序结束            if input = "Q":         

41、60;      self.enable = False                return            print u"第%d页t发布人:%st发布时间:%sn%sn赞:%sn" %(page,story0,story1

42、,story2,story3)         #开始方法    def start(self):        print u"正在读取糗事百科,按回车查看新段子,Q退出"        #使变量为True,程序可以正常运行     &#

43、160;  self.enable = True        #先加载一页内容        self.loadPage()        #局部变量,控制当前读到了第几页        nowPage = 0    

44、    while self.enable:            if len(self.stories)>0:                #从全局list中获取一页的段子        

45、        pageStories = self.stories0                #当前读到的页数加一                nowPage += 1  

46、;              #将全局list中第一个元素删除,因为已经取出                del self.stories0             &

47、#160;  #输出该页的段子                self.getOneStory(pageStories,nowPage)  spider = QSBK()spider.start()好啦,大家来测试一下吧,点一下回车会输出一个段子,包括发布人,发布时间,段子内容以及点赞数,是不是感觉爽爆了!我们第一个爬虫实战项目介绍到这里,欢迎大家继续关注,小伙伴们加油!Python爬虫实战(2)

48、:百度贴吧帖子大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子。与上一篇不同的是,这次我们需要用到文件的相关操作。本篇目标1.对百度贴吧的任意帖子进行抓取2.指定是否只抓取楼主发帖内容3.将抓取到的内容分析并保存到文件1.URL格式的确定首先,我们先观察一下百度贴吧的任意一个帖子。比如:1234http:/  代表资源传输使用http协议 是百度的二级域名,指向百度贴吧的服务器。/p/3138733512 是服务器某个资源,即这个帖子的地址定位符see_lz和pn是该URL的两个参数,分别代表了只看楼主和帖子页码,等于1表示该条件为真所以我们可以把

49、URL分为两部分,一部分为基础部分,一部分为参数部分。例如,上面的URL我们划分基础部分是 2.页面的抓取熟悉了URL的格式,那就让我们用urllib2库来试着抓取页面内容吧。上一篇糗事百科我们最后改成了面向对象的编码方式,这次我们直接尝试一下,定义一个类名叫BDTB(百度贴吧),一个初始化方法,一个获取页面的方法。其中,有些帖子我们想指定给程序是否要只看楼主,所以我们把只看楼主的参数初始化放在类的初始化上,即init方法。另外,获取页面的方法我们需要知道一个参数就是帖子页码,所以这个参数的指定我们放在该方法中。综上,我们初步构建出基础代码如下:1234567891011121314

50、15161718192021222324252627282930_author_ = 'CQC'# -*- coding:utf-8 -*-import urllibimport urllib2import re #百度贴吧爬虫类class BDTB:     #初始化,传入基地址,是否只看楼主的参数    def _init_(self,baseUrl,seeLZ):        self.bas

51、eURL = baseUrl        self.seeLZ = '?see_lz='+str(seeLZ)     #传入页码,获取该页帖子的代码    def getPage(self,pageNum):        try:        &

52、#160;   url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)            request = urllib2.Request(url)            response = urllib2.urlopen(request)&#

53、160;           print response.read()            return response        except urllib2.URLError, e:        

54、;    if hasattr(e,"reason"):                print u"连接百度贴吧失败,错误原因",e.reason                return No

55、ne baseURL = 'bdtb = BDTB(baseURL,1)bdtb.getPage(1)运行代码,我们可以看到屏幕上打印出了这个帖子第一页楼主发言的所有内容,形式为HTML代码。3.提取相关信息1)提取帖子标题首先,让我们提取帖子的标题。在浏览器中审查元素,或者按F12,查看页面源代码,我们找到标题所在的代码段,可以发现这个标题的HTML代码是1<h1 title="纯原创我心中的NBA2014-2015赛季现役50大" style="width: 396px">纯原创我心中的NBA2014-2015赛季现役5

56、0大</h1>所以我们想提取<h1>标签中的内容,同时还要指定这个class确定唯一,因为h1标签实在太多啦。正则表达式如下1<h1 class="core_title_txt.*?>(.*?)</h1>所以,我们增加一个获取页面标题的方法12345678910#获取帖子标题def getTitle(self):    page = self.getPage(1)    pattern = pile('<h1 class="core_

57、title_txt.*?>(.*?)</h1>',re.S)    result = re.search(pattern,page)    if result:        #print result.group(1)  #测试输出        return result.group(1).strip() &

58、#160;  else:        return None2)提取帖子页数同样地,帖子总页数我们也可以通过分析页面中的共?页来获取。所以我们的获取总页数的方法如下12345678910#获取帖子一共有多少页def getPageNum(self):    page = self.getPage(1)    pattern = pile('<li class="l_reply_num.*?&

59、lt;/span>.*?<span.*?>(.*?)</span>',re.S)    result = re.search(pattern,page)    if result:        #print result.group(1)  #测试输出        return result.group(1

60、).strip()    else:        return None3)提取正文内容审查元素,我们可以看到百度贴吧每一层楼的主要内容都在<div id=”post_content_xxxx”></div>标签里面,所以我们可以写如下的正则表达式1<div id="post_content_.*?>(.*?)</div>相应地,获取页面所有楼层数据的方法可以写成如下方法123456#获取每一层楼的内容,传入页面内容

61、def getContent(self,page):    pattern = pile('<div id="post_content_.*?>(.*?)</div>',re.S)    items = re.findall(pattern,page)    for item in items:        print item好,我们运行一下结

62、果看一下真是醉了,还有一大片换行符和图片符,好口怕!既然这样,我们就要对这些文本进行处理,把各种各样复杂的标签给它剔除掉,还原精华内容,把文本处理写成一个方法也可以,不过为了实现更好的代码架构和代码重用,我们可以考虑把标签等的处理写作一个类。那我们就叫它Tool(工具类吧),里面定义了一个方法,叫replace,是替换各种标签的。在类中定义了几个正则表达式,主要利用了re.sub方法对文本进行匹配后然后替换。具体的思路已经写到注释中,大家可以看一下这个类12345678910111213141516171819202122232425262728import re #处理页面标签类c

63、lass Tool:    #去除img标签,7位长空格    removeImg = pile('<img.*?>| 7|')    #删除超链接标签    removeAddr = pile('<a.*?>|</a>')    #把换行的标签换为n    replaceLine = pi

64、le('<tr>|<div>|</div>|</p>')    #将表格制表<td>替换为t    replaceTD= pile('<td>')    #把段落开头换为n加空两格    replacePara = pile('<p.*?>')    #将换行符或双换

65、行符替换为n    replaceBR = pile('|')    #将其余标签剔除    removeExtraTag = pile('<.*?>')    def replace(self,x):        x = re.sub(self.removeImg,"",x) &#

66、160;      x = re.sub(self.removeAddr,"",x)        x = re.sub(self.replaceLine,"n",x)        x = re.sub(self.replaceTD,"t",x)      

67、  x = re.sub(self.replacePara,"n    ",x)        x = re.sub(self.replaceBR,"n",x)        x = re.sub(self.removeExtraTag,"",x)       

68、; #strip()将前后多余内容删除        return x.strip()在使用时,我们只需要初始化一下这个类,然后调用replace方法即可。现在整体代码是如下这样子的,现在我的代码是写到这样子的12345678910111213141516171819202122232425262728import re #处理页面标签类class Tool:    #去除img标签,7位长空格    removeI

69、mg = pile('<img.*?>| 7|')    #删除超链接标签    removeAddr = pile('<a.*?>|</a>')    #把换行的标签换为n    replaceLine = pile('<tr>|<div>|</div>|</p>')   &

70、#160;#将表格制表<td>替换为t    replaceTD= pile('<td>')    #把段落开头换为n加空两格    replacePara = pile('<p.*?>')    #将换行符或双换行符替换为n    replaceBR = pile('|')   &#

71、160;#将其余标签剔除    removeExtraTag = pile('<.*?>')    def replace(self,x):        x = re.sub(self.removeImg,"",x)        x = re.sub(self.removeAddr,""

72、,x)        x = re.sub(self.replaceLine,"n",x)        x = re.sub(self.replaceTD,"t",x)        x = re.sub(self.replacePara,"n    ",x)

73、0;       x = re.sub(self.replaceBR,"n",x)        x = re.sub(self.removeExtraTag,"",x)        #strip()将前后多余内容删除        return

74、x.strip()我们尝试一下,重新再看一下效果,这下经过处理之后应该就没问题了,是不是感觉好酸爽!4)替换楼层至于这个问题,我感觉直接提取楼层没什么必要呀,因为只看楼主的话,有些楼层的编号是间隔的,所以我们得到的楼层序号是不连续的,这样我们保存下来也没什么用。所以可以尝试下面的方法:1.每打印输出一段楼层,写入一行横线来间隔,或者换行符也好。2.试着重新编一个楼层,按照顺序,设置一个变量,每打印出一个结果变量加一,打印出这个变量当做楼层。这里我们尝试一下吧,看看效果怎样把getContent方法修改如下123456789#获取每一层楼的内容,传入页面内容def getContent(self

75、,page):    pattern = pile('<div id="post_content_.*?>(.*?)</div>',re.S)    items = re.findall(pattern,page)    floor = 1    for item in items:        print

76、 floor,u"楼-n"        print self.tool.replace(item)        floor += 1运行一下看看效果嘿嘿,效果还不错吧,感觉真酸爽!接下来我们完善一下,然后写入文件4.写入文件最后便是写入文件的过程,过程很简单,就几句话的代码而已,主要是利用了以下两句file = open(“tb.txt”,”w”)file.writelines(obj)这里不再赘述,稍后直接贴上

77、完善之后的代码。5.完善代码现在我们对代码进行优化,重构,在一些地方添加必要的打印信息,整理如下123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120

78、121122123124125126127128129130131132133134135136137138139140141142143144145146147148_author_ = 'CQC'# -*- coding:utf-8 -*-import urllibimport urllib2import re #处理页面标签类class Tool:    #去除img标签,7位长空格    removeImg = pile('<img.*?>| 7|')&

79、#160;   #删除超链接标签    removeAddr = pile('<a.*?>|</a>')    #把换行的标签换为n    replaceLine = pile('<tr>|<div>|</div>|</p>')    #将表格制表<td>替换为t  

80、60; replaceTD= pile('<td>')    #把段落开头换为n加空两格    replacePara = pile('<p.*?>')    #将换行符或双换行符替换为n    replaceBR = pile('|')    #将其余标签剔除    remo

81、veExtraTag = pile('<.*?>')    def replace(self,x):        x = re.sub(self.removeImg,"",x)        x = re.sub(self.removeAddr,"",x)      &

82、#160; x = re.sub(self.replaceLine,"n",x)        x = re.sub(self.replaceTD,"t",x)        x = re.sub(self.replacePara,"n    ",x)       &#

83、160;x = re.sub(self.replaceBR,"n",x)        x = re.sub(self.removeExtraTag,"",x)        #strip()将前后多余内容删除        return x.strip()  #百度贴吧爬虫类class BDTB:

84、     #初始化,传入基地址,是否只看楼主的参数    def _init_(self,baseUrl,seeLZ,floorTag):        #base链接地址        self.baseURL = baseUrl        #是否只看楼主

85、0;       self.seeLZ = '?see_lz='+str(seeLZ)        #HTML标签剔除工具类对象        self.tool = Tool()        #全局file变量,文件写入操作对象   &#

86、160;    self.file = None        #楼层标号,初始为1        self.floor = 1        #默认的标题,如果没有成功获取到标题的话则会用这个标题        self.defaultT

87、itle = u"百度贴吧"        #是否写入楼分隔符的标记        self.floorTag = floorTag     #传入页码,获取该页帖子的代码    def getPage(self,pageNum):        t

88、ry:            #构建URL            url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)            request =

89、 urllib2.Request(url)            response = urllib2.urlopen(request)            #返回UTF-8格式编码内容            return respons

90、e.read().decode('utf-8')        #无法连接,报错        except urllib2.URLError, e:            if hasattr(e,"reason"):     &#

91、160;          print u"连接百度贴吧失败,错误原因",e.reason                return None     #获取帖子标题    def getTitle(self,page):

92、60;       #得到标题的正则表达式        pattern = pile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)        result = re.search(pattern,page)     

93、;   if result:            #如果存在,则返回标题            return result.group(1).strip()        else:     

94、60;      return None     #获取帖子一共有多少页    def getPageNum(self,page):        #获取帖子页数的正则表达式        pattern = pile('<li class="l_reply_num

95、.*?</span>.*?<span.*?>(.*?)</span>',re.S)        result = re.search(pattern,page)        if result:            return result.group(1).stri

96、p()        else:            return None     #获取每一层楼的内容,传入页面内容    def getContent(self,page):        #匹配所有楼层的内容 

97、       pattern = pile('<div id="post_content_.*?>(.*?)</div>',re.S)        items = re.findall(pattern,page)        contents =      

98、0;  for item in items:            #将文本进行去除标签处理,同时在前后加入换行符            content = "n"+self.tool.replace(item)+"n"            contents.append(content.encode('utf-8') 

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论