11.7 项目:下载所有 XKCD 漫画

博客和其他经常更新的网站通常有一个首页,其中有最新的帖子,以及一个“前
一篇”按钮,将你带到以前的帖子。然后那个帖子也有一个“前一篇”按钮,以此类推。这创建了一条线索,从最近的页面,直到该网站的第一个帖子。如果你希望拷贝该网站的内容,在离线的时候阅读,可以手工导航至每个页
面并保存。但这是很无聊的工作,所以让我们写一个程序来做这件事。
XKCD 是一个流行的极客漫画网站,它符合这个结构(参见图 11-6)。首页 
http://xkcd.com/有一个“Prev”按钮,让用户导航到前面的漫画。手工下载每张漫画要花较长的时间,但你可以写一个脚本,在几分钟内完成这件事。
下面是程序要做的事:
•    加载主页;
•    保存该页的漫画图片;
•    转入前一张漫画的链接;
•    重复直到第一张漫画。

 

这意味着代码需要做下列事情:
•    利用 requests 模块下载页面。
•    利用Beautiful Soup 找到页面中漫画图像的 URL。
•    利用 iter_content()下载漫画图像,并保存到硬盘。
•    找到前一张漫画的链接 URL,然后重复。
打开一个新的文件编辑器窗口,将它保存为downloadXkcd.py。

第 1 步:设计程序
打开一个浏览器的开发者工具,检查该页面上的元素,你会发现下面的内容:
•    漫画图像文件的 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  =  'http://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 
在该文件夹已经存在时,防止该函数抛出异常。剩下的代码只是注释,列出了剩下程序的大纲。

第 2 步:下载网页
我们来实现下载网页的代码。让你的代码看起来像这样:
#!  python3
#  downloadXkcd.py  -  Downloads  every  single  XKCD  comic. import  requests,  os,  bs4
url  =  'http://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 对象。

第 3 步:寻找和下载漫画图像
让你的代码看起来像这样:

#!  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  =  'http:'  comicElem[0].get('src') #  Download  the  image.
print('Downloading  image  %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(),下载这个漫画的图像文件。

第 4 步:保存图像,找到前一张漫画
让你的代码看起来像这样:
#!  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) imageFile.close()

#  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 的文档了解它的更多功能:http://www.  crummy.com/ 
software/BeautifulSoup/bs4/doc/.

第 5 步:类似程序的想法
下载页面并追踪链接,是许多网络爬虫程序的基础。类似的程序也可以做下面的事情:
•    顺着网站的所有链接,备份整个网站。
•    拷贝一个论坛的所有信息。
•    复制一个在线商店中所有产品的目录。
requests 和BeautifulSoup 模块很了不起,只要你能弄清楚需要传递给 requests.get()的 
URL。但是,有时候这并不容易找到。或者,你希望编程浏览的网站可能要求你先登录。selenium 模块将让你的程序具有执行这种复杂任务的能力。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大飞哥软件自习室

希望支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值