功能:爬取目標(biāo)網(wǎng)站全部主要圖片(例子中是美圖錄網(wǎng)站的全部寫真圖片语卤,按人名分類)
本示例使用Python3.5霎槐,需要額外安裝BeautifulSoup 4
BeautifulSoup 4 安裝方法:
Linux:
sudo apt-get install python-bs4
Mac:
sudo easy_install pip
pip install beautifulsoup4
Windows:
下載源碼后场勤,
python setup.py install
或者:
pip install beautifulsoup4
具體安裝方式見:<a >點(diǎn)這里</a>
分析網(wǎng)站結(jié)構(gòu)
目標(biāo)網(wǎng)站<a >“美圖錄”</a>(別問我為什么選這個(gè)網(wǎng)站爹橱。。糯崎。百度上“隨便”找的)
因?yàn)榇蛩阆螺d全部的網(wǎng)頁圖片拓哟,所以從最小的單元開始想许,也就是圖片集(再小就是單一的圖片了,也就可以直接下載了)
先打開首頁断序,隨便點(diǎn)開一個(gè)圖片集流纹,發(fā)現(xiàn)圖片集的地址是這樣的
http://www.meitulu.com/item/7487.html
在圖片集中檢查頁面元素,如下所示
<div class="content">
<center>
![[Ugirls尤果網(wǎng)] U181 陳雅漫 寫真套圖_第1頁/第1張圖](http://upload-images.jianshu.io/upload_images/2475481-8cdfce535296ab31.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</center>
<center>
![[Ugirls尤果網(wǎng)] U181 陳雅漫 寫真套圖_第1頁/第3張圖](http://upload-images.jianshu.io/upload_images/2475481-43e882deb2d0c0cf.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</center>
<center>
![[Ugirls尤果網(wǎng)] U181 陳雅漫 寫真套圖_第1頁/第4張圖](http://upload-images.jianshu.io/upload_images/2475481-2d5cb01257ad639e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</center>
<center>
![[Ugirls尤果網(wǎng)] U181 陳雅漫 寫真套圖_第1頁/第5張圖](http://upload-images.jianshu.io/upload_images/2475481-8e773c5ec770c466.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</center>
</div>
發(fā)現(xiàn)每一張主要圖片資源鏈接都在center標(biāo)簽中违诗,這樣就可以在這個(gè)頁面上提取圖片鏈接并下載了
繼續(xù)向下漱凝,發(fā)現(xiàn)下圖所示,圖片并不是存放在一個(gè)頁面中的
而檢查這里的html代碼可以看到
<div id="pages">
<a class="a1" >上一頁</a>
<span>1</span>
<a >2</a>
<a >4</a>
<a >5</a>
<a >6</a>
<a >7</a>
<a >8</a>
<a >9</a>
<a >10</a>
".."
<a >16</a>
<a class="a1" >下一頁</a>
</div>
這個(gè)頁面列表在class="pages"的div標(biāo)簽中较雕,當(dāng)前頁是用span標(biāo)簽裝飾的碉哑,我們可以通過提取 下一頁 按鈕的鏈接來繼續(xù)下載下一個(gè)頁面的圖片挚币,但是我們?cè)趺粗朗裁磿r(shí)候會(huì)到最后一頁呢?點(diǎn)擊最后一個(gè)頁面的按鈕扣典,這里就是16頁妆毕。再次檢查這一部分的html代碼
<div id="pages">
<a class="a1" >上一頁</a>
<a >1</a>
".."
<a >7</a>
<a >8</a>
<a >9</a>
<a >10</a>
<a >11</a>
<a >12</a>
<a >13</a>
<a >14</a>
<a >15</a>
<span>16</span>
<a class="a1" >下一頁</a>
</div>
從這段代碼中可以看到,下一頁 按鈕的鏈接指向的是16頁贮尖,也就是當(dāng)前頁笛粘,而前面的頁面指向的都是當(dāng)前頁的下一頁。所以我們可以利用這一點(diǎn)來判斷是否到最后一頁湿硝。這樣我們就有了下載一個(gè)完整圖片集的思路了薪前。
下面我們看看如何獲得所有圖片集的鏈接
發(fā)現(xiàn)網(wǎng)站首頁有一個(gè)圖集分類,我們可以認(rèn)為他把網(wǎng)站上所有的資源都放在這里分好類了关斜,隨便點(diǎn)開一個(gè)分類示括,可以看到里面有排列整齊的圖集,檢查html代碼
<div class="boxs">
<ul class="img">
<li>
<a target="_blank">
![[尤蜜薈] 可樂Vicky 蘇梅島旅拍 第二刊 ~[43]](http://upload-images.jianshu.io/upload_images/2475481-1915763ae66ee8ad.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</a>
<p><span>3</span>圖片: 43 張(1600X2400)</p>
<p>機(jī)構(gòu):
<a target="_blank" class="tags">推女神</a>
</p>
<p>模特:
<a target="_blank" class="tags">可樂Vicky</a>
</p>
<p>標(biāo)簽:
<a target="_blank" class="tags">極品</a>
<a target="_blank" class="tags">女神</a>
<a target="_blank" class="tags">清新</a>
<a target="_blank" class="tags">清純</a>
<a target="_blank" class="tags">唯美</a>
<a target="_blank" class="tags">戶外</a>
<a target="_blank" class="tags">養(yǎng)眼</a>
</p>
<p class="p_title">
<a target="_blank">[尤蜜薈] 可樂Vicky 蘇梅島旅拍 第二刊 ~</a>
</p>
</li>
"..."
<!--為了方便查看痢畜,這里省略了一堆li標(biāo)簽-->
</ul>
</div>
從這段代碼中可以發(fā)現(xiàn)垛膝,所有的圖集被放在了<div class="boxs">
標(biāo)簽中,每個(gè)圖集的信息中包含模特名字丁稀,發(fā)行機(jī)構(gòu)和一系列的標(biāo)簽吼拥。每一個(gè)信息對(duì)應(yīng)一個(gè)鏈接,鏈接中是包含對(duì)應(yīng)信息的圖集的分類頁面线衫,這里我們按照人名分類凿可,所以只要檢索 模特 關(guān)鍵字就可以了。
這段頁面的下方也是一個(gè)頁面列表授账,檢查html元素會(huì)發(fā)現(xiàn)與圖集的列表模式相同枯跑。
另外,對(duì)于某些只有一個(gè)圖集的人來說矗积,他沒有對(duì)應(yīng)的分類頁面全肮,對(duì)于這些人要另外處理
小結(jié)
根據(jù)這些特征敞咧,我們遍歷分類頁面中的所有圖集棘捣,通過字典記錄人名對(duì)應(yīng)的鏈接,如果遇到?jīng)]有分類頁面的人休建,則直接創(chuàng)建文件夾乍恐,下載圖集。這樣我們前期的分析工作就完成了测砂,下面
開始寫爬蟲吧
先初始化幾個(gè)要用到的全局變量
categaries = {} # 分類列表
person = {} # 人名列表
PATH = os.getcwd() # 根目錄路徑
forbidchar = r'<|>|/|\\|\||:|"|\*|\?' # 系統(tǒng)禁止用作文件名的字符茵烈,正則表達(dá)式
一、圖片下載函數(shù)
首先砌些,我們要下載網(wǎng)站上所有的圖片呜投,所以需要有一個(gè)給定圖片鏈接就能下載下來的函數(shù):
def downloadimg(link, name): # link為圖片鏈接加匈,name為圖片名字
data = urlopen(link, timeout=10) # 打開連接
tname = name+".jpg" # 給圖片命名
with open(tname, "ab") as code: # 以追加二進(jìn)制模式打開文件,并保存數(shù)據(jù)
code.write(data.read())
print(tname+" is done.") # 打印提示文字
但這還不夠仑荐,因?yàn)榻?jīng)常會(huì)碰到鏈接沒有響應(yīng)的情況雕拼,所以加上異常處理
def downloadimg(link, name):
name = re.split(forbidchar, name)
name = '.'.join(name) # 通過re模塊的split,將windows不支持的文件名符號(hào)粘招,全部換成'.'
for i in range(10):
time.sleep(0.5) # 照顧別人服務(wù)器的帶寬啥寇,適當(dāng)加點(diǎn)延時(shí)。洒扎。辑甜。加多少看你心情
try:
data = urlopen(link, timeout=10)
tname = name+".jpg"
with open(tname, "ab") as code:
code.write(data.read())
print(tname+" is done.")
break
except Exception:
time.sleep(3) # 多數(shù)情況下,上面的語句中只有urlopen會(huì)出現(xiàn)無響應(yīng)的異常袍冷,這時(shí)等待三秒磷醋,重新發(fā)送請(qǐng)求
二、圖集下載函數(shù)
更進(jìn)一步的胡诗,我們要處理一個(gè)給定鏈接的圖集子檀,首先我們寫一個(gè)下載當(dāng)前頁面的主要圖片的功能
def downloaditem(link, ):
html = urlopen(link, timeout=100) # 打開鏈接
bsObj = BeautifulSoup(html, "html.parser") # 用bs解析html
for center in bsObj.findAll("center"): # 找到所有的center標(biāo)簽
for img in center.findAll("img"): # 找到其中包含img標(biāo)簽的
boola = downloadimg(img.attrs['src'], img.attrs['alt'])
# 下載image,并以圖片的alt屬性內(nèi)容給圖片命名
但這還沒完乃戈,記得前面提到的頁面列表么褂痰,我們還要繼續(xù)下載 下一頁 的圖片。于是繼續(xù)
def downloaditem(link, ):
html = urlopen(link, timeout=100)
bsObj = BeautifulSoup(html, "html.parser")
for center in bsObj.findAll("center"):
for img in center.findAll("img"):
boola = downloadimg(img.attrs['src'], img.attrs['alt'])
#---------------------------------------------------------------------------
page = bsObj.find("div", {"id":"pages"}) # 找到所有id屬性為pages的div標(biāo)簽
for a in page.findAll("a", {"class":"a1"}):
# 找到其中class屬性為a1的a標(biāo)簽
if re.search(re.compile(r'下一頁'), a.getText()):
# 如果標(biāo)簽內(nèi)容包含下一頁
number = re.search(re.compile(r"http://www\.meitulu\.com/.*?_([0-9]*?)\.html"), a.attrs['href'])
#用正則表達(dá)式匹配鏈接中的頁碼
if number: #如果匹配成功症虑,失敗時(shí)number為None
link = number.group(0) #提取頁面鏈接
number = number.group(1) #提取頁碼
if number != page.find('span').getText():
#如果鏈接的頁碼跟當(dāng)前頁碼不同缩歪,則不是最后一頁,
print("download deeper...")#輸出提示信息
downloaditem(link) #繼續(xù)下載下一頁
完善一下代碼谍憔,添加異常捕捉和延時(shí)
def downloaditem(link, ):
for i in range(10):
time.sleep(1)
try:
html = urlopen(link, timeout=100)
break
except Exception:
print("Url Erroe")
time.sleep(2)
for i in range(10):
try:
bsObj = BeautifulSoup(html, "html.parser")
break
except Exception:
print("Soup Error")
for center in bsObj.findAll("center"):
for img in center.findAll("img"):
boola = downloadimg(img.attrs['src'], img.attrs['alt'])
time.sleep(2)
page = bsObj.find("div", {"id":"pages"})
for a in page.findAll("a", {"class":"a1"}):
if re.search(re.compile(r'下一頁'), a.getText()):
number = re.search(re.compile(r"http://www\.meitulu\.com/.*?_([0-9]*?)\.html"), a.attrs['href'])
if number:
link = number.group(0)
number = number.group(1)
if number != page.find('span').getText():
print("download deeper...")
downloaditem(link)
三匪蝙、獲取人名分類下的所有圖集鏈接
def downloadperson(link, name):
name = re.split(forbidchar, name)
name = '.'.join(name) # 跟圖片文件名原理一樣,替換被禁止的字符
personitems = {}
if not os.path.exists(name): # 檢查這個(gè)人的文件夾之前有沒有創(chuàng)建
os.mkdir(name) # 如果沒有就創(chuàng)建一個(gè)
os.chdir(name) # 進(jìn)入這個(gè)目錄
html = urlopen(link, timeout=100) # 打開鏈接
bsObj = BeautifulSoup(html, "html.parser") # 用bs解析
for boxs in bsObj.findAll("div", {"class":"boxs"}): # 找到裝載圖片集的<div>標(biāo)簽
for li in boxs.findAll("li"): # 處理每一個(gè)圖片集
for p in li.findAll('p', {"class":"p_title"}): # 找到包含圖片鏈接的p標(biāo)簽
psn = p.find('a')
personitems[psn.getText()] = psn.attrs['href'] # 用文件名作為key給字典添加圖集鏈接
PATHtmp = os.getcwd() # PATHtmp是這一層人名文件夾的路徑
for key in personitems: # 遍歷字典习贫,下載每一個(gè)圖集
print('\n', "downloading ", key, '\n')
if not os.path.exists(key): # 檢驗(yàn)文件夾是否存在
os.mkdir(key)
os.chdir(key) # 進(jìn)入文件夾
downloaditem(personitems[key]) # 下載圖集
os.chdir(PATHtmp) # 回到上一層目錄逛球,這里用的絕對(duì)路徑,避免中途被打斷導(dǎo)致后面的下載也出現(xiàn)錯(cuò)誤
os.chdir(PATH) # 回到根目錄
完善代碼苫昌,添加異常捕捉和延時(shí)颤绕,這里因?yàn)橥粋€(gè)人沒有發(fā)現(xiàn)有多頁的情況,所以沒有處理頁面列表的代碼
def downloadperson(link, name):
name = re.split(forbidchar, name)
name = '.'.join(name)
personitems = {}
if not os.path.exists(name):
os.mkdir(name)
os.chdir(name)
for i in range(10):
time.sleep(1)
try:
html = urlopen(link, timeout=100)
break
except Exception:
time.sleep(2)
print("Url Erroe")
for i in range(10):
try:
bsObj = BeautifulSoup(html, "html.parser")
break
except Exception:
print("Soup Error")
for boxs in bsObj.findAll("div", {"class":"boxs"}):
for li in boxs.findAll("li"):
try:
for p in li.findAll('p', {"class":"p_title"}):
print('\n',p,'\n')
psn = p.find('a')
personitems[psn.getText()] = psn.attrs['href']
except:
print("Find Error")
PATHtmp = os.getcwd()
for key in personitems:
print('\n', "downloading ", key, '\n')
if not os.path.exists(key):
os.mkdir(key)
os.chdir(key)
downloaditem(personitems[key])
os.chdir(PATHtmp)
os.chdir(PATH)
四祟身、獲得分類下所有人名的分類鏈接
def getperson(link,):
for i in range(10):
time.sleep(1)
try:
html = urlopen(link, timeout=100) # 打開連接
break
except Exception:
time.sleep(2)
print("Url Erroe")
for i in range(10):
try:
bsObj = BeautifulSoup(html, "html.parser") # bs解析
break
except Exception:
print("Soup Error")
for boxs in bsObj.findAll("div", {"class":"boxs"}): # 獲取分類下包含圖集的標(biāo)簽
for li in boxs.findAll("li"): # 逐個(gè)圖集處理
try:
for a in li.findAll('p'):
print(a.getText()) # 輸出圖集提示信息
name = re.search(re.compile(r'^模特:(.*?)$'), a.getText())
if name:
psn = a.find('a') # 嘗試查找人名分類頁面鏈接
person[psn.getText()] = psn.attrs['href']
except: # 如果找不到分類頁面奥务,則直接下載圖集
print("downloading item..."+name.group(1))
item = li.find('p', {"class":"p_title"}).find("a")
print(item.getText())
if not os.path.exists(name.group(1)):
os.mkdir(name.group(1)) # 創(chuàng)建人名文件夾
os.chdir(name.group(1)) # 進(jìn)入人名文件夾
print(name.group(1))
name = item.getText() # 提取圖集
name = re.split(forbidchar, name) # 處理圖集名(文件夾名)
name = '.'.join(name)
if not os.path.exists(name):
os.mkdir(name) # 創(chuàng)建圖集文件夾
os.chdir(name) # 進(jìn)入圖集文件夾
downloaditem(item.attrs['href']) # 下載圖集
os.chdir(PATH) # 回到根目錄
time.sleep(3) # 延時(shí)
page = bsObj.find("div", {"id":"pages"}) # 處理下一頁問題,原理同downloaditem函數(shù)
for a in page.findAll("a", {"class":"a1"}):
if re.search(re.compile(r'下一頁'), a.getText()):
number = re.search(re.compile(r"http://www\.meitulu\.com/t/.*?([0-9]*?)\.html"), a.attrs['href'])
link = number.group(0)
number = number.group(1)
if number != page.find('span').getText():
print("scrap deeper...")
getperson(link)
break
五袜硫、主函數(shù)
if __name__ == "__main__":
for i in range(10):
time.sleep(1)
try:
html = urlopen("http://www.meitulu.com", timeout=100) # 打開首頁鏈接
break
except Exception:
print("Url Erroe")
time.sleep(2)
for i in range(10):
try:
bsObj = BeautifulSoup(html, "html.parser") # bs解析
break
except Exception:
print("Soup Error")
for a in bsObj.find("li", {"id":"tag"}).find("ul", {"id":"tag_ul"}).findAll("a"):
categaries[a.getText()] = a.attrs['href'] # 獲取所有分類首頁的鏈接氯葬,以分類名為key
for key in categaries:
time.sleep(3)
print(i,"loading page..."+key)
getperson(categaries[key]) # 獲取每一個(gè)分類下的所有人名鏈接
for key in person:
downloadperson(person[key], key) # 下載每一個(gè)人名下的所有圖集
總結(jié)
完整代碼在這里:<a >simplespider.py</a>
我在代碼中延時(shí)加的比較多,所以運(yùn)行起來有些慢婉陷,但畢竟這只是個(gè)練習(xí)帚称,照顧一下別人服務(wù)器比較好= =官研。