《Python爬虫实战(158页).doc》由会员分享,可在线阅读,更多相关《Python爬虫实战(158页).doc(165页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Python爬虫实战Python爬虫实战(1):爬取糗事百科段子大家好,前面入门已经说了那么多基础知识了,下面我们做几个实战项目来挑战一下吧。那么这次为大家带来,Python爬取糗事百科的小段子的例子。首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来。本篇目标1.抓取糗事百科热门段子2.过滤带有图片的段子3.实现每按一次回车显示一个段子的发布时间,发布人,段子内容,点赞数。糗事百科是不需要登录的,所以也没必要用到Cookie,另外糗事百科有的段子是附图的,我们把图抓下来图片不便于显示,那么我们就尝试过滤掉有图的段子吧。好,现在我们尝试抓取一下糗
2、事百科的热门段子吧,每按下一次回车我们显示一个段子。1.确定URL并抓取页面代码首先我们确定好页面的URL是 我们初步构建如下的代码来打印页面代码内容试试看,先构造最基本的页面抓取方式,看看会不会成功12345678910111213141516# -*- coding:utf-8 -*-import urllibimport urllib2page = 1url = + str(page)try:request = urllib2.Request(url)response = urllib2.urlopen(request)print response.read()except urllib
3、2.URLError, e:if hasattr(e,code):print e.codeif hasattr(e,reason):print e.reason运行程序,哦不,它竟然报错了,真是时运不济,命途多舛啊123line 373, in _read_statusraise BadStatusLine(line)httplib.BadStatusLine: 好吧,应该是headers验证的问题,我们加上一个headers验证试试看吧,将代码修改如下1234567891011121314151617# -*- coding:utf-8 -*-import urllibimport urll
4、ib2page = 1url = + 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.read()except urllib2.URLError, e:if hasattr(e,code):print e.codeif hasattr(e,
5、reason):print e.reason嘿嘿,这次运行终于正常了,打印出了第一页的HTML代码,大家可以运行下代码试试看。在这里运行结果太长就不贴了。2.提取某一页的所有段子好,获取了HTML代码之后,我们开始分析怎样获取某一页的所有段子。首先我们审查元素看一下,按浏览器的F12,截图如下我们可以看到,每一个段子都是包裹的内容。现在我们想获取发布人,发布日期,段子内容,以及点赞的个数。不过另外注意的是,段子有些是带图片的,如果我们想在控制台显示图片是不现实的,所以我们直接把带有图片的段子给它剔除掉,只保存仅含文本的段子。所以我们加入如下正则表达式来匹配一下,用到的方法是 re.findal
6、l 是找寻所有匹配的内容。方法的用法详情可以看前面说的正则表达式的介绍。好,我们的正则表达式匹配语句书写如下,在原来的基础上追加如下代码123456content = response.read().decode(utf-8)pattern = pile(.*?a.*?.*?(.*?).*?(.*?)(.*?)(.*?),re.S)items = re.findall(pattern,content)for item in items:print item0,item1,item2,item3,item4现在正则表达式在这里稍作说明1).*? 是一个固定的搭配,.和*代表可以匹配任意无限多个字
7、符,加上?表示使用非贪婪模式进行匹配,也就是我们会尽可能短地做匹配,以后我们还会大量用到 .*? 的搭配。2)(.*?)代表一个分组,在这个正则表达式中我们匹配了五个分组,在后面的遍历item中,item0就代表第一个(.*?)所指代的内容,item1就代表第二个(.*?)所指代的内容,以此类推。3)re.S 标志代表在匹配时为点任意匹配模式,点 . 也可以代表换行符。现在我们可以看一下部分运行结果儒雅男神 2015-02-17 14:34:42小时候一个一个拆着放的举个爪7093奇怪的名字啊 2015-02-17 14:49:16回家的路,你追我赶,回家的心情和窗外的阳光一样灿烂。一路向前,
8、离亲人越来越近了。哪里有爸妈哪里才是家,希望所有糗友的爸爸妈妈都身体健康.4803这是其中的两个段子,分别打印了发布人,发布时间,发布内容,附加图片以及点赞数。其中,附加图片的内容我把图片代码整体抠了出来,这个对应item3,所以我们只需要进一步判断item3里面是否含有img这个字样就可以进行过滤了。好,我们再把上述代码中的for循环改为下面的样子1234for item in items:haveImg = re.search(img,item3)if not haveImg:print item0,item1,item2,item4现在,整体的代码如下123456789101112131
9、41516171819202122232425# -*- coding:utf-8 -*-import urllibimport urllib2import repage = 1url = + 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 = resp
10、onse.read().decode(utf-8)pattern = pile(.*?a.*?.*?(.*?).*?(.*?)(.*?)(.*?),re.S)items = re.findall(pattern,content)for item in items:haveImg = re.search(img,item3)if not haveImg:print item0,item1,item2,item4except urllib2.URLError, e:if hasattr(e,code):print e.codeif hasattr(e,reason):print e.reason运
11、行一下看下效果恩,带有图片的段子已经被剔除啦。是不是很开森?3.完善交互,设计面向对象模式好啦,现在最核心的部分我们已经完成啦,剩下的就是修一下边边角角的东西,我们想达到的目的是:按下回车,读取一个段子,显示出段子的发布人,发布日期,内容以及点赞个数。另外我们需要设计面向对象模式,引入类和方法,将代码做一下优化和封装,最后,我们的代码如下所示12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686
12、9707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110_author_ = CQC# -*- coding:utf-8 -*-import urllibimport urllib2import reimport threadimport time#糗事百科爬虫类class QSBK:#初始化方法,定义一些变量def _init_(self):self.pageIndex = 1self.user_agent = Mozilla/4.0 (compatible;
13、MSIE 5.5; Windows NT)#初始化headersself.headers = User-Agent : self.user_agent #存放段子的变量,每一个元素是每一页的段子们self.stories = #存放程序是否继续运行的变量self.enable = False#传入某一页的索引获得页面代码def getPage(self,pageIndex):try:url = + str(pageIndex)#构建请求的requestrequest = urllib2.Request(url,headers = self.headers)#利用urlopen获取页面代码res
14、ponse = urllib2.urlopen(request)#将页面转化为UTF-8编码pageCode = response.read().decode(utf-8)return pageCodeexcept urllib2.URLError, e:if hasattr(e,reason):print u连接糗事百科失败,错误原因,e.reasonreturn None#传入某一页代码,返回本页不带图片的段子列表def getPageItems(self,pageIndex):pageCode = self.getPage(pageIndex)if not pageCode:print
15、页面加载失败.return Nonepattern = pile(.*?a.*?.*?(.*?).*?(.*?)(.*?)(.*?),re.S)items = re.findall(pattern,pageCode)#用来存储每页的段子们pageStories = #遍历正则表达式匹配的信息for item in items:#是否含有图片haveImg = re.search(img,item3)#如果不含有图片,把它加入list中if not haveImg:#item0是一个段子的发布者,item1是发布时间,item2是内容,item4是点赞数pageStories.append(it
16、em0.strip(),item1.strip(),item2.strip(),item4.strip()return pageStories#加载并提取页面的内容,加入到列表中def loadPage(self):#如果当前未看的页数少于2页,则加载新一页if self.enable = True:if len(self.stories) 0:#从全局list中获取一页的段子pageStories = self.stories0#当前读到的页数加一nowPage += 1#将全局list中第一个元素删除,因为已经取出del self.stories0#输出该页的段子self.getOneSt
17、ory(pageStories,nowPage)spider = QSBK()spider.start()好啦,大家来测试一下吧,点一下回车会输出一个段子,包括发布人,发布时间,段子内容以及点赞数,是不是感觉爽爆了!我们第一个爬虫实战项目介绍到这里,欢迎大家继续关注,小伙伴们加油!Python爬虫实战(2):百度贴吧帖子大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子。与上一篇不同的是,这次我们需要用到文件的相关操作。本篇目标1.对百度贴吧的任意帖子进行抓取2.指定是否只抓取楼主发帖内容3.将抓取到的内容分析并保存到文件1.URL格式的确定首先,我们先观察一
18、下百度贴吧的任意一个帖子。比如:1234http:/ 代表资源传输使用http协议 是百度的二级域名,指向百度贴吧的服务器。/p/3138733512 是服务器某个资源,即这个帖子的地址定位符see_lz和pn是该URL的两个参数,分别代表了只看楼主和帖子页码,等于1表示该条件为真所以我们可以把URL分为两部分,一部分为基础部分,一部分为参数部分。例如,上面的URL我们划分基础部分是?see_lz=1&pn=12.页面的抓取熟悉了URL的格式,那就让我们用urllib2库来试着抓取页面内容吧。上一篇糗事百科我们最后改成了面向对象的编码方式,这次我们直接尝试一下,定义一个类名叫BDTB(百度贴吧
19、),一个初始化方法,一个获取页面的方法。其中,有些帖子我们想指定给程序是否要只看楼主,所以我们把只看楼主的参数初始化放在类的初始化上,即init方法。另外,获取页面的方法我们需要知道一个参数就是帖子页码,所以这个参数的指定我们放在该方法中。综上,我们初步构建出基础代码如下:123456789101112131415161718192021222324252627282930_author_ = CQC# -*- coding:utf-8 -*-import urllibimport urllib2import re#百度贴吧爬虫类class BDTB:#初始化,传入基地址,是否只看楼主的参数d
20、ef _init_(self,baseUrl,seeLZ):self.baseURL = baseUrlself.seeLZ = ?see_lz=+str(seeLZ)#传入页码,获取该页帖子的代码def getPage(self,pageNum):try:url = self.baseURL+ self.seeLZ + &pn= + str(pageNum)request = urllib2.Request(url)response = urllib2.urlopen(request)print response.read()return responseexcept urllib2.URL
21、Error, e:if hasattr(e,reason):print u连接百度贴吧失败,错误原因,e.reasonreturn NonebaseURL = bdtb = BDTB(baseURL,1)bdtb.getPage(1)运行代码,我们可以看到屏幕上打印出了这个帖子第一页楼主发言的所有内容,形式为HTML代码。3.提取相关信息1)提取帖子标题首先,让我们提取帖子的标题。在浏览器中审查元素,或者按F12,查看页面源代码,我们找到标题所在的代码段,可以发现这个标题的HTML代码是1纯原创我心中的NBA2014-2015赛季现役50大所以我们想提取标签中的内容,同时还要指定这个class
22、确定唯一,因为h1标签实在太多啦。正则表达式如下1(.*?)所以,我们增加一个获取页面标题的方法12345678910#获取帖子标题def getTitle(self):page = self.getPage(1)pattern = pile(.*?),re.S)result = re.search(pattern,page)if result:#print result.group(1) #测试输出return result.group(1).strip()else:return None2)提取帖子页数同样地,帖子总页数我们也可以通过分析页面中的共?页来获取。所以我们的获取总页数的方法如下
23、12345678910#获取帖子一共有多少页def getPageNum(self):page = self.getPage(1)pattern = pile(li class=l_reply_num.*?.*?(.*?),re.S)result = re.search(pattern,page)if result:#print result.group(1) #测试输出return result.group(1).strip()else:return None3)提取正文内容审查元素,我们可以看到百度贴吧每一层楼的主要内容都在标签里面,所以我们可以写如下的正则表达式1(.*?)相应地,获取页
24、面所有楼层数据的方法可以写成如下方法123456#获取每一层楼的内容,传入页面内容def getContent(self,page):pattern = pile(.*?),re.S)items = re.findall(pattern,page)for item in items:print item好,我们运行一下结果看一下真是醉了,还有一大片换行符和图片符,好口怕!既然这样,我们就要对这些文本进行处理,把各种各样复杂的标签给它剔除掉,还原精华内容,把文本处理写成一个方法也可以,不过为了实现更好的代码架构和代码重用,我们可以考虑把标签等的处理写作一个类。那我们就叫它Tool(工具类吧),里
25、面定义了一个方法,叫replace,是替换各种标签的。在类中定义了几个正则表达式,主要利用了re.sub方法对文本进行匹配后然后替换。具体的思路已经写到注释中,大家可以看一下这个类12345678910111213141516171819202122232425262728import re#处理页面标签类class Tool:#去除img标签,7位长空格removeImg = pile(| 7|)#删除超链接标签removeAddr = pile(|)#把换行的标签换为nreplaceLine = pile(|)#将表格制表替换为treplaceTD= pile()#把段落开头换为n加空两格
26、replacePara = pile()#将换行符或双换行符替换为nreplaceBR = pile(|)#将其余标签剔除removeExtraTag = pile()def replace(self,x):x = re.sub(self.removeImg,x)x = re.sub(self.removeAddr,x)x = re.sub(self.replaceLine,n,x)x = re.sub(self.replaceTD,t,x)x = re.sub(self.replacePara,n ,x)x = re.sub(self.replaceBR,n,x)x = re.sub(sel
27、f.removeExtraTag,x)#strip()将前后多余内容删除return x.strip()在使用时,我们只需要初始化一下这个类,然后调用replace方法即可。现在整体代码是如下这样子的,现在我的代码是写到这样子的12345678910111213141516171819202122232425262728import re#处理页面标签类class Tool:#去除img标签,7位长空格removeImg = pile(| 7|)#删除超链接标签removeAddr = pile(|)#把换行的标签换为nreplaceLine = pile(|)#将表格制表替换为treplac
28、eTD= pile()#把段落开头换为n加空两格replacePara = pile()#将换行符或双换行符替换为nreplaceBR = pile(|)#将其余标签剔除removeExtraTag = pile()def replace(self,x):x = re.sub(self.removeImg,x)x = re.sub(self.removeAddr,x)x = re.sub(self.replaceLine,n,x)x = re.sub(self.replaceTD,t,x)x = re.sub(self.replacePara,n ,x)x = re.sub(self.repl
29、aceBR,n,x)x = re.sub(self.removeExtraTag,x)#strip()将前后多余内容删除return x.strip()我们尝试一下,重新再看一下效果,这下经过处理之后应该就没问题了,是不是感觉好酸爽!4)替换楼层至于这个问题,我感觉直接提取楼层没什么必要呀,因为只看楼主的话,有些楼层的编号是间隔的,所以我们得到的楼层序号是不连续的,这样我们保存下来也没什么用。所以可以尝试下面的方法:1.每打印输出一段楼层,写入一行横线来间隔,或者换行符也好。2.试着重新编一个楼层,按照顺序,设置一个变量,每打印出一个结果变量加一,打印出这个变量当做楼层。这里我们尝试一下吧,看
30、看效果怎样把getContent方法修改如下123456789#获取每一层楼的内容,传入页面内容def getContent(self,page):pattern = pile(.*?),re.S)items = re.findall(pattern,page)floor = 1for item in items:print floor,u楼-nprint self.tool.replace(item)floor += 1运行一下看看效果嘿嘿,效果还不错吧,感觉真酸爽!接下来我们完善一下,然后写入文件4.写入文件最后便是写入文件的过程,过程很简单,就几句话的代码而已,主要是利用了以下两句file = open(“tb.txt”,”w”)file.writelines(obj)这里不再赘述,稍后直接贴上完善之后的代码。5.完善代码现在我们对代码进行优化,重构,在一些地方添加必要的打印信息,整理如下123