福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

今天給大家分享一個python福利,教大家用python爬取妹紙性感照,宅男們的福利哦。話不多說,快上車。

項目地址:
https://github.com/3inchtime/mmjpg_spider


福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

準備工作:

目標網址:https://www.mzitu.com/

我的開發環境為 Ubuntu 16.04 LTS 以及 Python3.6.4,理論上是兼容Windows和Mac OS的。

由於我們的爬蟲並不是很複雜,所以並不需要使用任何爬蟲框架,我們主要使用的是Requests庫

Requests庫十分強大,Requests 是使用 Apache2 Licensed 許可證的 基於Python開發的HTTP 庫,其在Python內置模塊的基礎上進行了高度的封裝,從而使得進行網絡請求時,使用Requests可以輕而易舉的完成瀏覽器可有的任何操作。

正式開始代碼時間

既然我們要基於面向對象的思想寫這個爬蟲,那麼我們就需要把這個爬蟲寫成一個類。

首先

  • pip install requests
  • pip install lxml
<code># -*- coding: utf-8 -*-
import requests
from lxml import etree

class Spider(object):
    def __init__(self, page_num):
        self.page_num = page_num
        self.page_urls = ['http://www.mmjpg.com/']
        self.girl_urls = []
        self.girl_name = ''
        self.pic_urls = []

    def get_girl_urls(self):
        pass

    def get_pic_urls(self):
        pass

    def download_pic(self):
        pass

if __name__ == '__main__':
    pass          
/<code>

我們使用lxml中的etree操作xpath,從而獲取我要想要爬取的字段。

想要實例化我們的爬蟲類,

page_num是必須輸入的參數,所以用戶必須在實例化這個類時輸入頁碼。

因為這個網站的第1頁與後面的網頁url沒有規律,而且用戶至少會爬取第1頁,所以我們先將首頁放入page_urls中,之後調用get_page_urls()獲取所有想要爬取的url。

  • 根據我們的爬蟲策略,先調用get_girl_urls()抓取所有妹子的url,
  • 將它們保存到girl_urls這個list中,
  • 之後將girl_urls傳遞給get_pic_urls(),獲取所有圖片的url保存到pic_urls,
  • 最後調用download_pic()下載圖片。

最後的main函數則用來讓用戶輸入想要爬取的頁碼以及實例化Spider類。

所以,首先我們先來抓取所有妹子的url

在get_page_urls()中我們首先判斷輸入的頁碼(這裡我們就不做輸入負數的判斷了),在獲取到page_num後拼接出所有要爬取的頁面url

<code>    def get_page_urls(self):
        if int(page_num) > 1:
            for n in range(2, int(page_num)+1):
                page_url = 'http://www.mmjpg.com/home/' + str(n)
                self.page_urls.append(page_url)
        elif int(page_num) == 1:
            pass
/<code>

這樣page_urls中就存放了我們所有要爬取的頁面url了。

打開首頁,調出開發者工具

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

我們定位到妹子的url,我們可以很容易的寫出妹子url的xpath。

我們使用Requests的get()方法,獲取到這一頁的HTML頁面,並調用etree.HTML()將html轉化成可以用xpath定位元素的格式。

妹子的url的xpath很容易就能標記出來,它是class="title"的span標籤下的a標籤的href屬性。

<code>    def get_girl_urls(self):
        for page_url in self.page_urls:
            html = requests.get(page_url).content
            selector = etree.HTML(html)
            self.girl_urls += (selector.xpath('//span[@class="title"]/a/@href'))
/<code>

這樣我們就獲取到了我們要抓取的所有頁面中的妹子url。

接下來我們就要獲取每張圖片的url了。
點進一個妹子的頁面,我們發現每一頁只有一張圖片,頁面的最下方有翻頁,全部圖片,下一張。

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

按照常規的方法,我們只要獲取一個妹子的圖片總數,一般這種圖片的url都是很有規律的,我們就可以拼接出所有圖片的url,因為一般的圖片鏈接都是有規律的。

調出開發者工具(注意力不要放在妹子上!!!)。

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

咦???這個圖片的url怎麼看起來很奇怪???往下點了幾頁,發現所有的url毫無規律,看來我們遇到了一個小小的麻煩,這樣我們就不能通過普通的url拼接來獲取圖片的鏈接了,那我們現在該怎麼辦呢?


我個人有兩種解決辦法:

  1. 一頁一頁的獲取圖片的url,我們可以獲取第一頁上的圖片之後,進入下一頁爬取下一頁的圖片,以此類推,直到爬下這個妹子所有的圖片。
  2. 我們發現每一頁都有一個按鈕 “全部圖片” ,我們點擊全部圖片之後,我們就看到了所有圖片的url。
福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

這樣我們就得到了所有圖片的url。

那麼問題來了,爬蟲本身並不具備點擊這個按鈕的功能,但是要想獲取所有圖片的url,我們必須點擊這個按鈕之後才能得到,怎麼辦呢?

這時,我就要向你們介紹一大神器——Selenium

Selenium 是為了測試而出生的. 但是沒想到到了爬蟲的年代, 它搖身一變, 變成了爬蟲的好工具。
Seleninm: 它能控制你的瀏覽器, 有模有樣地學人類”看”網頁。

Selenium可以操縱瀏覽器瀏覽網頁,而且它可以點擊按鈕,甚至輸入文字,是不是很神奇?

Selenium可以操縱的瀏覽器有很多,我選擇了比較常用的Chrome。


<code>pip install selenium
/<code>

之後下載你需要操縱的瀏覽器對應的驅動。
https://www.seleniumhq.org/download/


福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

下載你需要的驅動,到你常用的目錄就可以啦!

下面我們使用Selenium來模擬點擊“全部圖片”的按鈕,來獲取所有圖片的url了。

<code>import time
from selenium import webdriver

    def get_pic_urls(self):
        driver = webdriver.Chrome('你的webdriver路徑')
        for girl_url in self.girl_urls:
            driver.get(girl_url)
            time.sleep(3)
            driver.find_element_by_xpath('//em[@class="ch all"]').click()
/<code>

這裡首先要實例化一個Chromedriver,webdriver的路徑一定要精確到可執行文件。

  • 我們先使用webdriver的get()方法請求到頁面。
  • 之後使用find_element_by_xpath()方法,用xpath標記到需要點擊的按鈕。
  • 之後調用click()方法點擊這個按鈕,這樣就完成了我們控制瀏覽器獲取所有圖片的過程啦!
  • 如果一切正常的話,這時當你運行程序時,會有一個受程序控制的瀏覽器打開,
  • 並在三分鐘後,“全部圖片”的按鈕會被點擊,你就看到了除第一張以外其他的圖片。
  • 這時我們調出開發者工具,我們就看到了所有圖片的url了。
福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

現在我們就可以獲得每張圖片的url了,順便獲取一下妹子的標題。

<code>    def get_pic_urls(self):
        driver = webdriver.Chrome('/home/chen/WorkSpace/tools/chromedriver')
        for girl_url in self.girl_urls:
            driver.get(girl_url)
            time.sleep(3)
            driver.find_element_by_xpath('//em[@class="ch all"]').click()
            time.sleep(3)
            # 這裡暫停3秒之後獲取html的源代碼
            html = driver.page_source
            selector = etree.HTML(html)
            self.girl_name = selector.xpath('//div[@class="article"]/h2/text()')[0]
            self.pic_urls = selector.xpath('//div[@id="content"]/img/@data-img')
/<code>

在使用Selenium模擬點擊之後,一定要使用time.sleep()暫停幾秒,我們需要給頁面中的JS代碼時間,加載出圖片的url,如果你不暫停幾秒的話,除非JS的加載速度能快過我們代碼的執行速度,不然我們是得不到圖片的url的。

在每次測試程序的時候總會有我們調用的Chrome出現,為了防止妹子干擾我們幹正事(咳咳咳......),我們可以使用不加載圖片的Chrome來爬取數據,當然了,我們也可以隱藏Chrome。

<code>chrome_options = webdriver.ChromeOptions()
prefs = {"profile.managed_default_content_settings.images": 2}
chrome_options.add_experimental_option("prefs", prefs)
chrome_options.add_argument("--headless")
chrome_path = '/home/chen/WorkSpace/tools/chromedriver'

    def get_pic_urls(self):
        driver = webdriver.Chrome(chrome_path, chrome_options=chrome_options)
                                     .
                                     .
                                     .
/<code>


--headless
是無界面Chrome的選項

prefs = {"
profile.managed_default_content_settings.images": 2}
是讓Chrome不加載圖片,因為我們並不需要讓圖片加載出來,我們只需要獲取url就足夠了,這樣可以減少響應的時間。

那麼,你肯定會問我,既然可以使用無界面的Chrome,為啥還需要添加不加載圖片的選項呢,實際上無界面的Chrome也是會加載圖片的,只是你看不到罷了。

這樣就再也不會有妹子來打擾我們擼代碼了......

緊接著就調用我們的下載圖片的方法。

<code>import os

PICTURES_PATH = os.path.join(os.getcwd(), 'pictures/')

    def download_pic(self):
        try:
            os.mkdir(PICTURES_PATH)
        except:
            pass
        girl_path = PICTURES_PATH + self.girl_name
        try:
            os.mkdir(girl_path)
        except Exception as e:
            print("{}已存在".format(self.girl_name))
        img_name = 0
        for pic_url in self.pic_urls:
            img_name += 1
            img_data = requests.get(pic_url)
            pic_path = girl_path + '/' + str(img_name)+'.jpg'
            if os.path.isfile(pic_path):
                print("{}第{}張已存在".format(self.girl_name, img_name))
                pass
            else:
                with open(pic_path, 'wb')as f:
                    f.write(img_data.content)
                    print("正在保存{}第{}張".format(self.girl_name, img_name))
                    f.close()
        return
/<code>

這裡先設置好圖片儲存的路徑,在當前目錄下的pictures中,當然了,為了體驗更友好,我也寫了判斷圖片和圖片文件夾是不是已經存在的方法,已經存在的圖片就會跳過,這樣我們就可以多次使用爬蟲而不怕已經存在文件而報錯了,最後使用os.write()方法就可以保存圖片了。

到這裡我們就下載到了圖片,趕快打開欣賞一番吧!!!


福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

WHAT THE FUCK !!??!!??

反爬蟲機制問題:

看來我們遇到了這個網站的反爬蟲,很多網站都可以識別爬蟲,尤其是我們這種很初級的爬蟲,一抓一個準。那我們怎麼辦才能避免被反爬蟲識別出來呢?

這其實是一門很深的學問,反爬蟲與反反爬蟲的鬥爭由來已久,但是這點問題還是難不倒我們的。

我之前說過,爬蟲就是模擬人瀏覽網頁來獲取數據,那麼既然不想被反爬蟲識別出來,那麼我們的爬蟲就要更像人一樣瀏覽網頁,那麼我們現階段究竟離模擬人瀏覽網頁差多遠呢???

反爬蟲解決方式:

“我們只需要把我們的爬蟲偽裝成瀏覽器就可以了”。

難道我們要使用Selenium控制Chrome來爬取網頁麼?

不,完全不需要!這就需要我們有些計算機網絡的基礎知識了,

我們瀏覽網頁,絕大多數情況下都是基於HTTP協議的,在HTTP協議中,我們每次瀏覽網頁都要發一個Headers請求頭,在請求頭中包含了很多重要的東西。

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

現在我們就來對比一下我們正常訪問該網站的妹子圖片和直接看妹子圖片的請求頭有什麼不同。

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

這是我們通過正常瀏覽網頁,第一張圖片的請求頭。現在我們把第一張圖片的url複製下來,重新打開瀏覽器,輸入url,看看會發生什麼?

相信你已經看到了,出現在我們眼前的就是剛才讓我們失望的那張圖片了,現在我們來看看它的請求頭是什麼?

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

在最下面一欄Request Headers中,我們發現返回失敗的那張圖片參數明顯要多出很多,但是這都不是關鍵,關鍵就在於它少了什麼?


我相信通過對比你已經發現了端倪,那就是Referer

Referer是用來判斷這個HTTP請求是從哪個網站跳轉過來的,當我們正常訪問這個網站時,HTTP請求中的Referer都是'
https://www.mzitu.com/201498,包含了這個Referer屬性的請求就可以正常瀏覽圖片,反之,當我們直接訪問這個圖片的鏈接,我們的HTTP請求中不包含Referer,那麼服務器就會判斷我們並不是在訪問他們的網站,就將我們識別為爬蟲了,從而返回了那張不是我們期望的那張圖片。

現在,既然我們已經分析出了原因,那麼我們要怎麼樣才能解決這個問題呢?

別擔心,Request庫早都為我們提供瞭解決辦法,當我們使用Requests時,不只能添加url進去同時也可以加入headers。

<code>headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/65.0.3325.181 Safari/537.36',
    'Referer': "http://www.mmjpg.com"
}
/<code>

我們只要在requests.get()中加入headers就可以了。

<code>img_data = requests.get(pic_url, headers=headers)
/<code>

現在,我們再次運行我們的爬蟲!!!

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

成功!!!

結論:

到了這裡,很多朋友會抱怨了,這樣爬取的效率太低,這個單進程的爬蟲一直在等待網絡的IO。

並不高效,其實這個解決辦法很簡單,

引入Python的協程就是很棒的解決辦法,具體的實現方法,待我有時間慢慢更新,盡情期待吧……


源碼::

福利(一):宅男福利&Python爬蟲輕鬆爬取「妹子性感美圖」

此次的爬蟲源碼已經給大家放在了百度雲,大傢俬信我,領取源碼即可:

私信小編:回覆 “學習資料” 即可領取資料

頭條怎麼私信

點擊頭條,點擊右下角“我的”

在個人界面點擊關注,如果你關注了,點擊“小杆貨”就可以了

私信回覆:“學習資料”就可以了 但是你要先關注我哦~





分享到:


相關文章: