python爬虫小项目:爬取百度贴吧图片

来源:互联网 发布:淘宝装修素材刚 编辑:程序博客网 时间:2024/04/30 03:00

前一段时间,通过对爬虫的学习,完成了爬取百度贴吧帖子和爬取糗事百科段子。看着自己爬取下来的文字,心里也是满满的自豪感!

再次打开贴吧,在浏览时发现贴吧也是包含着大量图片,既然爬取下了贴吧文字,那么为何不将百度贴吧的图片爬取下来呢?

说干就干!(代码为python3.x版


本篇目标

    1.对百度贴吧的任意帖子的图片部分进行抓取

    2.指定是否只抓取楼主发帖内容图片

    3.将抓取到的图片保存到程序目录下

要完成目标,我们先从小方面做起

一.先完成对指定页面图片的爬取

先爬取一个某个页面的图片,链接如下:

http://tieba.baidu.com/p/3223379048?see_lz=1

(1)获取页面代码

import urllib.requestimport redef getPage():        url='http://tieba.baidu.com/p/3223379048?see_lz=1'        request=urllib.request.Request(url)       #构建请求的request        response=urllib.request.urlopen(request)  #利用urlopen获取页面代码        return response.read().decode('utf-8')    #以utf-8编码对字符串进行解码,获得字符串类型对象print(getPage())


爬取下来的url页面源码过长就不放上来了。

(2)获取页面中想要的图片位置

把鼠标放在谷歌浏览器中想要爬取的内容上,点击右键,选择"检查",打开源码。大家也可以用其他方式查看。

可以写出正则表达式如下:(不明白的可以学习一下正则表达式)

'<img class="BDE_Image".*?src="(.*?)"'

发现存在一些帖子的源码不一样(加了一些前缀什么的),大家可以视情况自行更改正则表达式。

说明:

(1).*? 是一个固定的搭配,.和*代表可以匹配任意无限多个字符,加上?表示使用非贪婪模式进行匹配,也就是会尽可能短地做匹配。

(2)(.*?)代表一个分组,是我们需要的内容。

承接上面,代码如下:

def getIMG():        page=getPage()        pattern=re.compile('<img class="BDE_Image".*?src="(.*?)"',re.S)#用于将字符串形式的正则表达式编译为Pattern对象,re.S 标志代表在匹配时为点任意匹配模式        img=re.findall(pattern,pag       #re.findall可以获取字符串中所有匹配的字符串        return img<pre class="python" name="code">print(getIMG())

结果举例如下(并非全部匹配结果,结果太长委屈);

['http://imgsrc.baidu.com/forum/w%3D580/sign=1bc76f5e7bf0f736d8fe4c093a54b382/6700040828381f305987d488aa014c086e06f021.jpg', 'http://imgsrc.baidu.com/forum/w%3D580/sign=6f730e3661d0f703e6b295d438fa5148/a820daf9d72a6059c04db93b2b34349b033bba66.jpg', 'http://imgsrc.baidu.com/forum/w%3D580/sign=e586e6c5b01c8701d6b6b2ee177e9e6e/a0c13e6d55fbb2fb88e300334c4a20a44723dcf3.jpg', 'http://imgsrc.baidu.com/forum/w%3D580/sign=f15bb4a549ed2e73fce98624b701a16d/f1d5523d269759eefa4c3beab1fb43166d22dfa7.jpg']

(3).将获取的数据下载到本地:

def getIMG(x):        page=getPage()        pattern=re.compile('<img class="BDE_Image".*?src="(.*?)"',re.S)        img=re.findall(pattern,page)        for i in img:            urllib.request.urlretrieve(i,'%s.jpg' %x)            x+=1        return x

(1)使用了urlretrieve方法

urlretrieve表示:直接将远程数据下载到本地。

和urlopen不同的是

urllib.urlopen(url[, data[, proxies]]) 表示:创建一个表示远程url的类文件对象,然后像本地文件一样操作这个类文件对象来获取远程数据。

(2)通过一个for循环对获取的图片链接进行遍历,因为图片数量较多,x变量是我们在下载图片时的计数变量,同时也是命名变量,命名规则通过x变量加1。保存的位置默认为程序的存放目录

完整代码如下:

import urllib.requestimport redef getPage():        url='http://tieba.baidu.com/p/3223379048?see_lz=1'        request=urllib.request.Request(url)        response=urllib.request.urlopen(request)        return response.read().decode('utf-8')        #以utf-8编码对字符串进行解码,获得字符串类型对象def getIMG(x):        page=getPage()        pattern=re.compile('<img class="BDE_Image".*?src="(.*?)"',re.S)        img=re.findall(pattern,page)        for i in img:            urllib.request.urlretrieve(i,'%s.jpg' %x)            x+=1        return xprint(getIMG(1))


完成结果如下:

完成到这里,看着这么多图片被爬取成功,是不是很高兴?

可是我们只是完成了对指定页面的图片爬取,离我们的目标还差一些步骤。继续进行吧!

.对百度贴吧的任意帖子的图片部分进行抓取,指定是否只抓取楼主发帖内容图片

我们想先将以上代码优化一下,创建一个百度贴吧图片类,调用功能:

import urllib.requestimport reclass bdtbpicture:    def getPage(self,url):            request=urllib.request.Request(url)            response=urllib.request.urlopen(request)            return response.read().decode('utf-8')            #以utf-8编码对字符串进行解码,获得字符串类型对象    def getIMG(self,page,x):            pattern=re.compile('<img class="BDE_Image".*?src="(.*?)"',re.S)            img=re.findall(pattern,page)            for i in img:                urllib.request.urlretrieve(i,'%s.jpg' %x)                x+=1            return xurl='http://tieba.baidu.com/p/3223379048?see_lz=1'bdtb=bdtbpicture()bdtb.getIMG(bdtb.getPage(url),1)


观察贴吧URL,我们可以把URL分为两部分,一部分为基础部分,一部分为参数部分。

例如,上面的URL我们划分基础部分是 http://tieba.baidu.com/p/3223379048,参数部分为?see_lz=1  (&pn=1表示的是页数,可改变数字从而改变页数)

为了完成目标,我们要让用户输入帖子代号,自动获取获取帖子页数,改变?see_lz=1的值,来确定是否只爬取楼主发言图片。改变&pn=1的值,遍历帖子,而不是只爬取帖子的其中一页。

知道了方向,我们就可以动手了。


(1).建立初始化函数,改变getPage函数(获取页码参数,更改url形式)

def __init__(self,baseurl,seeLZ):        self.baseURL=baseurl        self.seeLZ='?see_lz='+str(seeLZ)

def getPage(self,pageNum):        url=self.baseURL+self.seeLZ+'&pn='+str(pageNum)        request=urllib.request.Request(url)        response=urllib.request.urlopen(request)

(2).获取帖子页数(更改正则表达式):

def getPageNum(self,page):        pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)        result = re.search(pattern,page)        if result:        #print result.group(1)  #测试输出            return result.group(1).strip()        else:            return None
(3).建立start函数进行整体调用
def start(self):        indexPage = self.getPage(1)        pageNum = self.getPageNum(indexPage)        if pageNum == None:            print("URL已失效,请重试")            return        print("该帖子共有" + str(pageNum) + "页")        x=1        for i in range(1,int(pageNum)+1):            print("正在写入第" + str(i) + "页数据")            page = self.getPage(i)            x=self.getIMG(page,x)        print(u"写入任务完成") 
写的有些凌乱,整体代码如下:
import urllib.requestimport reclass bdtbpicture:    def __init__(self,baseurl,seeLZ):        self.baseURL=baseurl        self.seeLZ='?see_lz='+str(seeLZ)    def getPage(self,pageNum):        url=self.baseURL+self.seeLZ+'&pn='+str(pageNum)        request=urllib.request.Request(url)        response=urllib.request.urlopen(request)        return response.read().decode('utf-8','ignore')    def getPageNum(self,page):        pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)        result = re.search(pattern,page)        if result:        #print result.group(1)  #测试输出            return result.group(1).strip()        else:            return None    def getIMG(self,page,x):        pattern=re.compile('<img class="BDE_Image".*?src="(.*?)"',re.S)        img=re.findall(pattern,page)        for i in img:            urllib.request.urlretrieve(i,'%s.jpg' %x)            x+=1        return x    def start(self):        indexPage = self.getPage(1)        pageNum = self.getPageNum(indexPage)        if pageNum == None:            print("URL已失效,请重试")            return        print("该帖子共有" + str(pageNum) + "页")        x=1        for i in range(1,int(pageNum)+1):            print("正在写入第" + str(i) + "页数据")            page = self.getPage(i)            x=self.getIMG(page,x)        print(u"写入任务完成")        print(u"请输入帖子代号")baseURL = 'http://tieba.baidu.com/p/' + str(input(u'http://tieba.baidu.com/p/'))seeLZ = input("是否只获取楼主发言图片,是输入1,否输入0\n")bdtb=bdtbpicture(baseURL,seeLZ)bdtb.start()

注意:

第12行getPage函数的return response.read().decode('utf-8','ignore')如果不加参数‘ignore’,可能会报如下错误,(初次运行还好,后面报错)

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xca in position 139369: invalid continuation byte

其中decode("utf-8", "ignore")表示:忽略其中有异常的编码,仅显示有效的编码

参考菜鸟教程

解释如下:

Python decode() 方法以 encoding 指定的编码格式解码字符串。默认编码为字符串编码。

decode()方法语法:

str.decode(encoding='UTF-8',errors='strict')
encoding -- 要使用的编码,如"UTF-8"。
errors -- 设置不同错误的处理方案。默认为 'strict',意为编码错误引起一个UnicodeError。 其他可能得值有 'ignore', 'replace', 'xmlcharrefreplace', 'backslashreplace' 以及通过 codecs.register_error() 注册的任何值。


参考豆瓣:对于可能存在中英文混合的字符串,在使用python的.decode('utf-8')时有时会出错,但是不decode的话,那么纯中文的字符串就会转码错误,因此就需要对纯中文的字符串进行.decode('utf-8').encode('gbk','ignore'),而对中英文混合的字符串由于在decode会引起异常且无需对其进行decode就能正常显示,那么保持原格式就好。

参考轻峰

成果如下:



好了,现在我们就可以完成对百度贴吧的任意帖子的图片部分进行抓取,指定是否只抓取楼主发帖内容图片,将抓取到的图片保存到程序目录下等功能了。

心动了吗?大家快点动手吧!





0 0