Python爬虫实战(1)——百度贴吧抓取帖子并保存内容和图片
来源:互联网 发布:淘宝怎么使用购物车 编辑:程序博客网 时间:2024/04/30 02:56
最近在网上看了很多的爬虫脚本,写的参差不齐,但是其中有很多写的非常的优秀,代码质量很高,规范性也很好,很具有代表性,非常值得我们去学习!~
写好一个python爬虫需要有以下几个必备条件:
1、足够好的代码规范(等号前后加空格、逗号后加空格等等),结构性封装性好,重用性高。这需要时间和很多的训练。
2、在抓取网页的html源码后,快速找到自己想要的目标,准确的写出它的正则表达式。
3、得到目标内容后,准确进行一系列保存抓取等后续操作。
之前的帖子中介绍过url,也就是网址,在我们抓取网页的过程中也要非常注意网页的各项细节,而且需要我们不断的积累经验,也需要细心的分析:
例:
http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1
这是我们要抓去的一个百度贴吧帖子的url,主要是后面的一小串看的不是很明白吧。下面带大家细细分析一下:
“http://“ 代表资源传输使用http协议。
"tieba.baidu.com” 这是百度贴吧的二级域名,指向贴吧的服务器。
"/p/3138733512" 这是服务器中的某一个资源,也就是我们目标帖子的定位符。
"?see_lz=1&pn=1" 前半部分指的是是否只看楼主,后半部分指的是pagenumber页数第一页。
这样我们就完整分析了一个网页,比如说很多人喜欢看斗鱼:https://www.douyu.com/directory/game/LOL,或者CSDN的编辑页面:http://write.blog.csdn.net/postedit?type=edit这个网页应该很好分析吧,以后抓取各种网页的时候都要学会怎样去分析一个网页的url,这样我们才能写出更加壮硕的小爬虫~~。
我们可以将一个网页拆分成基础部分和参数部分,比如:
http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1
我们将它拆为基础部分:http://tieiba.baidu.com/p/3138733512,参数部分为:?see_lz=1&pn=1
这样做的目的使我们可以在一定程度上控制想要访问的页面,通过修改参数就可以达到我们想要的目的。
下面我们来实现第一条:
在Python中我们也可以运用面向对象的方法,定义一个百度贴吧BDTB的类,其中包含初始化函数和各种不同的抓取函数,相当于用这个类给我们的小爬虫做一身好看的衣服。在写任何爬虫的Python程序中,都可以用这种用类包装的方法,相当于结构化了自己的程序,也使用了面向对象的方法。
我们就先来看看完整的程序:
#coding=utf-8import urllib2import reimport sysreload(sys)sys.setdefaultencoding("utf-8")# 处理页面标签类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,lz): self.Baseurl = baseurl #基础部分 self.Lz = '?see_lz='+str(lz) #是否只看楼主参数部分 self.tool = Tool() #标签处理类 self.file = None self.floor = 1#######需要设置为全局变量,在写入文件时用来分割楼层。 def Getpage(self,pagenum): #函数用来生成完整url,并抓取网页返回 self.Pagenum = '&pn=' + str(pagenum) url = self.Baseurl+self.Lz+self.Pagenum #生成完整url request = urllib2.Request(url) Mypage = urllib2.urlopen(request) #使用request,获取网页内容 return Mypage.read().decode('utf-8') #将编码方式解码为utf-8 def Getpagenum(self,page): #函数用来抓取网页中包含的帖子最大页数 pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>', re.S) pagenum = re.search(pattern, page) #使用search()方法,抓取目标内容 if pagenum: return pagenum.group(1).strip() #如果包含的话就返回页数(字符串形式) else: return None def Gettitle(self,page): #函数用来抓取网页中包含的帖子的标题,用来命名txt文件 pattern = re.compile('<h3 class="core_title_txt.*?>(.*?)</h3>',re.S) result = re.search(pattern,page) if result: return result.group(1).strip()#注意search()和findall()的区别注意 else: return None#多次出现的格式,但是我们只需要一个,并且不需要保存成list格式,可以使用search()返回一个字符串即可 def Getimg(self,page): #函数用来抓取网页中包含的帖子中的目标图片链接 result_img = re.findall('<img class="BDE_Image" src="(.*?)" pic_ext=.*?>', page, re.S) #只要是符合正则表达式的所有图片都需要保存 result_imgs = [] for item in result_img: content = "\n" + self.tool.replace(item) + "\n" result_imgs.append(content.encode('utf-8')) #换行处理更加清晰 return result_imgs def Getcontent(self,page): #函数用来抓取网页中包含的帖子中的目标楼层文字内容 items = re.findall('<div id="post_content_.*?>(.*?)</div>',page, re.S) contents = [] for item in items : content = "\n" + self.tool.replace(item) + "\n" #这里使用到了便签的处理类 contents.append(content.encode('utf-8')) #换行处理更加清晰 return contents def setfile(self,title): #函数用来打开创建一个txt文件用来存储 if title is not None: self.file = open(title + ".txt", "w+") #没有这个文件的话会话就自己创建 else: self.file = open(u"百度贴吧" + ".txt","w+") def savefile(self,items): #函数用来保存目标楼层文字内容 for item in items: floorline = str(self.floor) + u'楼' + '——————————————————————————————' self.file.write(floorline) self.file.write(item) self.floor += 1 def saveimg(self,result_img): #函数用来保存目标楼层的目标图片链接 for img in result_img: self.file.write(img) def start(self): #开始函数,用来再次包装所有的函数以达到将内容写进txt文件的目的 page1 = self.Getpage(1) pagenum = self.Getpagenum(page1) title = self.Gettitle(page1) self.setfile(title) if pagenum == 0: print '该链接已失效或为无效链接' else: print '开始写入数据' for i in range(1,int(pagenum)+1): print '正在写入第'+str(i)+'页信息' page=self.Getpage(i) self.savefile(self.Getcontent(page)) self.saveimg(self.Getimg(page)) print '数据写入完毕'print u'请输入帖子链接'baseurl = str(raw_input())seeLZ = raw_input("是否只获取楼主发言,是输入1,否输入0\n")bdtb = BDTB(baseurl,seeLZ) #类的实例化bdtb.start()
咱们先不管一开始的Tool类,看下面的BDTB类,用来包装了我们所有的重要的函数。每个函数都有详细的介绍。
其中有几点需要着重注意的地方:
1、findall()和search()之间的区别和用法。
findall()函数:返回的是所有满足其正则表达式的内容,形式为列表。
search()函数:返回的是第一个满足条件的匹配,形式为字符串或者是元组。
这里我们就可以看出为什么程序中有的需要使用findall,有的需要使用search()。例如:
我们要的是一个最大的页数,但是玩过贴吧都知道在页首和页尾都会显示最大页数,我们用正则表达式就会匹配到两个地方的内容,如果我们使用findall()函数,返回的是[5,5],但是我们只需要一个简单的5,就够了,这就需要search()的使用了。
学会在什么情况下分别使用这两种函数也是比较重要的
2、result.group(1).strip()
这样的表达是为了得到我们的目标结果。
group(),返回的是匹配到的一个或者多个子组,如果只有一个参数,那么就是字符串。多个参数返回的就是元组。这里的group(1)指的就是第一个参数。
strip(),默认删除空白符(包括'\n', '\r', '\t', ' ')。
3、抓去了网页的html源码以后还是要仔细的观察、分析, 找到目标后,写出合适的正则表达式也是一个重点所在!~
再说我们多出的Tool类,这个类是对很多哦帖子分析得来的结果,你可以尝试单独输出一下没有经过标签处理类的楼层文字内容,会有很多想不到的标签出现,比如说空格、换行符、超链接等等,Tool类中给出了很多种情况。
在此我们又要讨论一种情况的发生,就是目标内容的内部包含了我们不想要的东西,比如说:“我喜欢<HVGgvjhhgu>Python”,我们想去除其中的<>内容,就需要用到re模块的sub()函数处理我们的目标内容。这也算是比较精髓的一种内容啦。
对于这个程序的改写,也非常容易,只要替换掉一些正则表达式即可。或者不想处理百度贴吧的帖子,想抓去淘宝链接的美图,也可以通过稍加修改来得到。
本例部分结果如下:
更新一下抓取其中的主要图片的例子,上一次我们已经得到了其中的图片链接,下面就是保存抓取了:
#coding=utf-8import urllib2import urllibimport reimport osimport sysreload(sys)sys.setdefaultencoding("utf-8")# 处理页面标签类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,lz): self.Baseurl = baseurl #基础部分 self.Lz = '?see_lz='+str(lz) #是否只看楼主参数部分 self.tool = Tool() #标签处理类 self.file = None self.floor = 1#######需要设置为全局变量,在写入文件时用来分割楼层。 self.name = 1 def Getpage(self,pagenum): #函数用来生成完整url,并抓取网页返回 self.Pagenum = '&pn=' + str(pagenum) url = self.Baseurl+self.Lz+self.Pagenum #生成完整url request = urllib2.Request(url) Mypage = urllib2.urlopen(request) #使用request,获取网页内容 return Mypage.read().decode('utf-8') #将编码方式解码为utf-8 def Getpagenum(self,page): #函数用来抓取网页中包含的帖子最大页数 pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>', re.S) pagenum = re.search(pattern, page) #使用search()方法,抓取目标内容 if pagenum: return pagenum.group(1).strip() #如果包含的话就返回页数(字符串形式) else: return None def Gettitle(self,page): #函数用来抓取网页中包含的帖子的标题,用来命名txt文件 pattern = re.compile('<h3 class="core_title_txt.*?>(.*?)</h3>',re.S) result = re.search(pattern,page) if result: return result.group(1).strip()#注意search()和findall()的区别注意 else: return None#多次出现的格式,但是我们只需要一个,并且不需要保存成list格式,可以使用search()返回一个字符串即可 def Getimg(self,page): #函数用来抓取网页中包含的帖子中的目标图片链接 result_img = re.findall('<img class="BDE_Image" src="(.*?)" pic_ext=.*?>', page, re.S) #只要是符合正则表达式的所有图片都需要保存 result_imgs = [] for item in result_img: content = "\n" + self.tool.replace(item) + "\n" result_imgs.append(content.encode('utf-8')) #换行处理更加清晰 return result_imgs def Getcontent(self,page): #函数用来抓取网页中包含的帖子中的目标楼层文字内容 items = re.findall('<div id="post_content_.*?>(.*?)</div>',page, re.S) contents = [] for item in items : content = "\n" + self.tool.replace(item) + "\n" contents.append(content.encode('utf-8')) #换行处理更加清晰 return contents def setfile(self,title): #函数用来打开创建一个txt文件用来存储 if title is not None: self.file = open(title + ".txt", "w+") #没有这个文件的话会话就自己创建 else: self.file = open(u"百度贴吧" + ".txt","w+") def savefile(self,items): #函数用来保存目标楼层文字内容 for item in items: floorline = str(self.floor) + u'楼' + '——————————————————————————————' self.file.write(floorline) self.file.write(item) self.floor += 1 def saveimg(self,result_img): #函数用来保存目标楼层的目标图片链接 for img in result_img: self.file.write(img) def saveImg(self, imageURL, fileName): u = urllib.urlopen(imageURL) data = u.read() f = open(fileName, 'wb') f.write(data) f.close() def saveImgs(self, images): # 保存多张写真图片 print u"发现共有", len(images), u"张照片" for imageURL in images: splitPath = imageURL.split('.') fTail = splitPath.pop() if len(fTail) > 3: fTail = "jpg" fileName = str(self.name) + "." + fTail self.saveImg(imageURL, fileName) self.name += 1 def mkdir(self, path):# 创建新目录 isExists = os.path.exists(path)# 判断结果 if not isExists: # 如果不存在则创建目录 print u"偷偷新建了名字叫做", path, u'的文件夹' # 创建目录操作函数 os.makedirs(path) return True else: # 如果目录存在则不创建,并提示目录已存在 print u"名为", path, '的文件夹已经创建成功' return False def start(self): #开始函数,用来再次包装所有的函数以达到将内容写进txt文件的目的 page1 = self.Getpage(1) pagenum = self.Getpagenum(page1) title = self.Gettitle(page1) self.setfile(title) if pagenum == 0: print '该链接已失效或为无效链接' else: print '开始写入数据' for i in range(1,int(pagenum)+1): print '正在写入第'+str(i)+'页信息' page = self.Getpage(i) self.mkdir(str(i)) self.savefile(self.Getcontent(page)) self.saveImgs(self.Getimg(page)) print '数据写入完毕'print u'请输入帖子链接'baseurl = str(raw_input())seeLZ = raw_input("是否只获取楼主发言,是输入1,否输入0\n")bdtb = BDTB(baseurl,seeLZ) #类的实例化bdtb.start()
- Python爬虫实战(1)——百度贴吧抓取帖子并保存内容和图片
- 【python爬虫】百度贴吧帖子图片批量保存爬虫
- python 抓取天涯帖子内容并保存
- Python爬虫实战:百度贴吧帖子
- python爬虫实战(1)抓取网页图片自动保存
- Python爬虫实战(2):百度贴吧帖子
- Python爬虫实战(2):百度贴吧帖子
- 【python 爬虫】百度贴吧帖子所有楼层图片爬虫
- Python爬虫实战(五) :下载百度贴吧帖子里的所有图片
- Python爬虫实战之爬取百度贴吧帖子
- python抓取百度贴吧帖子
- python爬虫(抓取百度图片)
- java爬虫实战(1):抓取信息门户网站中的图片及其他文件并保存至本地
- python网络爬虫系列(四) --- 批量抓取并保存图片
- 爬虫抓取百度贴吧帖子标题及作者
- python爬虫学习-爬取百度贴吧图片并保存
- Python爬虫实战二之爬取百度贴吧帖子
- Python爬虫实战二:下载百度贴吧帖子内的壁纸
- SQL代码-创建DeskInfo表
- 太阳能电池暗时特性
- java编程语言基础知识要点
- 快速入门:十分钟学会Python
- poj 1061 扩展GCD
- Python爬虫实战(1)——百度贴吧抓取帖子并保存内容和图片
- Android果冻效果滑动控件
- vue-ES2015:
- Linux bashrc和profile的用途和区别
- 根据textfield的状态设置按钮的状态
- 智能指针
- Android studio 获取SHA1
- 一个正则表达式引擎的设计和实施1-如何通过NFA识别字符串
- PIL安装指导(mac环境)