Python 爬取煎蛋網隨手拍

百度百科對爬蟲的定義爲:網絡爬蟲(又被稱爲網頁蜘蛛,網絡機器人,在FOAF社區中間,更經常的稱爲網頁追逐者),是一種按照一定的規則,自動的抓取萬維網信息的程序或者腳本。另外一些不常使用的名字還有螞蟻,自動索引,模擬程序或者蠕蟲。

而我本人對爬蟲的理解,就是利用Python下載網站的源代碼,並對該源代碼進行解析,並提取出我們想要的內容(如文字,圖片,音頻等)。這聽起來好像挺簡單的,只要知道網站的地址,並利用resquests模塊訪問網站並下載源碼就可以了。

於是我們可以照着這個初步的思路,就能成功地爬取一些未設置反爬蟲技術的網站,比如我另外一篇爬取豆瓣圖書目錄的文章。今天我們做一個進階,實現對圖片的爬取,以及破解簡單的反爬蟲技術

首先,我們去目標網站煎蛋網看一下。
在這裏插入圖片描述
我們先進入首頁,但這個網頁不是我們要爬取的目標。在導航條中找到隨手拍欄目,點進去。
在這裏插入圖片描述
點進去以後纔是我們要爬取的網頁,這裏面有很多的圖片,這就是我們今天的最終爬取目標。Ok,首先我們把這個網址複製下來——http://jandan.net/ooxx。
萬丈高樓平地起,我們先試着爬取其中一張圖片。
鼠標右鍵查看源代碼,在圖片上右鍵檢查元素,就可以在網頁代碼中定位到該圖片的位置。
在這裏插入圖片描述

我們知道自己要爬取的圖片的信息爲:

<img style="max-height: none; max-width: 480px;" src="http://ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg">

有了以上信息,我們就可以使用

picture=soup.find("img",style="max-height: none; max-width: 480px;",attrs="src")

嘗試初步的爬取了。代碼如下:

from bs4 import BeautifulSoup
import requests
import base64

#把網址粘貼進來
url="http://jandan.net/ooxx"
#僞裝成瀏覽器
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
#固定格式:訪問網站,下載網頁代碼
req=requests.get(url,headers=headers)
#固定格式:以lxml解釋器讀取下載的網頁文本
soup=BeautifulSoup(req.text,"lxml")#調用lxml解析器,需要導入lxml模塊
#找到該圖片
picture=soup.find("img",style="max-height: none; max-width: 480px;",attrs="src")
#輸出圖片地址
print(picture)

理想狀態下,這段代碼應該輸出"http://ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg",但是…
在這裏插入圖片描述
???
爲什麼會是"None",仔細檢查了n遍代碼後發現沒問題,網站上的源碼也沒問題。然後再想一下,既然是在我們下載的html文本中沒找到我們想要的字段,那就看一下我們下載到的網頁文本

print(soup)

在這裏插入圖片描述
用"逐字逐句對照法"對比我們下載的網頁代碼和網站源代碼。最後我們終於不得不承認那句莫名其妙的

<span class="img-hash">Ly93dzMuc2luYWltZy5jbi9tdzYwMC8wMDZYTkVZN2d5MWZ4ejNma3hxYjJqMzBybzByb2pzeC5qcGc=</span></p>

就是我們要找的圖片下載地址,而源代碼中的“查看原圖”也被替換成了一張blank.gif。

噹噹噹!傳說中的反爬蟲技術,我們總算是見識到了。然後呢?只差臨門一腳就踏入新天地了,結果發現大門緊閉。那就看看門上的鎖是怎樣的嘍?(檢索這段亂碼)

#picture=soup.find("img",style="max-height: none; max-width: 480px;",attrs="src")
picture=soup.find("span",class_="img-hash")

得到的結果如下:
在這裏插入圖片描述
關於這段亂碼,我有兩個猜想:
1.這只是一段純粹的亂碼,網站維護者爲了戲耍爬蟲而設置的隨機請求響應;
2.這段亂碼是有規律的,可以通過解碼譯出圖片地址。

對於猜想1,很明顯那是絕對不可能的,因爲我堅信編程具有嚴謹性,如果是純粹的亂碼,那普通用戶訪問該網站時也絕對不可能看到圖片。但就算不能真的亂碼,如果是基於請求時間進行相應反饋的動態加密數據(C++中隨機函數rand()就是根據時間給出隨機數的,而百度翻譯的反爬蟲技術就與此類似),以我目前的技術來看,也跟亂碼差不多了。所以爲了避免我做徒勞的無用功,我多次爬取了該段代碼,發現它是一串靜態的數據,那應該就比較容易破解了…吧。

好了,知道了這個鎖不是死鎖,但我們並不知道鎖的內部構造(亂碼的生成函數)如何,也沒有配鎖(編寫解碼函數)的技術。那怎麼辦?只能找找看有沒有萬能鑰匙(廣泛使用的基礎解碼函數)了。
結果還真有!(百度大法好啊)

import base64
print(base64.b64decode(picture.text).decode("utf-8"))
#運行得到:
#//ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg
#好像不太完整,加個“http:”
print("http"+base64.b64decode(picture.text).decode("utf-8"))
#http://ww3.sinaimg.cn/mw600/0073tLPGgy1fxyzdn2l6jj31400u0e83.jpg

Tips:Base64是網絡上最常見的用於傳輸8Bit字節代碼的編碼方式之一,可以查看RFC2045~RFC2049,上面有MIME的詳細規範。Base64編碼可用於在HTTP環境下傳遞較長的標識信息。
有一個bs4加密解密網站:https://base64.supfree.net/。

把結果複製到瀏覽器粘貼:
在這裏插入圖片描述

爬到圖片地址,接下來就是下載了:
1.先拿出圖片地址

src="http"+base64.b64decode(picture.text).decode("utf-8")

2.像訪問網頁一樣訪問該網址

img=requests.get(src,headers=headers)

3.像下載txt文本一樣,創建路徑,在該路徑下創建.jpg後綴名文件存儲數據。(這裏要特別說明一下,在獲取圖片時,調用img對象的content屬性獲取二進制文本,而獲取txt文本時,調用img對象的text屬性獲取字符串類型文本。)

import os
path = os.path.join("L:/煎蛋網隨手拍")  # 合併打開路徑
if not os.path.exists(path):#校驗文件是否存在
   os.mkdir(path)
 with open(os.path.join(path, "下載的圖片.jpg"), "wb") as f:  # 合併路徑path和jpg,不存在則自動創建路徑
   f.write(img.content)

4.下載完成。
在這裏插入圖片描述

既然門都開了,那肯定不能只看完前院就走,我們要整個座房子都看看。爬取它整個網站的圖片!
將之前的find(class_=“img-hash”)函數改成find_all(class_=“img-hash”),就能將在當前網站查找"img-hash"值的class標籤字段從一個變成全部,並且生成列表存儲。
結果如下:
在這裏插入圖片描述
在這裏插入圖片描述但是你發現,這樣只能下載當前頁的圖片,整個網站60頁的圖片我全都要,該怎麼辦?
那就點一下其他頁面看看
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
可以發現這些網頁跳轉都是有固定格式的,就最前面那一頁沒有後綴,那我們點一下第60頁
在這裏插入圖片描述
可以發現它也是符合一般規律,可以有後綴的。
那這樣我們就可以寫一個字符串內嵌變量的url去做頁面循環跳轉,但因爲網站的圖片一直在更新,最大頁面數也一直在變,我們怎麼得到這個最大頁數呢?
哈哈,其實很簡單,看下面的操作
在這裏插入圖片描述
還是檢查元素,幫我們找到了這個最大頁數,然後

page=int(soup.find("span",class_="current-comment-page").get_text()[1:-1])
print(page)
#運行結果:
#60

然後就可以做一個循環讓它去不斷跳轉頁面了

url=url = 'http://jandan.net/ooxx'
page=int(soup.find("span",class_="current-comment-page").get_text()[1:-1])
while page>0:
	real_url=url+"/page-"+str(page)
	#http://jandan.net/ooxx/page-?
	page=page-1

最後,我們把上面那些零件拼裝一下,得到我們想要的最終代碼:


from bs4  import BeautifulSoup
import requests
import os
import base64

url = 'http://jandan.net/ooxx'
headers={
   'User-Agent':'MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'
}
#先進入主站
html=requests.get(url,headers)
soup=BeautifulSoup(html.text,"lxml")

#獲取總頁數
page=int(soup.find("span",class_="current-comment-page").get_text()[1:-1])

#先創建圖片存儲位置
path = os.path.join("L:/煎蛋網隨手拍")  # 合併打開路徑
if not os.path.exists(path):#校驗文件是否存在
   os.mkdir(path) #創建路徑path
num=1#記錄圖片數
while page>0:
   #每一頁的網站地址爲 http://jandan.net/ooxx/page-頁數
   real_url=url+"/page-"+str(page)

   #讀取網頁
   html=requests.get(real_url,headers=headers)
   soup=BeautifulSoup(html.text,"lxml")

   # 找到被掩蓋的圖片地址亂碼
   imgurl=soup.find_all(class_="img-hash")

   #譯出亂碼內的真實地址,別看很複雜的一段,用base64的.decode直接解碼,就出現真實網址了
   real_img_url=[]
   for a in imgurl:
      img_url=a.text
      img_url=base64.b64decode(img_url).decode("utf-8")
      real_img_url.append(img_url)

   #下載圖片
   for a in real_img_url:
         src="http:"+a#圖片地址並不完整,要添加一個http頭
         print(src)
         img=requests.get(src,headers=headers)#訪問圖片頁面
         with open(os.path.join(path, str(num)+".jpg"), "wb") as f:  # 合併路徑path和num.jpg,不存在則自動創建
            f.write(img.content)  # img.content獲取二進制數據,而img.text獲取字符串文本
         num=num+1#圖片數+1
   page=page-1#頁數-1

最後的最後,應該挺詳細的了吧,如果還有什麼不懂的地方可以在下方評論留言,或者有什麼錯誤的地方也希望各位不吝指教。

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