【Python】下載所有 XKCD 漫畫


博客和其他經常更新的網站通常有一個首頁,其中有最新的帖子,以及一個“前 一篇”按鈕,將你帶到以前的帖子。然后那個帖子也有一個“前一篇”按鈕,以此 類推。這創建了一條線索,從最近的頁面,直到該網站的第一個帖子。如果你希望 拷貝該網站的內容,在離線的時候閱讀,可以手工導航至每個頁面並保存。但這是 很無聊的工作,所以讓我們寫一個程序來做這件事。 XKCD 是一個流行的極客漫畫網站,它符合這個結構(參見圖 11-6)。首頁 http://xkcd.com/有一個“Prev”按鈕,讓用戶導航到前面的漫畫。手工下載每張漫 畫要花較長的時間,但你可以寫一個腳本,在幾分鍾內完成這件事。 下面是程序要做的事:

• 加載主頁;

• 保存該頁的漫畫圖片;

• 轉入前一張漫畫的鏈接;

• 重復直到第一張漫畫。

這意味着代碼需要做下列事情:

• 利用 requests 模塊下載頁面。

• 利用 Beautiful Soup 找到頁面中漫畫圖像的 URL。

• 利用 iter_content()下載漫畫圖像,並保存到硬盤。

• 找到前一張漫畫的鏈接 URL,然后重復。

打開一個新的文件編輯器窗口,將它保存為 downloadXkcd.py。

設計程序

打開一個瀏覽器的開發者工具,檢查該頁面上的元素,你會發現下面的內容:

• 漫畫圖像文件的 URL,由一個<img>元素的 href 屬性給出。

• <img>元素在<div id="comic">元素之內。

• Prev 按鈕有一個 rel HTML 屬性,值是 prev。

• 第一張漫畫的Prev按鈕鏈接到 http://xkcd.com/# URL,表明沒有前一個頁面了。
讓你的代碼看起來像這樣:

#! python3
# downloadXkcd.py - Downloads every single XKCD comic.

import requests, os, bs4

url = 'https://xkcd.com' # starting url
os.makedirs('xkcd', exist_ok=True) # store comics in ./xkcd
while not url.endswith('#'):
# TODO: Download the page.

# TODO: Find the URL of the comic image.

# TODO: Download the image.

# TODO: Save the image to ./xkcd

# TODO: Get the Prev button's url.

print('Done')

 


你會有一個 url 變量,開始的值是'http://xkcd.com',然后反復更新(在一個 for 循環中),變成當前頁面的 Prev 鏈接的 URL。在循環的每一步,你將下載 URL 上 的漫畫。如果 URL 以'#'結束,你就知道需要結束循環。 將圖像文件下載到當前目錄的一個名為 xkcd 的文件夾中。調用 os.makedirs() 函數。確保這個文件夾存在,並且關鍵字參數 exist_ok=True 在該文件夾已經存在時, 防止該函數拋出異常。剩下的代碼只是注釋,列出了剩下程序的大綱。

下載網頁

我們來實現下載網頁的代碼。讓你的代碼看起來像這樣:

#! python3
# downloadXkcd.py - Downloads every single XKCD comic.

import requests, os, bs4

url = 'https://xkcd.com' # starting url
os.makedirs('xkcd', exist_ok=True) # store comics in ./xkcd
while not url.endswith('#'):
# Download the page.
print('Downloading page %s...' % url)
res = requests.get(url)
res.raise_for_status()

soup = bs4.BeautifulSoup(res.text)

# TODO: Find the URL of the comic image.

# TODO: Download the image.

# TODO: Save the image to ./xkcd

# TODO: Get the Prev button's url.

print('Done')

 


首先,打印 url,這樣用戶就知道程序將要下載哪個 URL。然后利用 requests 模塊的 request.get()函數下載它。像以往一樣,馬上調用 Response對象的 raise_for_status()方法, 如果下載發生問題,就拋出異常,並終止程序。否則,利用下載頁面的文本創建一 個 BeautifulSoup 對象。

尋找和下載漫畫圖像

讓你的代碼看起來像這樣:

#! python3
# downloadXkcd.py - Downloads every single XKCD comic.

import requests, os, bs4

--snip--

# Find the URL of the comic image.
comicElem = soup.select('#comic img')
if comicElem == []:
print('Could not find comic image.')
else:
comicUrl = 'https:' + comicElem[0].get('src')
# Download the image.
print('Downloading iamge %s...' % (comicUrl))
res = requests.get(comicUrl)
res.raise_for_status()

# TODO: Save the image to ./xkcd

# TODO: Get the Prev button's url.

print('Done')

 


用開發者工具檢查 XKCD 主頁后,你知道漫畫圖像的<img>元素是在一個<div>元 素中,它帶有的 id 屬性設置為 comic。所以選擇器'#comic img'將從 BeautifulSoup 對象中選出正確的<img>元素。 有一些 XKCD 頁面有特殊的內容,不是一個簡單的圖像文件。這沒問題,跳過它們 就好了。如果選擇器沒有找到任何元素,那么 soup.select('#comic img')將返回一個空的列 表。出現這種情況時,程序將打印一條錯誤消息,不下載圖像,繼續執行。 否則,選擇器將返回一個列表,包含一個<img>元素。可以從這個<img>元素中 取得 src 屬性,將它傳遞給 requests.get(),下載這個漫畫的圖像文件。

保存圖像,找到前一張漫畫

讓你的代碼看起來像這樣:

#! python3
# downloadXkcd.py - Downloads every single XKCD comic.

import requests, os, bs4

--snip--

# Save the image to ./xkcd
imageFile = open(os.path.join('xkcd', os.path.basename(comicUrl)), 'wb')
for chunk in res.iter_content(100000):
imageFile.write(chunk)

# Get the Prev button's url.
prevLink = soup.select('a[rel="prev"]')[0]
url = 'http://xkcd.com' + prevLink.get('href')

print('Done')

 


這時,漫畫的圖像文件保存在變量 res 中。你需要將圖像數據寫入硬盤的文件。 你需要為本地圖像文件准備一個文件名,傳遞給 open()。comicUrl 的值類似 'http://imgs.xkcd.com/comics/heartbleed_explanation.png'。你可能注意到,它看起來很 像文件路徑。實際上,調用 os.path.basename()時傳入 comicUrl,它只返回 URL 的 最后部分:'heartbleed_explanation.png'。你可以用它作為文件名,將圖像保存到硬 盤。用 os.path.join()連接這個名稱和 xkcd 文件夾的名稱,這樣程序就會在 Windows 下使用倒斜杠(\),在 OS X 和 Linux 下使用斜杠(/)。既然你最后得到了文件名, 就可以調用 open(),用'wb'模式打開一個新文件。 回憶一下本章早些時候,保存利用 Requests 下載的文件時,你需要循環處理 iter_content()方法的返回值。for 循環中的代碼將一段圖像數據寫入文件(每次最多 10 萬字節),然后關閉該文件。圖像現在保存到硬盤中。 然后,選擇器'a[rel="prev"]'識別出rel 屬性設置為 prev 的<a>元素,利用這個<a> 元素的 href 屬性,取得前一張漫畫的 URL,將它保存在 url 中。然后 while 循環針 對這張漫畫,再次開始整個下載過程。 這個程序的輸出看起來像這樣:

Downloading page http://xkcd.com...
Downloading image http://imgs.xkcd.com/comics/phone_alarm.png...
Downloading page http://xkcd.com/1358/...
Downloading image http://imgs.xkcd.com/comics/nro.png...
Downloading page http://xkcd.com/1357/...
Downloading image http://imgs.xkcd.com/comics/free_speech.png...
Downloading page http://xkcd.com/1356/...
Downloading image http://imgs.xkcd.com/comics/orbital_mechanics.png...
Downloading page http://xkcd.com/1355/...
Downloading image http://imgs.xkcd.com/comics/airplane_message.png...
Downloading page http://xkcd.com/1354/...
Downloading image http://imgs.xkcd.com/comics/heartbleed_explanation.png...
--snip--
這個項目是一個很好的例子,說明程序可以自動順着鏈接,從網絡上抓取大量 的數據。你可以從 Beautiful Soup 的文檔了解它的更多功能:https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/#

類似程序的想法

下載頁面並追蹤鏈接,是許多網絡爬蟲程序的基礎。類似的程序也可以做下面的事情:
• 順着網站的所有鏈接,備份整個網站。

• 拷貝一個論壇的所有信息。

• 復制一個在線商店中所有產品的目錄。

requests 和 BeautifulSoup 模塊很了不起,只要你能弄清楚需要傳遞給 requests.get() 的 URL。但是,有時候這並不容易找到。或者,你希望編程瀏覽的網站可能要求你先 登錄。selenium 模塊將讓你的程序具有執行這種復雜任務的能力。

完整代碼

#! python3
# downloadXkcd.py - Downloads every single XKCD comic.

import requests, os, bs4

url = 'https://xkcd.com' # starting url
os.makedirs('xkcd', exist_ok=True) # store comics in ./xkcd
while not url.endswith('#'):
# Download the page.
print('Downloading page %s...' % url)
res = requests.get(url)
res.raise_for_status()

soup = bs4.BeautifulSoup(res.text)

# Find the URL of the comic image.
comicElem = soup.select('#comic img')
if comicElem == []:
print('Could not find comic image.')
else:
comicUrl = 'https:' + comicElem[0].get('src')
# Download the image.
print('Downloading iamge %s...' % (comicUrl))
res = requests.get(comicUrl)
res.raise_for_status()

# Save the image to ./xkcd
imageFile = open(os.path.join('xkcd', os.path.basename(comicUrl)), 'wb')
for chunk in res.iter_content(100000):
imageFile.write(chunk)

# Get the Prev button's url.
prevLink = soup.select('a[rel="prev"]')[0]
url = 'http://xkcd.com' + prevLink.get('href')

print('Done')

 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM