您的位置:首页 > 编程语言 > Python开发

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

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

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

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

本篇目标

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

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

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

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

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

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

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


(1)获取页面代码

import urllib.request
import re

def 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.request
import re

def 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 x
print(getIMG(1))


完成结果如下:


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

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

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

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

import urllib.request
import re
class 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 x
url='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.request
import re

class 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就能正常显示,那么保持原格式就好。

参考轻峰

成果如下:




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

心动了吗?大家快点动手吧!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 爬虫 图片 百度