前言
還在去網站上找網絡美女看嘛,卻發現網站上找到的並不是特別好看,而且比較模糊,下面教你們如何用爬蟲去爬取網絡美女圖片。
一、準備工作
安裝requests庫(用於請求靜態頁面)
pip install requests -i https://mirrors.ustc.edu.cn/pypi/web/simple
安裝lxml庫(用於解析html文件)
pip install lxml -i https://mirrors.ustc.edu.cn/pypi/web/simple
安裝與配置selenium(用於請求動態頁面)
pip install selenium -i https://mirrors.ustc.edu.cn/pypi/web/simple
selenium 需要與瀏覽器配合使用,在以前的爬蟲教程中,往往把 selenium 和 PhantomJS 配合使用,PhantomJS 是一款無界面的瀏覽器,能否執行js腳本,從而可以實現加載和渲染動態頁面。但是由於最新版 selenium 已經不再支持 PhantomJS,所以必須使用其他瀏覽器代替,本教程使用的是Firefox瀏覽器,Firefox瀏覽器也支持無界面模式( headless模式 ),用於爬蟲非常方便。另外Chrome也是支持的。詳細的安裝與配置教程請參考:
Python爬蟲利器五之Selenium的用法
二、頁面分析
打開妹子圖頁面,如下圖。頁面的正文部分列出了33個美女圖集。每個頁面共有33個圖集,總共有33個頁面,所以總共有1089個圖集。圖片的鏈接就包含在每個圖集中。
在頁面上單擊右鍵,選擇 審查元素按鍵,或直接按 F12進入調試模式。頁面結構如下:
點進任意一個圖集中,進入調試模式,可以看出頁面結構如下:
源碼詳解
從前面的頁面分析中可以看出,這個網站的頁面結構其實很簡單,我們只需要依次打開第1到第33個頁面,得到每個頁面的html源碼,然後從html源碼中提取出每個頁面上包含的圖集的鏈接列表(共33個),然後再依次打開第1到第33個圖集詳情頁面,但是圖集詳情頁面中的圖片列表是用js動態加載的,我們需要使用selenium加載這個動態頁面,等該頁面加載完畢之後再得到該頁面的html源碼,然後從圖集詳情頁面中提取出每張圖片的鏈接,最後再依次下載每張圖片保存即可。
整個流程其實並不複雜。
使用requests下載靜態html頁面
該函數用於下載圖集列表頁面,這個頁面是靜態的,可以直接通過 requests.get(url) 函數抓取。但是有一點需要注意,爲了把我們的爬蟲僞裝成正常的瀏覽器請求,避免我們的爬蟲被服務器禁止,我們需要給 requests 添加http請求頭,其中包含僞造的 User-Agent 瀏覽器標識
如果你對python感興趣,我這有個學習Python基地,裏面有很多學習資料,感興趣的+Q羣:688244617
def download_page_html(url):
phtml = None
try:
requests_header["User-Agent"] = random.choice(user_agent_list) # 選擇一個隨機的User-Agent
# print(requests_header)
page = requests.get(url=url, headers=requests_header) # 請求指定的頁面
# print(page.encoding)
page.encoding = "gb2312" # 轉換頁面的編碼爲gb2312(避免中文亂碼)
phtml = page.text # 提取請求結果中包含的html文本
# print("requests success")
page.close() # 關閉requests請求
except requests.exceptions.RequestException as e:
print("requests error:", e)
phtml = None
finally:
return phtml
下載指定鏈接的圖片文件
該函數用於提取出圖片鏈接之後下載圖片,圖片保存在以圖集標題命名的文件夾中
def download_picture(url, dir):
try:
picdir = "{0}/{1}".format(PICTURE_PATH, dir) # 構造圖片保存路徑
# print(picdir)
if os.path.exists(picdir) != True:
os.makedirs(picdir) # 如果指定的文件夾不存在就遞歸創建
pic_name = url.split("/")[-1] # 用圖片鏈接中最後一個/後面的部分作爲保存的圖片名
pic_full_name = "{0}/{1}".format(picdir, pic_name)
# print("save picture to :", pic_full_name)
requests_header["User-Agent"] = random.choice(user_agent_list) # 選擇一個隨機的User-Agent
response = requests.get(url, headers=requests_header) # 獲取的文本實際上是圖片的二進制文本
imgdata = response.content # 將他拷貝到本地文件 w 寫 b 二進制 wb代表寫入二進制文本
if len(imgdata) > (5*1024): # 只保存大於5k的圖片
with open(pic_full_name, 'wb') as f:
f.write(imgdata) # 把圖片數據寫入文件。with語句會自動關閉f
print("save picture to :", pic_full_name)
else:
print("picture size too small")
response.close()
except:
print("download piccture {0} error".format(url))
獲取所有需要爬取的頁面數
我們剛纔已經分析過了,實際上只有33個需要處理的頁面,但是以後網站肯定會更新,爲了兼容性,我們並不準備寫死,而是從html頁面中提並分析出待處理的頁面數。在選擇頁面底部的導航欄中有一個 末頁 按鈕,其href屬性中包含了一個數字,這個數字就是頁面的數目(也是頁面的序號)。
def get_page_list_num(tree):
page_list_num = 0
try:
gotolast = tree.xpath("/html/body/div[1]/div[2]/div[2]/div[1]/div[2]/div/ul/li[18]/a/@href")[0] # sexy_33.html
# print(gotolast)
gotolast = str(gotolast)
# print(gotolast)
gotolast = re.sub(r"\D", "", gotolast) # 把非數字字符串替換爲空
page_list_num = int(gotolast) # 轉化爲整數
print("max_page_number:", page_list_num)
except:
print("get page number error")
page_list_num = 0
finally:
return page_list_num
xpath選擇器
其實要在複雜的html文件中定位出我們需要的元素並不容易,但是一般瀏覽器都爲我們提供了這一功能,比如我所使用的firefox瀏覽器就自帶了這一功能,選中所需要的元素,然後在右鍵複製菜單中可以看到 複製Xpath的選項,使用它可以幫助我們快速定位我們需要的元素。下文所有的xpath都是使用這種方式得到的。如下圖:
獲取頁面中的圖片集列表
def get_pagealbum_list(tree): # 獲取頁面中的圖片集列表
pagealbum_list = []
pagealbum_list = tree.xpath(
"/html/body/div[1]/div[2]/div[2]/div[1]/div[1]/ul/li/div/div/a/@href")
return pagealbum_list
獲取圖集的標題和圖集中的圖片鏈接
這裏同樣使用的firefox的xpath工具定位到我們需要的元素的,然後通過lxml庫的xpath方法從html頁面中提取出我們想要的內容
def get_albumphoto_list(tree): # 獲取圖集中的圖片列表
albumphoto_list = []
albumphoto_list = tree.xpath(
"/html/body/div[2]/div[2]/div[2]/div[1]/div[2]/div[1]/p/img/@src")
return albumphoto_list
def get_albumphoto_title(tree): # 獲取圖集中的標題
albumphoto_title = None
albumphoto_title = tree.xpath(
"/html/body/div[2]/div[2]/div[2]/div[1]/div[1]/div[1]/h2/a/text()")[0]
# print(albumphoto_title)
return albumphoto_title
加載和渲染動態網頁
由於圖集詳情頁中的圖片列表是通過js代碼動態渲染得到的,所以如果直接通過requests請求這個頁面會得到一個只包含js代碼的html,裏面並沒有我們需要的圖片鏈接,因爲requests請求不能執行js代碼,所以沒法加載出圖片。我們這裏需要使用selenium配合firefox瀏覽器來加載和渲染這個動態頁面,等待渲染完成後再獲取html源碼,這時html中包含了圖片鏈接
def web_driver_init():
global web_driver
if web_driver == None:
options = Options()
options.add_argument('-headless') # 無頭參數
# 配了環境變量第一個參數就可以省了,不然傳絕對路徑
web_driver = Firefox(firefox_options=options)
web_driver.implicitly_wait(20)
def web_driver_page(url):
htmlpage = None
try:
web_driver.get(url)
htmlpage = web_driver.page_source
except:
print("get webpage {0} error".format(url))
finally:
return htmlpage
def web_driver_exit():
if web_driver != None:
web_driver.close()
爬蟲處理邏輯meizitu_webspider
def meizitu_webspider():
global page_list_num
global page_list_idx
global requests_url
global webspider_sleep
print("requests_url :", requests_url)
page_html_list = download_page_html(requests_url) # 下載當前頁面
if(page_html_list != None):
# print(page_html_list)
tree = lxml.html.fromstring(page_html_list)
if page_list_num == 0: # 計算當前共有多少個頁面需要處理
page_list_num = get_page_list_num(tree)
pagealbum_list = get_pagealbum_list(tree) # 獲取當前頁面中圖集列表
# print(pagealbum_list)
for lst in pagealbum_list: # 以此遍歷當前頁面中的每個圖集
print("album_list:", lst)
# 圖集使用的js異步加載圖片,所以這裏要用selenium加載動態頁面
albumphoto_page = web_driver_page(lst)
# print(albumphoto_page)
if albumphoto_page != None:
tree0 = lxml.html.fromstring(albumphoto_page)
albumphoto_title = get_albumphoto_title(tree0) # 獲取圖集標題
print("Title:", albumphoto_title)
albumphoto_list = get_albumphoto_list(tree0) # 獲取圖集中的圖片列表
for plst in albumphoto_list:
print("imgsrc:", plst)
download_picture(plst, albumphoto_title) # 下載圖片
webspider_sleep = random.randint(1, 5) # 延時一個隨機值,避免被服務器反爬
print("waiting {0} seconds".format(webspider_sleep))
time.sleep(webspider_sleep)
if page_list_idx < page_list_num: # 遞歸處理下一個頁面
page_list_idx = page_list_idx + 1
requests_url = REQUEST_URL1.format(page_list_idx)
meizitu_webspider()
else:
return 0
運行爬蟲
if __name__ == "__main__":
web_driver_init() # 初始化selenium
meizitu_webspider()
web_driver_exit() # 退出selenium
程序運行截圖
項目文件截圖