新手入門Python小爬蟲-一步一步學會爬取今日頭條街拍美女圖

文中代碼只是部分講解,完整的代碼放在文章末尾地址。

先實際感受一下我們要抓取的福利是什麼?點擊 今日頭條,在搜索欄輸入街拍 兩個字,點開任意一篇文章,裡面的圖片即是我們要抓取的內容。

新手入門Python小爬蟲-一步一步學會爬取今日頭條街拍美女圖

可以看到搜索結果默認返回了 20 篇文章,當頁面滾動到底部時頭條通過 ajax 加載更多文章,瀏覽器按下 F12 打開調試工具(我的是 Chrome),點擊 Network 選項,嘗試加載更多的文章,可以看到相關的 http 請求:

新手入門Python小爬蟲-一步一步學會爬取今日頭條街拍美女圖

可以看到請求的 URL(Request URL)為:http://www.toutiao.com/search_content/, 其請求參數為:

新手入門Python小爬蟲-一步一步學會爬取今日頭條街拍美女圖

很容易猜測 offset 表示偏移量,即已經請求的文章數;format 為返回格式,這裡返回的是 json 格式的數據;keyword 是我們的搜索關鍵字;autoload 應該是自動加載的指示標誌,無關緊要;count 為請求的新文章數量;_ 應該是請求發起時的時間戳。將請求的 URL 和這些查詢參數拼接即組成完整的 Request URL,例如這次的 Request URL 是: http://www.toutiao.com/search_content/?offset=20&format=json&keyword=%E8%A1%97%E6%8B%8D&autoload=true&count=20&_=1480675595492。

先讓我們來看看這個請求為我們返回了什麼樣的數據。

import json

from pprint import pprint

from urllib import request

url = "http://www.toutiao.com/search_content/?offset=20&format=json&keyword=%E8%A1%97%E6%8B%8D&autoload=true&count=20&_=1480675595492"

with request.urlopen(url) as res:

d = json.loads(res.read().decode())

print(d)

這裡我們首先通過 request.urlopen(url) 向這個 url 發送請求,返回的數據保存在 res 中,res 是一個 HttpResponse 對象,通過調用其 read 方法獲取實際返回的內容,由於 read 方法返回的是 Python 的 bytes 類型的字符串,通過調用其 decode 方法將其編碼成 string 類型字符串,默認為 UTF-8 編碼。由於數據以 json 格式返回,因此通過 json.load 方法將其轉為 Python 的字典形式。

打印出這個字典,可以看到字典中有一個鍵 ‘data’ 對應著一個由字典組成的列表的值,分析可知這個值就是返回的全部文章的數據列表,稍微修改一下代碼,來看看 ‘data’ 對應的值是什麼樣的:

with request.urlopen(url) as res:

d = json.loads(res.read().decode())

d = d.get('data')

pprint(d)

這裡使用了 pprint 讓字典打印的出來的值更加的格式化,便於分析。可以看到這是一個由字典組成的列表,列表的每一個項代表一篇文章,包含了文章的全部基本數據,例如標題,文章的 URL 等。於是我們可以通過如下的方式來獲取我們本次請求的全部文章的 URL 列表:

urls = [article.get('article_url') for article in d if article.get('article_url')]

這裡使用了列表推導式,循環文章列表,通過 get('article_url') 獲取到文章的 URL,加上 if 判斷條件是為了防止因為數據缺失而得到空的文章 URL。我們將通過不斷請求這些文章的 URL,讀取其內容,並把圖片提取出來保存到我們的硬盤裡。

先來處理一篇文章,看看我們如何把文章裡的全部圖片提取出來。

隨便點開一個文章鏈接,按 F12 查看網頁源代碼,可以看到文章的主體部分位於一個 id="article-main"

的 div 裡。這個 div 下有 h1 標籤表示文章標題,另外一系列 img 標籤,其 src 屬性即保存著圖片所在的鏈接,於是我們通過訪問這些鏈接把圖片下載下來,看看具體怎麼做:

url = "http://www.toutiao.com/a6351879148420235522/"

with request.urlopen(url) as res:

soup = BeautifulSoup(res.read().decode(errors='ignore'), 'html.parser')

article_main = soup.find('div', id='article-main')

photo_list = [photo.get('src') for photo in article_main.find_all('img') if photo.get('src')]

print(photo_list)

# 輸出:

['http://p9.pstatp.com/large/111200020f54729cd558', 'http://p3.pstatp.com/large/11100005d3e8b9e69a88', 'http://p3.pstatp.com/large/106b00058387c12351c7', ...]

這裡我們請求文章的 URL,將返回的內容(html)傳遞給 BeautifulSoup 為我們做解析。通過 find 方法找到 article-main 對應的 div 塊,在該 div 塊下繼續使用 find_all 方法搜尋全部的 img 標籤,並提取其 src 屬性對應的值,於是我們便獲得了該文章下全部圖片的 URL 列表。 現在要做的就是繼續請求這些圖片的 URL,並把返回的圖片數據保存到硬盤裡。以一張圖片示例:

photo_url = "http://p9.pstatp.com/large/111200020f54729cd558"

photo_name = photo_url.rsplit('/', 1)[-1] + '.jpg'

with request.urlopen(photo_url) as res, open(photo_name, 'wb') as f:

f.write(res.read())

此時就可以在當前目錄下看到我們保存下來的圖片了。這裡我們使用了 URL 最後一段的數字做為圖片的文件名,並將其保存為 jpg 的格式。

基本步驟就是這麼多了,整理下爬取流程:

  1. 指定查詢參數,向 http://www.toutiao.com/search_content/ 提交我們的查詢請求。
  2. 從返回的數據(JSON 格式)中解析出全部文章的 URL,分別向這些文章發送請求。
  3. 從返回的數據(HTML 格式)提取出文章的標題和全部圖片鏈接。
  4. 再分別向這些圖片鏈接發送請求,將返回的圖片輸入保存到本地。
  5. 修改查詢參數,以使服務器返回新的文章數據,繼續第一步。

運行此代碼後一共爬取了大概 1000 多張圖片:

完整項目地址:https://github.com/zmrenwu/toutiao.git


分享到:


相關文章: