題目:Python爬取表情包下載到本地
上面是鬥圖啦最新鬥圖的圖片,我們需要的是獲取每張鬥圖的鏈接,這裏有兩個鏈接需要區分以下:
src ="" 是加載後顯示圖的鏈接,是一個動態的
data-original = ""是原始的圖的鏈接,不會改變的。所以我們選取該鏈接爬取
需要掌握的模塊:
1 Beautiful Soup
是用Python寫的一個HTML/XML的解析器,它可以很好的處理不規範標記並生成剖析樹(parse tree)。 它提供簡單又常用的導航(navigating),搜索以及修改剖析樹的操作。它可以大大節省你的編程時間。
1.1 基本用途
- 將html文檔解析成文檔樹,返回bs對象: BeautifulSoup(res.text,'lxml')
- 通過get_text()函數返回文檔除鏈接、標籤、段落外的文本內容
- find函數和findAll函數通過標籤和屬性過濾html頁面,標籤可以多個,屬性是字典類型,自然可以多值,BeautifulSoup(res.text,'lxml').find_all('img',attrs={'class':'img-responsive lazy image_dta'}),該方法獲取到的是一個列表。
- 3中的兩個函數當通過keyword參數過濾時,如果key爲class,則需寫爲class_=“green”
- bs對象可以直接調用子標籤來返回,但這種方式靈活性不大,當頁面結構發生些許改變後,可能會導致爬蟲程序不能正確返回結果。
- 子代標籤就是父標籤的下一級,而後代標籤是父標籤下所有級別的標籤
- 可以處理兄弟標籤,向前處理或者向後處理,返回的列表不包括自身對象
- 爲了讓爬蟲更穩定,最好還是讓標籤的選擇更加的具體,意思就是儘可能多的指定屬性
- 可以處理父標籤
- 大多數支持字符串參數的函數,都可以使用正則表達式來實現
- 可以通過myTag.attrs返回所有屬性,爲字典類型
- BeautifulSoup允許把特定函數類型當做findAll函數的參數,如使用lambda表達式。唯一的限制就是這些函數必須把一個標籤當做參數且返回結果是布爾類型。BeautifulSoup用這個函數來評估它遇到的每個標籤對象,最後把評估結果爲真的標籤保留,把其他標籤刪除。
urlretrieve(url, filename=None, reporthook=None, data=None)函數
- 參數 finename 指定了保存本地路徑(如果參數未指定,urllib會生成一個臨時文件保存數據。)
- 參數 reporthook 是一個回調函數,當連接上服務器、以及相應的數據塊傳輸完畢時會觸發該回調,我們可以利用這個回調函數來顯示當前的下載進度。
- 參數 data 指 post 到服務器的數據,該方法返回一個包含兩個元素的(filename, headers)元組,filename 表示保存到本地的路徑,header 表示服務器的響應頭。
單線程:代碼實現
import urllib
from bs4 import BeautifulSoup
import requests
from lxml import etree
import os
'''獲取所有頁面的URL'''
# BASE_PAGE_URL = "http://www.doutula.com/photo/list/?page="
# PAGE_URL_LIST = []
# for i in range(1,30):
# url = BASE_PAGE_URL + str(i)
# print (url)
'''
如何讓獲取任一個頁面下面的圖:通過requests.request來獲取網頁的html代碼,
然後找到代碼對應的src,就是每個圖片對應的url
'''
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
url = "http://www.doutula.com/photo/list/"
def save_img(url):
filename = url.split('/')[-1]
path = os.path.join('image',filename)
img_data = urllib.request.urlretrieve(url_img,filename = path)
with requests.request('GET',url,headers = {'User-agent':ua}) as res:
content = res.text
soup = BeautifulSoup(content,'lxml')
image_list = soup.find_all('img',attrs={'class':'img-responsive lazy image_dta'})
for img in image_list:
url_img = img['data-original']
save_img(url_img)
# http = etree.HTML(content)
# res = http.xpath("//div[@class='page-content text-center']//@image")
# print(res)
多線程:代碼實現
這兩個黃色對於每個線程來說是全局變量。多個線程對這兩個黃色區域進行多次I/O操作,所以這兩個全局變量需要加鎖和釋放鎖。
其次需要注意的是:
1 由於線程是同時進行的,也不知道具體那個快那個慢,什麼時候遍歷了哪幾個鏈接。這裏只能用while循環,不能用for in range的循環。
2 consumer和producer都不需要傳入參數
3 每次加鎖,操作結束後,一定要釋放鎖,不然程序會停在那裏,不再執行下去
import urllib
from bs4 import BeautifulSoup
import requests
from lxml import etree
import threading
import os
'''獲取所有頁面的URL'''
BASE_PAGE_URL = "http://www.doutula.com/photo/list/?page="
PAGE_URL_LIST = []
for i in range(1,30):
url_page = BASE_PAGE_URL + str(i)
PAGE_URL_LIST.append(url_page)
IMG_URL = []
'''
如何獲取任一個頁面下面的圖:通過requests.request來獲取網頁的html代碼,
然後找到代碼對應的src,就是每個圖片對應的url
'''
gLock = threading.Lock()
def producer():
while True:
gLock.acquire()
global PAGE_URL_LIST
if len(PAGE_URL_LIST) ==0:
gLock.release()
break
else:
page_url = PAGE_URL_LIST.pop()
gLock.release()
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
with requests.request('GET',page_url,headers = {'User-agent':ua}) as res:
content = res.text
soup = BeautifulSoup(content,'lxml')
image_list = soup.find_all('img',attrs={'class':'img-responsive lazy image_dta'})
gLock.acquire()
for img in image_list:
url_img = img['data-original']
IMG_URL.append(url_img)
gLock.release()
def consumer():
while True:
gLock.acquire()
global IMG_URL
if len(IMG_URL)==0:
gLock.release()
continue
else:
face_url = IMG_URL.pop()
gLock.release()
filename = face_url.split('/')[-1]
path = os.path.join('image',filename)
img_data = urllib.request.urlretrieve(face_url,filename = path)
def main():
'''全局變量是每個圖片對應的URL地址'''
###建立3個線程爬取圖片的URL地址(生產者模式)
for x in range(3):
th1 = threading.Thread(target=producer)
th1.start()
###建立5個線程保存爬取到URL地址對應的圖片(消費者模式)
for y in range(5):
th2 = threading.Thread(target=consumer)
th2.start()
if __name__ == "__main__":
main()