python爬取美空網女神圖片,小心記憶體走火
爬蟲分析
首先,我們已經爬取到了N多的使用者個人主頁,我通過連結拼接獲取到了
www.moko.cc/post/da39db…
在這個頁面中,咱們要找幾個核心的關鍵點,發現 平面拍攝 點選進入的是圖片列表頁面。 接下來開始程式碼走起。
獲取所有列表頁面
我通過上篇部落格已經獲取到了70000(實際測試50000+)使用者資料,讀取到python中。
這個地方,我使用了一個比較好用的python庫pandas,大家如果不熟悉,先模仿我的程式碼就可以了,我把註釋都寫完整。
import pandas as pd # 使用者圖片列表頁模板 user_list_url = "http://www.moko.cc/post/{}/list.html" # 存放所有使用者的列表頁 user_profiles = [] def read_data(): # pandas從csv裡面讀取資料 df = pd.read_csv("./moko70000.csv") #檔案在本文末尾可以下載 # 去掉暱稱重複的資料 df = df.drop_duplicates(["nikename"]) # 按照粉絲數目進行降序 profiles = df.sort_values("follows", ascending=False)["profile"] for i in profiles: # 拼接連結 user_profiles.append(user_list_url.format(i)) if __name__ == '__main__': read_data() print(user_profiles) 複製程式碼
資料已經拿到,接下來我們需要獲取圖片列表頁面,找一下規律,看到重點的資訊如下所示,找對位置,就是正則表示式的事情了。
快速的編寫一個正則表示式 <p class="title"><a hidefocus="ture".*?href="(.*?)" class="mwC u">.*?\((\d+?)\)</a></p> 引入re,requests模組
import requests import re 複製程式碼 # 獲取圖片列表頁面 def get_img_list_page(): # 固定一個地址,方便測試 test_url = "http://www.moko.cc/post/da39db43246047c79dcaef44c201492d/list.html" response = requests.get(test_url,headers=headers,timeout=3) page_text = response.text pattern = re.compile('<p class="title"><a hidefocus="ture".*?href="(.*?)" class="mwC u">.*?\((\d+?)\)</a></p>') # 獲取page_list page_list = pattern.findall(page_text) 複製程式碼
執行得到結果
[('/post/da39db43246047c79dcaef44c201492d/category/304475/1.html', '85'), ('/post/da39db43246047c79dcaef44c201492d/category/304476/1.html', '2'), ('/post/da39db43246047c79dcaef44c201492d/category/304473/1.html', '0')] 複製程式碼
繼續完善程式碼,我們發現上面獲取的資料,有"0"的產生,需要過濾掉
# 獲取圖片列表頁面 def get_img_list_page(): # 固定一個地址,方便測試 test_url = "http://www.moko.cc/post/da39db43246047c79dcaef44c201492d/list.html" response = requests.get(test_url,headers=headers,timeout=3) page_text = response.text pattern = re.compile('<p class="title"><a hidefocus="ture".*?href="(.*?)" class="mwC u">.*?\((\d+?)\)</a></p>') # 獲取page_list page_list = pattern.findall(page_text) # 過濾資料 for page in page_list: if page[1] == '0': page_list.remove(page) print(page_list) 複製程式碼
獲取到列表頁的入口,下面就要把所有的列表頁面全部拿到了,這個地方需要點選下面的連結檢視一下
www.moko.cc/post/da39db…
本頁面有分頁,4頁,每頁顯示資料 4*7=28 條 所以,基本計算公式為 math.ceil(85/28) 接下來是連結生成了,我們要把上面的連結,轉換成
http://www.moko.cc/post/da39db43246047c79dcaef44c201492d/category/304475/1.html http://www.moko.cc/post/da39db43246047c79dcaef44c201492d/category/304475/2.html http://www.moko.cc/post/da39db43246047c79dcaef44c201492d/category/304475/3.html http://www.moko.cc/post/da39db43246047c79dcaef44c201492d/category/304475/4.html 複製程式碼 page_count = math.ceil(int(totle)/28)+1 for i in range(1,page_count): # 正則表示式進行替換 pages = re.sub(r'\d+?\.html',str(i)+".html",start_page) all_pages.append(base_url.format(pages)) 複製程式碼
當我們回去到足夠多的連結之後,對於初學者,你可以先幹這麼一步,把這些連結儲存到一個csv檔案中,方便後續開發
# 獲取所有的頁面 def get_all_list_page(start_page,totle): page_count = math.ceil(int(totle)/28)+1 for i in range(1,page_count): pages = re.sub(r'\d+?\.html',str(i)+".html",start_page) all_pages.append(base_url.format(pages)) print("已經獲取到{}條資料".format(len(all_pages))) if(len(all_pages)>1000): pd.DataFrame(all_pages).to_csv("./pages.csv",mode="a+") all_pages.clear() 複製程式碼
讓爬蟲飛一會,我這邊拿到了80000+條資料
好了,列表資料有了,接下來,我們繼續操作這個資料,是不是感覺速度有點慢,程式碼寫的有點LOW,好吧,我承認這是給新手寫的 其實就是懶 ,我回頭在用一篇文章把他給改成面向物件和多執行緒的
我們接下來基於爬取到的資料再次進行分析
例如 www.moko.cc/post/nimusi… 這個頁面中,我們需要獲取到,紅色框框的地址,為什麼要或者這個?因為點選這個圖片之後進入裡面才是完整的圖片列表。
我們還是應用爬蟲獲取 幾個步驟
- 迴圈我們剛才的資料列表
- 抓取網頁原始碼
- 正則表示式匹配所有的連結
def read_list_data(): # 讀取資料 img_list = pd.read_csv("./pages.csv",names=["no","url"])["url"] # 迴圈操作資料 for img_list_page in img_list: try: response = requests.get(img_list_page,headers=headers,timeout=3) except Exception as e: print(e) continue # 正則表示式獲取圖片列表頁面 pattern = re.compile('<a hidefocus="ture" alt="(.*?)".*? href="(.*?)".*?>VIEW MORE</a>') img_box = pattern.findall(response.text) need_links = [] # 待抓取的圖片資料夾 for img in img_box: need_links.append(img) # 建立目錄 file_path = "./downs/{}".format(str(img[0]).replace('/', '')) if not os.path.exists(file_path): os.mkdir(file_path) # 建立目錄 for need in need_links: # 獲取詳情頁面圖片連結 get_my_imgs(base_url.format(need[1]), need[0]) 複製程式碼
上面程式碼幾個重點地方
pattern = re.compile('<a hidefocus="ture" alt="(.*?)".*? href="(.*?)".*?>VIEW MORE</a>') img_box = pattern.findall(response.text) need_links = [] # 待抓取的圖片資料夾 for img in img_box: need_links.append(img) 複製程式碼
獲取到抓取目錄,這個地方,我匹配了兩個部分,主要用於建立資料夾 建立資料夾需要用到 os 模組,記得匯入一下
# 建立目錄 file_path = "./downs/{}".format(str(img[0]).replace('/', '')) if not os.path.exists(file_path): os.mkdir(file_path) # 建立目錄 複製程式碼
獲取到詳情頁面圖片連結之後,在進行一次訪問抓取所有圖片連結
#獲取詳情頁面資料 def get_my_imgs(img,title): print(img) headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"} response = requests.get(img, headers=headers, timeout=3) pattern = re.compile('<img src2="(.*?)".*?>') all_imgs = pattern.findall(response.text) for download_img in all_imgs: downs_imgs(download_img,title) 複製程式碼
最後編寫一個圖片下載的方法,所有的程式碼完成,圖片儲存本地的地址,用的是時間戳。
def downs_imgs(img,title): headers ={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"} response = requests.get(img,headers=headers,timeout=3) content = response.content file_name = str(int(time.time()))+".jpg" file = "./downs/{}/{}".format(str(title).replace('/','').strip(),file_name) with open(file,"wb+") as f: f.write(content) print("完畢") 複製程式碼
執行程式碼,等著收圖
程式碼執行一下,發現報錯了
原因是路徑的問題,在路徑中出現了...這個特殊字元,我們需要類似上面處理 / 的方式處理一下。自行處理一下吧。
資料獲取到,就是這個樣子的
程式碼中需要完善的地方
- 程式碼分成了兩部分,並且是面向過程的,非常不好,需要改進
- 網路請求部分重複程式碼過多,需要進行抽象,並且加上錯誤處理,目前是有可能報錯的
- 程式碼單執行緒,效率不高,可以參照前兩篇文章進行改進
- 沒有模擬登入,最多隻能爬取6個圖片,這也是為什麼先把資料儲存下來的原因,方便後期直接改造