項目簡介:
本實驗通過使用 Python 實現一個淘寶女郎圖片收集爬蟲,學習並實踐 BeautifulSoup、Selenium Webdriver 及正則表達式等知識。
一、實驗說明
1.1 實驗介紹
本項目通過使用 Python 實現一個淘女郎圖片收集爬蟲,學習並實踐 BeautifulSoup、Selenium Webdriver 及正則表達式等知識。在項目開發過程中采用瀑布流開發模型。
1.2 知識點
本項目中將會學習並實踐以下知識點:
- Python3 編程
- 使用 BeautifulSoup
- 解析 HTML 頁面
- 使用 Selenium Webdriver 爬取 Web 頁面
- 使用正則表達式提取所需的關鍵信息
1.3 實驗效果
這是我們要爬取的目標頁面:
淘女郎:https://mm.taobao.com/search_tstar_model.htm
爬取后的目錄結構如下:
每個目錄中都有一系列的圖片:
二、基礎工具
2.1 安裝 pip3
首先,由於使用的工具都需要通過 pip3 進行安裝,實驗樓的環境中沒有安裝 pip3(現在已經有了,可以跳過此步),所以需要先將pip3准備好。
sudo apt-get install python3-pip
2.2 安裝 BeatifulSoup
簡介 BeautifulSoup 庫的名字取自劉易斯·卡羅爾在《愛麗絲夢游仙境》里的同名歌詞。就像故事中他在仙境中的說法一樣,BeautifulSoup 試圖化平淡為神奇。它通過定位 HTML 標簽來去格式化和組織復雜的網絡信息,用簡單易用的 Python 對象為我們展現 XML 結構信息。
安裝 由於這次實驗是在 python3.X 版本以上的所以,將拓展庫安裝到特定的庫中使用 pip3,從而安裝到 python3 的系統目錄中,
sudo pip3 install beautifulsoup4
BeautifulSoup4 是現今的最新版本,也是接下來重點使用的工具。
此外,項目中 beautifulsoup 還會用到 html5lib 這個模塊,所以也需要安裝 html5lib:
sudo apt-get install python3-html5lib
2.3 Selenium
簡介 Selenium 是一個強大的網絡數據采集工具,最初是為網站自動化測試而開發的。近幾年,他還被廣泛用於獲取精確的網站快照,因為他們可以直接運行在瀏覽器上。Selenium 可以讓瀏覽器自動加載頁面,獲取需要的數據,甚至頁面截屏,或者判斷網站上某些動作上是否發生。
Selenium 自己不帶瀏覽器,它需要與第三方瀏覽器結合在一起使用。,可以直接看到一個 FireFox 窗口被打開,進入網站,然后執行你在代碼中設置的動作。雖然使用 Firefox瀏覽器看起來更清楚,但在本實驗中我們采用 PhantomJS來代替真實的瀏覽器結合使用。
安裝
sudo pip3 install selenium
測試是否都安裝成功:
2.4 PhantomJS
簡介 一個 無頭 的瀏覽器,PhantomJS 會把網站加載到內存並執行頁面上的 JavaScript,但是不會向用戶展示網頁的圖形化界面,可以用來處理 cookie、JavaScript 及 header 信息,以及任何你需要瀏覽器協助完成的事情。
安裝 我們從阿里的鏡像下載包,然后解壓到你喜歡的目錄,這里我們解壓到 /opt/。
wget https://npm.taobao.org/mirrors/phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2
sudo tar xjf phantomjs-2.1.1-linux-x86_64.tar.bz2 -C /opt/
2.5 Ajax 信息加載
現在有很多頁面都是采用 Ajax 加載數據,我們實驗的目標網址也是這樣的:淘女郎
如果我們用傳統的方法采集這個頁面,只能獲取加載前的頁面,而我們真正需要的信息( Ajax 執行之后的頁面)卻抓不到,后續實驗中可以看到效果的區別。
三、項目實現
3.1 本節目標
本節實驗中我們將分別按照如下步驟:
抓取淘寶MM 的姓名,封面圖片,年齡、所在城市等信息 抓取每一個MM個人主頁內的寫真圖片 把每一個MM的寫真圖片按照文件夾保存到本地
3.2 可行性分析
淘女郎首頁上的源碼信息是公開的,本次實驗僅僅是用來技術實踐,並不帶盈利性目的,也不將圖片用於其他商業環境,並不會產生商業上的產權糾紛,所以這個項目是可行的。
3.3 程序結構
遍歷淘女郎主頁上所有 MM 抓取各個 MM 的姓名,封面圖片,年齡、所在城市等信息 遍歷MM 個人主頁內的所有寫真圖片 把每 MM 的寫真圖片按照文件夾保存到本地
3.4 流程說明
通過 Selenium Webdriver 獲得目標頁面源碼,之后通過 BeautifulSoup 解析概源碼,通過正則表達式提取出模特名字、所在城市、身高、體重,個人主頁、封面圖片地址等信息,根據模特名字和城市建立文件夾。 再次通過 Selenium Webdriver 獲得模特個人主頁的頁面源碼,之后通過 BeautifulSoup 解析源碼,通過正則獲得頁面藝術照的URL地址信息。 最后通過 urllib 內置庫,打開圖片地址,通過二進制讀寫的方式獲得模特藝術照,並將藝術照存在相應文件夾里面。
3.5 獲取信息模塊實現
獲得頁面源碼 最簡單的查看網頁源碼的方式就是在瀏覽器中右鍵選擇審查元素,其他類型瀏覽器也是類似的:
而 Python 代碼中的實現則是調用 Selenium Webdriver 和 PhantomJS 來模擬打開該頁面源碼,最后使用 BeautifulSoup 進行解析。
注意實驗的時候,代碼先不要直接寫在腳本文件里,可以在交互模式的解釋器里嘗試一下代碼,試着了解下運行原理。
我們先導入相關模塊,然后設置一些變量(瀏覽器路徑,起始頁,輸出目錄,解析器名稱):
import os
import threading
import re
from bs4 import BeautifulSoup
from urllib.request import urlopen
from selenium import webdriver
browserPath = '/opt/phantomjs-2.1.1-linux-x86_64/bin/phantomjs'
homePage = 'https://mm.taobao.com/search_tstar_model.htm?'
outputDir = 'photo/'
parser = 'html5lib'
現在我們來看看怎樣模擬瀏覽器查看源碼:
driver = webdriver.PhantomJS(executable_path=browserPath) #瀏覽器的地址
driver.get(homePage) #訪問目標網頁地址
bsObj = BeautifulSoup(driver.page_source, parser) #解析目標網頁的 Html 源碼
這個過程就相當於右鍵的點擊審查的過程。
driver = webdriver.PhantomJS(executable_path=browserPath)
這里的意思是實例化一個 PhantomJS 瀏覽器對象,括號里面填的是瀏覽器的安裝路徑信息,填在單引號里面。selenium支持的瀏覽器類型有 chrome、FireFox 等,具體的自行查看 webdriver 的 API。
bsObj=BeautifulSoup(driver.page_source,parser)
這里的 driver.page_source 意思是網頁的全部 HTML 源碼,包含的具體內容,可以通過 print(driver.page_source)打印查看。
獲得MM個人信息
girlsList = driver.find_element_by_id('J_GirlsList').text.split(
'\n') #獲得主頁上所有妹子的姓名、所在城市、身高、體重等信息
上面的截圖可以發現,整個圖片層次是在
-
里面的通過 J_GirlsList 定位到這個層次,屬性 text 包含網頁中所有 HTML 標簽的內容,類型為字符串,我們可以看看 text里的東西是什么:
print(driver.find_element_by_id('J_GirlsList').text)
而后面的 split('\n') 則會將屬性 text 中的字符串以換行符分割,得到一個包含所有分割后的字符串的列表。
獲得 MM 個人主頁地址
girlsUrl = bsObj.find_all("a",{"href": re.compile("\/\/.*\.htm\?(userId=)\d*")}) #解析出妹子的個人主頁地址等信息
BeautifulSoup 的具體內容我這里不會講深入的,想詳細了解的,可以去他們的官網查閱API,用到的方法稍后會進行分析。 find_all 方法可以獲得所有的你想通過定位獲得的信息,可以使用 xml、xPath、正則表達式等語言來進行定位。
re.compile("\/\/.*\.htm\?(userId=)\d*")
這里雙引號里面各種斜桿和反斜桿的符號就是正則表達式,專門用來的做信息配對的,Python 的正則匹配引擎,有很多東西可以研究
獲得 MM 封面圖片地址
imagesUrl = re.findall('\/\/gtd\.alicdn\.com\/sns_logo.*\.jpg',
driver.page_source) #獲取所有妹子的封面圖片
我們可以發現,妹子的封面圖片的 url 都是形如 //gtd.alicdn.com/sns_logo/xx/xxxxxx.jpg 這樣的字符串,這樣我們就可以用正則表達式匹配它。
建立相應文件夾 本部分代碼用來創建保存照片的目錄結構:
def mkdir(path):
# 判斷路徑是否存在
isExists = os.path.exists(path)
# 判斷結果
if not isExists:
# 如果不存在則創建目錄
print(" [*]新建了文件夾", path)
# 創建目錄操作函數
os.makedirs(path)
else:
# 如果目錄存在則不創建,並提示目錄已存在
print(' [+]文件夾', path, '已創建')
這里是一些基本的文件操作,判斷文件夾是否存在,存在則不創建,不存在就創建文件夾。
獲得MM個人頁面源碼 這里前面的操作一樣的原理:
driver = webdriver.PhantomJS(executable_path=browserPath)
driver.get(url)
bsObj = BeautifulSoup(driver.page_source, parser)
3.6 圖片存儲模塊實現
存儲封面圖片 下面的代碼用於存儲主頁的封面圖片(girlCover 是封面圖片 url 地址):
data = urlopen(girlCover).read()
with open(outputDir + girlNL + '/cover.jpg', 'wb') as f:
f.write(data)
urlopen() 打開圖片的URL地址,然后使用 read() 方法讀取圖片的二進制數據。
存儲個人藝術照 下面的代碼用來存儲個人藝術照片:
imgs = bsObj.find_all("img", {"src": re.compile(".*\.jpg")})
for i, img in enumerate(imgs[1:]):
html = urlopen('https:' + img['src'])
data = html.read()
fileName = "{}/{}.jpg".format(path, i + 1)
print(" [+]Loading...", fileName)
with open(fileName, 'wb') as f:
f.write(data)
html = urlopen('https:' + img['src']) 這么做的原因是:我們獲得的網址信息是不完整的,我們需要手動補充完整。
四、組裝及調試
4.1 調試之前
前面已經所有模塊拆分並解釋得很詳細,現在應該是從全局的角度審視這個項目,然后增加一些異常處理、存儲操作,以增加軟件運行的健壯性。
整合數據
# 所有妹子的名字地點
girlsNL = girlsList[::3]
# 所有妹子的身高體重
girlsHW = girlsList[1::3]
# 所有妹子的個人主頁地址
girlsHURL = [('http:' + i['href']) for i in girlsUrl]
# 所有妹子的封面圖片地址
girlsPhotoURL = [('https:' + i) for i in imagesUrl]
girlsInfo = zip(girlsNL, girlsHW, girlsHURL, girlsPhotoURL)
4.2 組裝各個模塊
for girlNL, girlHW, girlHURL, girlCover in girlsInfo:
print("[*]Girl :", girlNL, girlHW)
# 為妹子建立文件夾
mkdir(outputDir + girlNL)
print(" [*]saving...")
# 獲取妹子封面圖片
data = urlopen(girlCover).read()
with open(outputDir + girlNL + '/cover.jpg', 'wb') as f:
f.write(data)
print(" [+]Loading Cover... ")
# 獲取妹子個人主頁中的圖片
getImgs(girlHURL, outputDir + girlNL)
將個人網頁上的圖片存儲到相應的個人文件夾中 即之前調用過的 getImgs() 函數
def getImgs(url, path):
driver = webdriver.PhantomJS(executable_path=browserPath)
driver.get(url)
print(" [*]Opening...")
bsObj = BeautifulSoup(driver.page_source, parser)
#獲得模特個人頁面上的藝術照地址
imgs = bsObj.find_all("img", {"src": re.compile(".*\.jpg")})
for i, img in enumerate(imgs[1:]): #不包含與封面圖片一樣的頭像
try:
html = urlopen('https:' + img['src'])
data = html.read()
fileName = "{}/{}.jpg".format(path, i + 1)
print(" [+]Loading...", fileName)
with open(fileName, 'wb') as f:
f.write(data)
except Exception:
print(" [!]Address Error!")
driver.close()
for i, img in enumerate(imgs[1:]): 這一行為什么使用 imgs[1:] 而不是直接使用 imgs,因為每個 MM 的個人頁面的第一個圖片必定是頭像,而頭像卻是與封面圖片一樣,分辨率還小了許多,就沒必要抓取了:
另外注意兩個地方:
1.正則表達式部分
imgs = bsObj.find_all("img", {"src": re.compile(".*\.jpg")})
用 ".*.jpg" 匹配任意以 '.jpg' 結尾的字符串。
2.異常處理
有時候解析出來的圖片 url 會有問題,或者解析出錯之類的,但我們程序沒有必要因為個別錯誤拋出的異常而被終止。
所以我們應該做一下異常處理,使用 try ... except ... 異常處理語句。
try 里面是執行語句塊,當拋出異常的時候,就會捕獲異常,並跳轉到 except 子句,這里的寫法會捕獲任意異常,這是因為這里會不只出現一種異常。不過在生產環境中不建議這樣做
至此,整個項目就完成啦。
五、整個項目源碼
項目使用的完整代碼供參考:
#!/usr/bin/env python3
import os
import threading
import re
from bs4 import BeautifulSoup
from urllib.request import urlopen
from selenium import webdriver
browserPath = '/opt/phantomjs-2.1.1-linux-x86_64/bin/phantomjs'
homePage = 'https://mm.taobao.com/search_tstar_model.htm?'
outputDir = 'photo/'
parser = 'html5lib'
def main():
driver = webdriver.PhantomJS(executable_path=browserPath) #瀏覽器的地址
driver.get(homePage) #訪問目標網頁地址
bsObj = BeautifulSoup(driver.page_source, parser) #解析目標網頁的 Html 源碼
print("[*]OK GET Page")
girlsList = driver.find_element_by_id('J_GirlsList').text.split(
'\n') #獲得主頁上所有妹子的姓名、所在城市、身高、體重等信息
imagesUrl = re.findall('\/\/gtd\.alicdn\.com\/sns_logo.*\.jpg',
driver.page_source) #獲取所有妹子的封面圖片
girlsUrl = bsObj.find_all(
"a",
{"href": re.compile("\/\/.*\.htm\?(userId=)\d*")}) #解析出妹子的個人主頁地址等信息
# 所有妹子的名字地點
girlsNL = girlsList[::3]
# 所有妹子的身高體重
girlsHW = girlsList[1::3]
# 所有妹子的個人主頁地址
girlsHURL = [('http:' + i['href']) for i in girlsUrl]
# 所有妹子的封面圖片地址
girlsPhotoURL = [('https:' + i) for i in imagesUrl]
girlsInfo = zip(girlsNL, girlsHW, girlsHURL, girlsPhotoURL)
# 姓名地址 girlNL, 身高體重 girlHW
# 個人主頁地址 girlHRUL, 封面圖片 URL
for girlNL, girlHW, girlHURL, girlCover in girlsInfo:
print("[*]Girl :", girlNL, girlHW)
# 為妹子建立文件夾
mkdir(outputDir + girlNL)
print(" [*]saving...")
# 獲取妹子封面圖片
data = urlopen(girlCover).read()
with open(outputDir + girlNL + '/cover.jpg', 'wb') as f:
f.write(data)
print(" [+]Loading Cover... ")
# 獲取妹子個人主頁中的圖片
getImgs(girlHURL, outputDir + girlNL)
driver.close()
def mkdir(path):
# 判斷路徑是否存在
isExists = os.path.exists(path)
# 判斷結果
if not isExists:
# 如果不存在則創建目錄
print(" [*]新建了文件夾", path)
# 創建目錄操作函數
os.makedirs(path)
else:
# 如果目錄存在則不創建,並提示目錄已存在
print(' [+]文件夾', path, '已創建')
def getImgs(url, path):
driver = webdriver.PhantomJS(executable_path=browserPath)
driver.get(url)
print(" [*]Opening...")
bsObj = BeautifulSoup(driver.page_source, parser)
#獲得模特個人頁面上的藝術照地址
imgs = bsObj.find_all("img", {"src": re.compile(".*\.jpg")})
for i, img in enumerate(imgs[1:]): #不包含與封面圖片一樣的頭像
try:
html = urlopen('https:' + img['src'])
data = html.read()
fileName = "{}/{}.jpg".format(path, i + 1)
print(" [+]Loading...", fileName)
with open(fileName, 'wb') as f:
f.write(data)
except Exception:
print(" [!]Address Error!")
driver.close()
if __name__ == '__main__':
if not os.path.exists(outputDir):
os.makedirs(outputDir)
main()
最后在調試的時候,可能會獲取不到淘寶頁面的 HTML 源碼,出口帶寬不足,所以多試幾次,或者拿到自己電腦本地運行一下。
六、總結
這個小項目通過爬取淘女郎的照片來熟悉 BeautifulSoap、正則表達式、Selenium Webdriver、Phantomjs、文件流操作的基礎知識,如果有興趣可以對該程序進行擴展,一些擴展思路供參考:
增強異常處理,使程序爬取的成功率更高,程序更加穩健。 通過機器學習挑選長得好看 MM 照片 增加多線程操作,以增加圖片收集效率,但是從應用角度講,這樣會過度消耗服務器資源,這又是一種DDOS攻擊 繼續衍生下去,爬取主頁中詳細的個人簡歷。