天天看點

Python老司機手把手帶你寫爬蟲,整站下載下傳妹子圖,一次爽個夠!準備工作在寫代碼之前開始寫代碼最終完整代碼和運作效果

其實很多程式設計語言都可以做爬蟲,例如java、c#、php等等甚至excel都可以抓網頁的圖表,那麼為什麼我們要用Python呢?它簡單、便捷,而且有好多庫可以選擇,可以說python是寫爬蟲的首選了!

今天就來帶大家寫一個簡單而又完整的爬蟲,我們來抓取整站的圖檔的,并且儲存到電腦上!

準備工作

工具:Python3.6、pycharm

庫:requests、re、time、random、os

目标網站:妹子圖(具體url大家自己去代碼裡看。。。)

在寫代碼之前

在我們開始寫代碼之前,要先對網站進行分析,重點有這個幾個地方:

1、先判斷網頁是否靜态網頁,這個關系我們采用的爬蟲手段!

簡單的說,網頁中的内容,在網頁源代碼中都可以找到,那麼就可以斷定,這個網站是靜态的了;如果沒有找到,就需要去開發者工具中查找,看看是抓包呢還是分析js結構或者其他的方式。

2、看看網頁的結構,大緻清楚抓取目标資料,需要幾層循環,每次循環的方式,以及是否保證沒有遺漏!

3、根據網頁源代碼來決定采用的比對方式

一般來說,正規表達式是處理字元串最快的方式,但是在爬蟲中它的效率并不是很高,因為它需要周遊整個html來比對相關内容,如果網頁源代碼比較規整的話,建議采用bs4或者xpath等等解析網頁結構的方式比較好!

當然,今天我們是基礎向的爬蟲,就用正規表達式了,畢竟正則是必須掌握的内容!

那麼,具體怎麼寫爬蟲代碼呢~?簡單的舉例給大家說下:

如果是手工操作的話,大概是這個流程

打開首頁==>選擇一個分類==>選擇一個圖集==>依次選擇圖檔==>右鍵儲存==>重複以上儲存其他圖檔

那麼這個過程放到代碼中呢,它的結構大概是這樣:

通路首頁url==>找到并循環所有分類==>建立分類檔案夾==>通路分類url==>找到頁碼建構循環分類所有頁==>循環頁面所有圖集==>建立圖集檔案夾==>找到圖集内所有圖檔url==>儲存到對應檔案夾

好了,思路也有了,那就廢話不多說了,我們來寫代碼吧~!

開始寫代碼

首先是導入上述的各種庫,沒有的需要安裝一下!然後寫入以下幾行代碼擷取網頁源代碼看看是否有反爬:

import requests
import time
import random
import re
import os


url = 'http://www.meizitu.com/'
html = requests.get(url)
html.encoding = 'gb2312'
           

如果能順利列印出源代碼且和網頁右鍵檢視的源代碼一緻,那麼可以判定該網站基本沒有反爬了!

第16行代碼的含義是給html設定編碼格式。因為Python3預設是utf-8,如果網站不是這個編碼格式的話,會出現亂碼,是以我們直接指定一下。

接下來呢,就是找到所有分類的名字和url了,來看看網頁中和源代碼中,它的位置在哪

全部在a标簽的屬性中,那麼我們可以用一行代碼擷取了

infos = re.findall(r'a href="(http://www.meizitu.com/.*?html)" target="_blank" title="(.*?)" ',html.text)           

這裡用正則比對,2個括号中的内容就是我們需要的url和名字了,然後開始建構循環周遊所有的分類

上一步取出的infos是清單,而且每一個元素都是一個元組,格式為(url,名字),所有我們用2個元素去周遊infos,來擷取我們需要的内容,先列印下看看結果是否正确!

這裡先不建立檔案夾,先進行下一步,通路分類的url,然後開始建構分類中的頁碼吧!分析網頁發現,所有的頁碼都在下方,但是還是稍有不同:沒有目前頁、多了下一頁和末頁

由于存在圖集不足一頁的情況(上述源代碼就不會出現),是以我們這麼處理循環

19-21行擷取分類的源代碼,22行擷取所有頁碼的url,然後用set()函數去重,再建立一個空清單,将分類的url加進去,注意,元組是不能用append()方法添加到清單中的,是以要先将set元組轉化為清單然後分别重新拼接清單内所有的url,在将2個清單相加的方式合并為一個清單!這樣我們就得到了分類下所有翻頁頁面的url

循環所有的url,擷取所有圖集的url清單,27行沒有用encoding指定編碼是因為這裡我不需要取到中文的内容,是以簡寫了一下!終于該取圖檔了!

圖集的title和圖集内所有圖檔的url都取到了!其實到這裡就已經完成了爬蟲的80%了!剩下的20%就是儲存圖檔到本地,這裡就不多說了,給大家提供2個代碼片段,一個是建立檔案夾并判斷是否存在,一個是剔除字元串内不符合命名要求的字元

path = 'E://python/mn/meizitu/%s/'%sor#路徑
if os.path.exists(path):#判斷路徑及檔案夾是否存在,不存在即建立
    pass
else:
    os.mkdir(path)
           
def new_title(title):
    rstr = r"[\/\\\:\*\?\"\<\>\|]"  # '/ \ : * ? " < > |'
    new_title = re.sub(rstr, "_", title)  # 替換為下劃線
    return new_title           

最終完整代碼和運作效果

在請求中加入了時間子產品的暫停功能,不加入的話可能會被網頁拒絕通路!

在最後請求圖檔位址的時候,需要加入UA來告訴伺服器你是浏覽器而不是腳本,這個是最常用的反爬手段了

#author:雲飛
#QQ群542110741
import requests
import time
import random
import re
import os

def new_title(title):
    rstr = r"[\/\\\:\*\?\"\<\>\|]"  # '/ \ : * ? " < > |'
    new_title = re.sub(rstr, "_", title)  # 替換為下劃線
    return new_title

url = 'http://www.meizitu.com/'
html = requests.get(url)
html.encoding = 'gb2312'
infos = re.findall(r'a href="(http://www.meizitu.com/.*?html)"  target="_blank" title="(.*?)" ',html.text)
i = 1
for sor_url,sor in infos:
    sor = new_title(sor)
    path = 'E://python/mn/meizitu/%s/'%sor#路徑
    if os.path.exists(path):#判斷路徑及檔案夾是否存在,不存在即建立
        pass
    else:
        os.mkdir(path)
    time.sleep(random.random())
    sor_html = requests.get(sor_url)
    sor_html.encoding = 'gb2312'
    atlas = set(re.findall(r"<li><a href='(.*?html)'>\d+</a></li>",sor_html.text))
    atlas_lis = []
    atlas_lis.append(sor_url)
    atlas_lis += [url+'a/'+x for x in list(atlas)]
    for atla in atlas_lis:
        atla_html = requests.get(atla).text
        at_url_lis = re.findall(r'h3 class="tit"><a href="(http://www.meizitu.com/.*?html)"  targe',atla_html)
        for at_url in at_url_lis:
            at_html = requests.get(at_url)
            at_html.encoding = "gb2312"
            atlas_title = ''.join(re.findall(r'<title>(.*?)</title>',at_html.text))
            atlas_title = new_title(atlas_title)
            img_path = 'E://python/mn/meizitu/%s/%s/'%(sor,atlas_title)
            if os.path.exists(img_path):#判斷路徑及檔案夾是否存在,不存在即建立
                pass
            else:
                os.mkdir(img_path)
            img_urls = re.findall(r'src="(http://mm.chinasareview.com/.*?jpg)" /><br />',at_html.text)
            k = 1
            for img_url in img_urls:
                header = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0'}
                data = requests.get(img_url,headers=header).content#擷取圖檔的二進制格式
                with open('%s%s'%(img_path,img_url.split('/')[-1]),'wb') as f:
                    f.write(data)
                print("【正在下載下傳】 {%s}的第%d張圖檔,一共下載下傳了%d張圖檔"%(atlas_title,k,i))
                i += 1
                k += 1
           

下載下傳一段時間後的效果