1. 程式人生 > >《零基礎入門學習Python》第056講:論一隻爬蟲的自我修養4:網路爬圖

《零基礎入門學習Python》第056講:論一隻爬蟲的自我修養4:網路爬圖

今天我們結合前面學習的知識,進行一個例項,從網路上下載圖片,話說我們平時閒來無事會上煎蛋網看看新鮮事,那麼,熟悉煎蛋網的朋友一定知道,這裡有一個 隨手拍 的欄目,我們今天就來寫一個爬蟲,自動抓取每天更新的 隨手拍。

要寫爬蟲,首先要做的第一件事就是踩點,主動發現網頁之間的規律,還有圖片連結之間有什麼規律,例如說,該網站的連結形式為:http://jandan.net/ooxx/page-‘頁碼數’#comments,(頁碼數應該小於等於當天的頁碼數(即目前最大頁碼數)),

1.那我們怎樣獲取目前最大的頁碼數呢(最新頁碼),我們在頁碼[77]這個位置點選右鍵,審查元素,看到了:<span class="current-comment-page">[77]</span>

我們完全可以通過搜尋 current-comment-page 在後面偏移 3 位就可以得到 77 這個最新的頁面,因為你不能去輸入一個具體的數字,因為這裡的數字每天都會改變。

2.我們在圖片的位置點選右鍵,審查元素,發現了圖片的地址,都是來自於新浪,然後都在 img 標籤裡,我們就可以使用 img src 作為關鍵詞來進行查詢,搜尋到了圖片的地址就可以參照我們之前下載一隻貓的例子了。把下面圖片的地址用 urlopen() 開啟,然後將其 save 到一個檔案裡去(二進位制),就可以了。

<img src="http://ww3.sinaimg.cn/mw600/006XNEY7gy1fy62ba9d6cj30u00u0x6p.jpg" style="max-width: 480px; max-height: 750px;">

我們弄清楚了以上幾點,就可以開始寫我們的爬蟲程式啦.....

(我們抓取前10頁的圖片,儲存到指定的本地資料夾中)

下面是老師講的程式碼:

#從煎蛋網的隨手拍欄目下載圖片
import urllib.request
import os
import random

def url_open(url):
        req = urllib.request.Request(url)
        req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36')
        
        #使用代理(就加入下面五行)
        #proxies = ['119.6.144.70:81', '111.1.36.9:80', '203.144.144.162:8080']
        #proxy = random.choice(proxies)

        #proxy_support = urllib.request.ProxyHandler({'http':proxy})
        #opener = urllib.request.build_opener(proxy_support)
        #urllib.request.install_opener(opener)

        response = urllib.request.urlopen(url)
        html = response.read()
        return html    

def get_page(url): #得到最新頁面的頁碼數
        html = url_open(url)
        html = html.decode('utf-8') #因為要以字串的形式查詢,所以要 decode

        #然後就是查詢 html 中的 'current-comment-page'
        a = html.find( 'current-comment-page') + 23  #加上 23 位偏移就剛到到頁碼數的第一位數字
        b = html.find(']', a)  #找到 a 位置之後的第一個方括號所在位置的索引座標
               
        return html[a : b]  #這就是最新的頁碼數啦

def find_imgs(url):  #給一個頁面的連結,返回所有圖片地址組成的列表
        html = url_open(url).decode('utf-8')
        img_addrs = []  #宣告一個儲存圖片地址的列表

        #查詢圖片地址        
        a = html.find('img src=')        
        while a != -1:
                b = html.find('.jpg', a, a+255) #在 a 到 a+255 區間找 '.jpg',防止有不是 '.jpg' 格式的圖片
                #如果 b 找不到,b 就返回 -1        
                if b != -1:
                        img_addrs.append(html[a+9: b+4])
                else:
                        b = a + 9

                a = html.find('img src=', b)

        return img_addrs
                             
def save_imgs(folder, img_addrs):
        for each in img_addrs:
        filename = each.split('/')[-1]
        with open(filename, 'wb') as f:
                img = url_open(each)
                f.write(img)


def download_figures(folder = 'figures', page = 10):
        os.mkdir(folder)  #建立資料夾
        os.chdir(folder)

        url = "http://jandan.net/ooxx/"  #隨手拍欄目的連結,也是最新頁面的連結
        page_num = int(get_page(url)) #得到最新頁面的頁碼數

        for i in range(page):                
                page_url = url + 'page-' + str(page_num) + '#comments'  #得到要爬取的頁面的連結
                print(page_url)
                img_addrs = find_imgs(page_url)  #得到頁面所有圖片的地址,儲存為列表
                save_imgs(folder, img_addrs)  #儲存圖片到本地資料夾
                page_num -= 1  #逐步找到前幾個頁面

if __name__ == '__main__':
        download_figures()                   

但是現在,煎蛋網用這段程式碼是無法實現的了,主要問題在於 沒有辦法爬取到 .jpg,這是因為這個網站已經被加密了。

怎樣判斷一個網站被加密了,就是

使用urllib.urlopen匯出html文字和審查元素中相應欄位對不上。

以後你會發現對不上是常態,一般是JS加密的  可以說大一點的網站這些資訊都會對不上。

那怎麼解決呢?

目前我只用的一種方法就是:使用selenium爬取js加密的網頁

需要詳細講解的可以檢視:python使用selenium爬取js加密的網頁

所以呢,我的程式碼就是下面這樣子了:

#從加密的煎蛋網的隨手拍欄目下載圖片
import os
from selenium import webdriver
import urllib.request

def url_open(url):  #返回普通不加密網頁的原始碼(速度快)
        req = urllib.request.Request(url)
        req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36')
        response = urllib.request.urlopen(url)
        html = response.read()
        return html

def url_open_jm(url):   #返回加密網頁的原始碼(速度慢)
        chrome = webdriver.Chrome()
        chrome.get(url)
        html = chrome.page_source
        return html #返回的就是字串
       
'''
def get_page(url): #得到最新頁面的頁碼數(可以使用不加密讀碼得到,為了加快速度)
        html = url_open(url)
        
        #然後就是查詢 html 中的 'current-comment-page'
        a = html.find( 'current-comment-page') + 23  #加上 23 位偏移就剛到到頁碼數的第一位數字
        b = html.find(']', a)  #找到 a 位置之後的第一個方括號所在位置的索引座標
                
        return html[a : b]  #這就是最新的頁碼數啦
'''
def get_page(url): #得到最新頁面的頁碼數
        html = url_open(url)
        html = html.decode('utf-8') #因為要以字串的形式查詢,所以要 decode

        #然後就是查詢 html 中的 'current-comment-page'
        a = html.find( 'current-comment-page') + 23  #加上 23 位偏移就剛到到頁碼數的第一位數字
        b = html.find(']', a)  #找到 a 位置之後的第一個方括號所在位置的索引座標
               
        return html[a : b]  #這就是最新的頁碼數啦

def find_imgs(url):  #給一個頁面的連結,返回所有圖片地址組成的列表
        html = url_open_jm(url)  #這個必須使用加密開啟的方式
        
        img_addrs = []  #宣告一個儲存圖片地址的列表
        
        #查詢圖片地址
        #加密的網頁破解後得到的影象在這裡:
        #<img src="http://ww3.sinaimg.cn/mw600/006XNEY7gy1fy66dacugfj30qh0zkdhu.jpg" 
        #所以要先找jpg,然後找img src=
        
        a = html.find('.jpg') 
        while a != -1:
                b = html.rfind('img src=', a-100, a) #在 a-100 到 a區間找 'img src=',必須反向查詢
                
                #如果 b 找不到,b 就返回 -1        
                if b != -1:                        
                        img_addrs.append(html[b+9: a+4])
                
                a = html.find('.jpg', a+4)
        for each in img_addrs:
                print(each)

        return img_addrs               
                
def save_imgs(folder, img_addrs):
        for each in img_addrs:
                filename = each.split('/')[-1]
                with open(filename, 'wb') as f:
                        img = url_open(each)
                        f.write(img)

def download_figures(folder = 'figures', page = 2):
        os.mkdir(folder)  #建立資料夾
        os.chdir(folder)

        url = "http://jandan.net/ooxx/"  #隨手拍欄目的連結,也是最新頁面的連結
        page_num = int(get_page(url)) #得到最新頁面的頁碼數

        for i in range(page):
        
                page_url = url + 'page-' + str(page_num) + '#comments'  #得到要爬取的頁面的連結
                print(page_url)
                img_addrs = find_imgs(page_url)  #得到頁面所有圖片的地址,儲存為列表
                save_imgs(folder, img_addrs)  #儲存圖片到本地資料夾
                page_num -= 1  #逐步找到前幾個頁面

if __name__ == '__main__':
        download_figures()

完美實現目標,只不過selenium 的速度是真的慢,以後如果有更好的辦法,會繼續改進的,也希望大家多多批評指導。