一、先說爬取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))
輸出結果: