本实例地址:http://python.jobbole.com/81353/
本篇目标
1.对百度贴吧的任意帖子进行抓取
2.指定是否只抓取楼主发帖内容
3.将抓取到的内容分析并保存到文件
1.URL格式的确定
比如:http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1
http:// 代表资源传输使用http协议 tieba.baidu.com 是百度的二级域名,指向百度贴吧的服务器。 /p/3138733512 是服务器某个资源,即这个帖子的地址定位符 see_lz和pn是该URL的两个参数,分别代表了只看楼主和帖子页码,等于1表示该条件为真
2.页面的抓取
打印出了这个帖子第一页楼主发言的所有内容,形式为HTML代码
扫描二维码关注公众号,回复:
524648 查看本文章
import urllib import urllib.request import re #百度贴吧爬虫类 class BDTB: #初始化,传入基地址,是否只看楼主的参数 def __init__(self,baseUrl,seeLZ): self.baseURL = baseUrl self.seeLZ = '?see_lz='+str(seeLZ) #传入页码,获取该页帖子的代码 def getPage(self,pageNum): try: url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum) request = urllib.request.Request(url) response = urllib.request.urlopen(request) print(response.read()) return response except urllib.error.URLError as e: if hasattr(e,"reason"): print(u"连接百度贴吧失败,错误原因",e.reason) return None baseURL = 'http://tieba.baidu.com/p/3138733512' bdtb = BDTB(baseURL,1) bdtb.getPage(1)
结果:
3.提取相关信息
1)提取帖子标题
import urllib import urllib.request import re #百度贴吧爬虫类 class BDTB: #初始化,传入基地址,是否只看楼主的参数 def __init__(self,baseUrl,seeLZ): self.baseURL = baseUrl self.seeLZ = '?see_lz='+str(seeLZ) #传入页码,获取该页帖子的代码 def getPage(self,pageNum): try: url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum) request = urllib.request.Request(url) response = urllib.request.urlopen(request) #print(response.read()) return response.read().decode('utf-8') except urllib.error.URLError as e: if hasattr(e,"reason"): print(u"连接百度贴吧失败,错误原因",e.reason) return None #获取帖子标题 def getTitle(self,page): #得到标题的正则表达式 pattern = re.compile('<h3 class="core_title_txt.*?>(.*?)</h3>',re.S) result = re.search(pattern,page) if result: #如果存在,则返回标题 return result.group(1).strip() else: return None baseURL = 'http://tieba.baidu.com/p/3138733512' bdtb = BDTB(baseURL,1) bdpage = bdtb.getPage(1) title = bdtb.getTitle(bdpage) print(title)
2)提取帖子页数
import urllib import urllib.request import re #百度贴吧爬虫类 class BDTB: #初始化,传入基地址,是否只看楼主的参数 def __init__(self,baseUrl,seeLZ): self.baseURL = baseUrl self.seeLZ = '?see_lz='+str(seeLZ) #传入页码,获取该页帖子的代码 def getPage(self,pageNum): try: url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum) request = urllib.request.Request(url) response = urllib.request.urlopen(request) #print(response.read()) return response.read().decode('utf-8') except urllib.error.URLError as e: if hasattr(e,"reason"): print(u"连接百度贴吧失败,错误原因",e.reason) return None #获取帖子一共有多少页 def getPageNum(self,page): #获取帖子页数的正则表达式 pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S) result = re.search(pattern,page) if result: return result.group(1).strip() else: return None baseURL = 'http://tieba.baidu.com/p/3138733512' bdtb = BDTB(baseURL,1) bdpage = bdtb.getPage(1) #title = bdtb.getTitle(bdpage) pageNum = bdtb.getPageNum(bdpage); print(pageNum)
3)提取正文内容
4)替换楼层
因为只看楼主的话,有些楼层的编号是间隔的,所以我们得到的楼层序号是不连续的,这样我们保存下来也没什么用。所以可以尝试下面的方法:
a.每打印输出一段楼层,写入一行横线来间隔,或者换行符也好。
b.试着重新编一个楼层,按照顺序,设置一个变量,每打印出一个结果变量加一,打印出这个变量当做楼层。
4.写入文件
最后便是写入文件的过程,过程很简单,就几句话的代码而已,主要是利用了以下两句
file = open(“tb.txt”,”w”)
file.writelines(obj)
5.完善代码
现在我们对代码进行优化,重构
完整代码如下:
__author__ = 'CQC' # -*- coding:utf-8 -*- import urllib import urllib.request import re #处理页面标签类 class Tool: #去除img标签,7位长空格 removeImg = re.compile('<img.*?>| {7}|') #删除超链接标签 removeAddr = re.compile('<a.*?>|</a>') #把换行的标签换为\n replaceLine = re.compile('<tr>|<div>|</div>|</p>') #将表格制表<td>替换为\t replaceTD= re.compile('<td>') #把段落开头换为\n加空两格 replacePara = re.compile('<p.*?>') #将换行符或双换行符替换为\n replaceBR = re.compile('<br><br>|<br>') #将其余标签剔除 removeExtraTag = re.compile('<.*?>') 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(self.removeExtraTag,"",x) #strip()将前后多余内容删除 return x.strip() #百度贴吧爬虫类 class BDTB: #初始化,传入基地址,是否只看楼主的参数 def __init__(self,baseUrl,seeLZ,floorTag): #base链接地址 self.baseURL = baseUrl #是否只看楼主 self.seeLZ = '?see_lz='+str(seeLZ) #HTML标签剔除工具类对象 self.tool = Tool() #全局file变量,文件写入操作对象 self.file = None #楼层标号,初始为1 self.floor = 1 #默认的标题,如果没有成功获取到标题的话则会用这个标题 self.defaultTitle = u"百度贴吧" #是否写入楼分隔符的标记 self.floorTag = floorTag #传入页码,获取该页帖子的代码 def getPage(self,pageNum): try: #构建URL url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum) request = urllib.request.Request(url) response = urllib.request.urlopen(request) #返回UTF-8格式编码内容 return response.read().decode('utf-8') #无法连接,报错 except urllib.error.URLError as e: if hasattr(e,"reason"): print(u"连接百度贴吧失败,错误原因",e.reason) return None #获取帖子标题 def getTitle(self,page): #得到标题的正则表达式 pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S) result = re.search(pattern,page) if result: #如果存在,则返回标题 return result.group(1).strip() else: return None #获取帖子一共有多少页 def getPageNum(self,page): #获取帖子页数的正则表达式 pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S) result = re.search(pattern,page) if result: return result.group(1).strip() else: return None #获取每一层楼的内容,传入页面内容 def getContent(self,page): #匹配所有楼层的内容 pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S) items = re.findall(pattern,page) contents = [] for item in items: #将文本进行去除标签处理,同时在前后加入换行符 content = "\n"+self.tool.replace(item)+"\n" contents.append(content.encode('utf-8')) return contents def setFileTitle(self,title): #如果标题不是为None,即成功获取到标题 if title is not None: self.file = open(title + ".txt","wb") else: self.file = open(self.defaultTitle + ".txt","wb") def writeData(self,contents): #向文件写入每一楼的信息 for item in contents: if self.floorTag == '1': #楼之间的分隔符 floorLine = "\n" + str(self.floor) + "-----------------------------------------------------------------------------------------\n" floorLine = bytes(floorLine,encoding="utf-8") self.file.write(floorLine) self.file.write(item) self.floor += 1 def start(self): indexPage = self.getPage(1) pageNum = self.getPageNum(indexPage) title = self.getTitle(indexPage) self.setFileTitle(title) if pageNum == None: print("URL已失效,请重试") return try: print("该帖子共有" + str(pageNum) + "页") for i in range(1,int(pageNum)+1): print("正在写入第" + str(i) + "页数据") page = self.getPage(i) contents = self.getContent(page) self.writeData(contents) #出现写入异常 except IOError as e: print("写入异常,原因" + e.message) finally: print("写入任务完成") print(u"请输入帖子代号") baseURL = 'http://tieba.baidu.com/p/' + str(input(u'http://tieba.baidu.com/p/')) seeLZ = input("是否只获取楼主发言,是输入1,否输入0\n") floorTag = input("是否写入楼层信息,是输入1,否输入0\n") bdtb = BDTB(baseURL,seeLZ,floorTag) bdtb.start()
输出的文件内容为: