基於urllib編寫爬蟲,爬取Google動態圖片,詳細分析

一、先說爬取Google的幾個坑

1、首先要翻牆才能訪問Google,這個不用多講。但是注意一點,要把shadowsocks的代理模式開爲全局,如下所示。否則可能訪問某些圖片地址時會出錯。

                                             

2、注意訪問節奏,一個不小心Google就會封你的IP,不過比較人性化的是,過幾分鐘,Google就會給你解封了。所以使用代理IP時,最好是有多個服務器可以選擇。我這裏設置的訪問節奏是sleep(0.5),即0.5秒。

                  

3、Google的圖片動態加載方式無異於AJAX,什麼是AJAX,自行百度吧。當我們的界面滾動到預設的錨點時。服務器會給瀏覽器發送一個.txt文件,這裏面就包含了新加載進來的所有鏈接。

二、分析如何拿到圖片的url

1、第一步

打開開發者工具,分析包。由於圖片的加載時ajax,所以我們直接看filter裏面的XHR即可。XHR又是什麼,同樣-自行百度吧。filter裏面的XHR就是篩選所有XHR類型的請求,通俗的講XHR用來記錄ajax的請求。

      

2、第二步

輸入搜索關鍵字,點擊搜索,發現多了兩條記錄。請注意含有search的這條記錄。你要是問我,爲什麼要注意這條記錄,其實這也是我後面找到規律才發現是它,一開始我也不知道的。

我們在preview中分別查看兩條記錄(如下),發現含search的這條記錄沒什麼東西,而含irc的東西較多。這時怎麼辦呢,我們看一下含src的這條記錄中是否含有 搜索結果中的圖片的url。

 

 

我們將這條記錄的url,copy下來,粘貼到瀏覽器地址欄,看看有什麼變化。發現在瀏覽器左下角,有一個txt文檔被下載了,而txt的內容和此條鏈接的preview的內容是一樣的。

                 

我們可以懷疑搜索結果的圖片的url放在這個txt文本中。所以此時要幹什麼了?

去搜索結果頁面,copy某一張圖片的圖片地址,然後在txt文本中查找,找到了的話那就是它拉。

  

但是,發現並沒有找到。劇透一下,這個確實不是我們要找的交互的數據。

別急,咱們繼續往下走。

第三步:頁面繼續往下拉

它不是動態加載嗎,它不是到錨點就開始返回新圖片嗎?歐克,那我就往下拉,看看XHR裏會有什麼反應。

我們發現多了很多ajax請求,再去看preview欄,發現都含有很多內容。如圖中1,2這兩種類型。

這時候還和剛纔一樣,copy這些請求的url,粘到瀏覽器中,發現同樣返回給txt文件。

那麼然後呢? 肯定是去圖片搜索結果欄,copy圖片地址,到txt文件中查找,哪個文件有圖片地址,我們就可以在這個文件中解析圖片的url了。(這個就需要你自己去試了)

最後發現圖片的url全都藏在以search開頭的鏈接中(也就是下圖中的紅框裏面的1鏈接)

 

這時候我們把所有的含有search的url複製出來觀察。我們發現第一個url長得和別人不一樣,我們先不管它。

後面的url長得基本一樣,稍加分析,我們就能看出,紅色矩形框內表示的頁面信息。q=“關鍵詞”。

由此,推測第一個url的正確格式應該爲 jin=0&start=0,構造好了放到瀏覽器中同樣返回一個txt文檔。copy搜索頁面圖片的url,發現可以在此txt文檔中找到。

至此,應該大家已經找到規律了。

四、話不多說,貼代碼把

我是使用多線程爬取的,速度比單線程快很多。

# -*- coding: utf-8 -*-
############################################################
#          google圖片爲動態加載,ajax會返回一個txt的文件   #
#    圖片地址就藏在這個txt文件中,因此要使用txt的處理方式  #
#       由於爬取谷歌需要翻牆,此處需要將ss設置爲全局即可   #
############################################################

############################################################
#          注意控制訪問節奏,最好是要sleep一秒             #
#          google稍微檢測到訪問速度過快,就封IP            #
############################################################

############################################################
#                  ******** 注意*********                  #
#       使用多進程的map函數時,map會自動將url_list中的     #
#       url逐一發送給get_info函數。                        #
#       因此get_info拿到的單個的url                        #
############################################################
import urllib
import urllib.request
import re
import time
import socket
import random
from multiprocessing.dummy import Pool as ThreadPool
from multiprocessing import cpu_count
import multiprocessing
import time

#統計圖片下載數量,並且爲圖片命名時會用到
img_count = 1
def get_url_list(keywords, page):
    '''在網頁中提取所有的圖片鏈接'''
    opener = urllib.request.build_opener()
    header = get_header()
    opener.addheaders = [header]
    urllib.request.install_opener(opener)
    #圖片鏈接提取表達式
    pat = 'ou":"(.*?)","ow'
    #urls_list存放圖片url
    urls_list = []
    for i in range(0, page):
        url = "https://www.google.com/search?ei=k8gBXeXYN9Kn1fAPso636Aw&yv=3&tbm=isch&q="+keywords+"&vet=10ahUKEwil7su2x-XiAhXSUxUIHTLHDc0QuT0ISygB.k8gBXeXYN9Kn1fAPso636Aw.i&ved=0ahUKEwil7su2x-XiAhXSUxUIHTLHDc0QuT0ISygB&ijn="+str(i)+"&start="+str(i*100)+"&asearch=ichunk&async=_id:rg_s,_pms:s,_fmt:pc"
        #data爲str類型
        data = opener.open(url, timeout=5).read().decode("utf-8")
        urls = re.compile(pat).findall(data)
        for url_in_urls in urls:
            urls_list.append(url_in_urls)
        time.sleep(1)
    return urls_list

def get_header():
    #隨機返回一個header
    headerUserAgentList = (
              "Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)",
              "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0",
              "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36",
              "Opera/9.80 (Windows NT 6.1; WOW64; U; en) Presto/2.10.229 Version/11.62",
              "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
              )
    header = ('User-Agent',random.choice(headerUserAgentList))
    return header

def get_info(urls_list):

    #將圖片下載到本地
    opener = urllib.request.build_opener()
    header = get_header()
    opener.addheaders = [header]
    urllib.request.install_opener(opener)
    print(urls_list)
    global img_count
    try:
        file = 'C:/Users/weihua/Desktop/信用證圖片/test/' + str(img_count) + '.png'
        # tests = opener.open(urls_list, timeout=10)
        urllib.request.urlretrieve(urls_list, file)
        img_count += 1
        print('**************第 %s 張圖片爬取成功******************'% img_count)
        print("-------成功-------")
        time.sleep(0.5)

    except urllib.request.URLError as e:
        if hasattr(e, "code"):
            print(e.code)
        if hasattr(e, "reason"):
            print(e.reason)
            # 若爲異常,延時10s執行
            time.sleep(1)
            print("-------下載失敗-------")
    except Exception as e:
        print("exception:", str(e))
        # 若爲Exception異常,延時1s執行
        time.sleep(1)

    # 防止下載時間過長,需<=60s
    except socket.timeout:
        count = 1
        while count <= 2:
            try:
                urllib.request.urlretrieve(urls[j], file)
                break
            except socket.timeout:
                err_info = 'Reloading for %d time' % count if count == 1 else 'Reloading for %d times' % count
                print(err_info)
                count += 1
        if count > 2:
            print("下載超時")

def method(method, keywords, pages):
    #傳參選擇多進程還是多線程爬取
    url_list = get_url_list(keywords, pages)
    print(url_list)
    print(len(url_list))
    if method == "threading":
        pool = ThreadPool(cpu_count())  # 4進程
        time1 = time.time()
        results = pool.map(get_info, url_list)  # 需要執行的方法和執行列表
        pool.close()
        pool.join()  # 等待線程都結束再執行主模塊
        time2 = time.time()
    if method == "progress":
        pool = multiprocessing.Pool(processes=cpu_count())
        time1 = time.time()
        # resluts = pool.map(get_info, url_list)
        resluts = pool.map(get_info, url_list)
        pool.close()
        pool.join()
        time2 = time.time()
    return time2 - time1
if __name__ == "__main__":
    print("--------------start-----------")
    socket.setdefaulttimeout(60)
    # time_use = method("threading")
    time_use = method("threading", "insurance+document", 8)
    print("多線程耗時:"+str(time_use))

 

輸出結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章