標籤:

【Python爬蟲實戰】——爬取今日頭條美女圖片

筆者是頭條的深度使用者,經常用頭條完成「看片」大業。若不信的話可以試試在頭條搜索街拍,返回的都是一道道靚麗的風景線。

想把圖片存下來,該怎麼辦呢?我們可以用Python爬蟲啊。

人生苦短,我用Python!

1、工具

Python3.5,Sublime Text,Windows 7

2、分析(第三步有完整代碼)

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

此次返回Request URL:

toutiao.com/search_cont

來試試返回了什麼

import jsonnfrom urllib import requestnnurl = "http://www.toutiao.com/search_content/?offset=20&format=json&keyword=%E8%A1%97%E6%8B%8D&autoload=true&count=20&cur_tab=1"nnwith request.urlopen(url) as res:n d = json.loads(res.read().decode())n print(d)n

發現我們需要的東西在data里,打開一篇文章,來試試如何下載單篇圖片。

import jsonnfrom urllib import requestnnurl = http://www.toutiao.com/a6314996711535444226/#p=1nnwith request.urlopen(url) as res:n soup = BeautifulSoup(res.read().decode(errors=ignore), html.parser)n article_main = soup.find(div, id=article-main)n photo_list = [photo.get(src) for photo in article_main.find_all(img) if photo.get(src)]n print(photo_list)n

輸出

[p3.pstatp.com/large/159, p1.pstatp.com/large/153, p3.pstatp.com/large/159, p1.pstatp.com/large/153]

首先用BeautifulSoup解析網頁,通過 find 方法找到 article-main 對應的 div 塊,在該 div 塊下繼續使用 find_all 方法搜尋全部的 img 標籤,並提取其 src 屬性對應的值,於是我們便獲得了該文章下全部圖片的 URL 列表。

接下來就是保存圖片。

photo_url = "http://p3.pstatp.com/large/159f00010b30d6736512"nphoto_name = photo_url.rsplit(/, 1)[-1] + .jpgnnwith request.urlopen(photo_url) as res, open(photo_name, wb) as f:n f.write(res.read())n

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

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

3、完整代碼

import renimport jsonnimport timenimport randomnnfrom pathlib import Pathnfrom urllib import parsenfrom urllib import errornfrom urllib import requestnfrom datetime import datetimenfrom http.client import IncompleteReadnfrom socket import timeout as socket_timeoutnnfrom bs4 import BeautifulSoupnnndef _get_timestamp():n """n 向 http://www.toutiao.com/search_content/ 發送的請求的參數包含一個時間戳,n 該函數獲取當前時間戳,並格式化成頭條接收的格式。格式為 datetime.today() 返回n 的值去掉小數點後取第一位到倒數第三位的數字。n """n row_timestamp = str(datetime.timestamp(datetime.today()))n return row_timestamp.replace(., )[:-3]nnndef _create_dir(name):n """n 根據傳入的目錄名創建一個目錄,這裡用到了 python3.4 引入的 pathlib 庫。n """n directory = Path(name)n if not directory.exists():n directory.mkdir()n return directorynnndef _get_query_string(data):n """n 將查詢參數編碼為 url,例如:n data = {n offset: offset,n format: json,n keyword: 街拍,n autoload: true,n count: 20,n _: 1480675595492n }n 則返回的值為:n ?offset=20&format=json&keyword=%E8%A1%97%E6%8B%8D&autoload=true&count=20&_=1480675595492"n """n return parse.urlencode(data)nnndef get_article_urls(req, timeout=10):n with request.urlopen(req, timeout=timeout) as res:n d = json.loads(res.read().decode()).get(data)nn if d is None:n print("數據全部請求完畢...")n returnnn urls = [article.get(article_url) for article in d if article.get(article_url)]n return urlsnnndef get_photo_urls(req, timeout=10):n with request.urlopen(req, timeout=timeout) as res:n # 這裡 decode 默認為 utf-8 編碼,但返回的內容中含有部分非 utf-8 的內容,會導致解碼失敗n # 所以我們使用 ignore 忽略這部分內容n soup = BeautifulSoup(res.read().decode(errors=ignore), html.parser)n article_main = soup.find(div, id=article-main)nn if not article_main:n print("無法定位到文章主體...")n returnnn heading = article_main.h1.stringnn if 街拍 not in heading:n print("這不是街拍的文章!!!")n returnnn img_list = [img.get(src) for img in article_main.find_all(img) if img.get(src)]n return heading, img_listnnndef save_photo(photo_url, save_dir, timeout=10):n photo_name = photo_url.rsplit(/, 1)[-1] + .jpgnn # 這是 pathlib 的特殊操作,其作用是將 save_dir 和 photo_name 拼成一個完整的路徑。例如:n # save_dir = E:jiepain # photo_name = 11125841455748.jpgn # 則 save_path = E:jiepai11125841455748.jpgn save_path = save_dir / photo_namenn with request.urlopen(photo_url, timeout=timeout) as res, save_path.open(wb) as f:n f.write(res.read())n print(已下載圖片:{dir_name}/{photo_name},請求的 URL 為:{url}n .format(dir_name=dir_name, photo_name=photo_name, url=a_url))nnnif __name__ == __main__:n ongoing = Truen offset = 0 # 請求的偏移量,每次累加 20n root_dir = _create_dir(E:jiepai) # 保存圖片的根目錄n request_headers = {n Referer: http://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D,n User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36n }nn while ongoing:n timestamp = _get_timestamp()n query_data = {n offset: offset,n format: json,n keyword: 街拍,n autoload: true,n count: 20, # 每次返回 20 篇文章n _: timestampn }n query_url = http://www.toutiao.com/search_content/ + ? + _get_query_string(query_data)n article_req = request.Request(query_url, headers=request_headers)n article_urls = get_article_urls(article_req)nn # 如果不再返回數據,說明全部數據已經請求完畢,跳出循環n if article_urls is None:n breaknn # 開始向每篇文章發送請求n for a_url in article_urls:n # 請求文章時可能返回兩個異常,一個是連接超時 socket_timeout,n # 另一個是 HTTPError,例如頁面不存在n # 連接超時我們便休息一下,HTTPError 便直接跳過。n try:n photo_req = request.Request(a_url, headers=request_headers)n photo_urls = get_photo_urls(photo_req)nn # 文章中沒有圖片?跳到下一篇文章n if photo_urls is None:n continuenn article_heading, photo_urls = photo_urlsnn # 這裡使用文章的標題作為保存這篇文章全部圖片的目錄。n # 過濾掉了標題中在 windows 下無法作為目錄名的特殊字元。n dir_name = re.sub(r[/:*?"<>|], , article_heading)n download_dir = _create_dir(root_dir / dir_name)nn # 開始下載文章中的圖片n for p_url in photo_urls:n # 由於圖片數據以分段形式返回,在接收數據時可能拋出 IncompleteRead 異常n try:n save_photo(p_url, save_dir=download_dir)n except IncompleteRead as e:n print(e)n continuen except socket_timeout:n print("連接超時了,休息一下...")n time.sleep(random.randint(15, 25))n continuen except error.HTTPError:n continuenn # 一次請求處理完畢,將偏移量加 20,繼續獲取新的 20 篇文章。n offset += 20n

文章參考Python 福利小爬蟲,爬取今日頭條街拍美女圖

同理,只需修改代碼,就可以下載想要的關鍵詞,自己動手,想啥有啥。

打個廣告,尋找喜歡爬蟲的小夥伴。

打算爬取頭條的評論,因為頭條評論比正文好看。

既然有人要圖,好吧。。只有下載的一部分圖片(?? . ??)

鏈接:pan.baidu.com/s/1qYuD20 密碼:tr88


推薦閱讀:

爬蟲軟體|軟體的簡單使用(二)
數據採集技術指南 第一篇 技術棧總覽
校長,我要上車——python模擬登錄熊貓TV
從零開始寫Python爬蟲 --- 3.1 Selenium模擬瀏覽器
Trip: 給Requests加上協程,一百份網路請求一份時間

TAG:Python | 爬虫 |