文章目录

一、背景介绍
XKCD网站是一个关于浪漫、隐喻、数字、以及语言的线上漫画。下面我们将使用Pyton中两个强大而常用的库Requests,Beautiful Soup 约30行代码一键下载网站所有漫画到本地。
官网地址:https://xkcd.com/
二、实现思路
官网首页有⼀个Prev按钮,让用户导航到前面的漫画。导航到前一个漫画也有⼀个Prev按钮,以此类推。这创建了⼀条线索,可以从最近的页面浏览到该网站的第⼀个页面。
程序需要完成以下任务。
1.加载XKCD主页。
2.保存该页的漫画图⽚。
3.转⼊前⼀张漫画的链接。
4.重复直到第⼀张漫画。
对应的代码结构
1.利用requests模块下载页⾯。
2.利用Beautiful Soup找到页⾯中漫画图像的URL。
3.利用iter_content()下载漫画图像,并保存到硬盘。
4.找到前一张漫画的URL链接,然后重复。
三、效果图
四、构思
打开官网首页,点击F12开发者工具,检查该页面上的元素。你会发现
代码结构:
import requests, os, bs4
url = ‘https://xkcd.com/’
os.makedirs('xkcd', exist_ok=True) # 新建一个用来保存下载图片的文件夹
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.')
五、实现细节
1. 第一步下载网页
# Download the page.
print('Downloading page %s...' % url)
res = requests.get(url)
res.raise_for_status()
soup = bs4.BeautifulSoup(res.text, 'html.parser')
说明:
首先,输出url,这样用户就知道程序将要下载哪个URL。
然后利用requests模块的request.get()函数下载它。
接着调用Response对象的raise_for_status()方法,
如果下载发发生问题,就抛出异常,并终止程序;
否则,利用下载页面的文本创建⼀个BeautifulSoup对象。
2. 寻找和下载漫画图像
# 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 image %s...' % (comicUrl))
res = requests.get(comicUrl)
res.raise_for_status()
说明:
用开发者工具检查XKCD主页后,发现漫画图像的<img>元素在<div>元素中,<div>带有的id属性设置为comic。选择器'#comic img'将从BeautifulSoup对象中选出正确的<img>元素。选择器将返回⼀个包含⼀个<img>元素的列表。可以从这个<img>元素中取得src属性,将src传递给requests.get(),以下载这个漫画的图像文件
3. 保存图像,找到前⼀张漫画
# 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 = 'https://xkcd.com/' + prevLink.get('href')
说明:
这时,漫画的图像文件保存在变量res中。你需要将图像数据写入硬盘的文件。
先为本地的图像文件准备⼀个文件名,并将其传递给open()。comicUrl的值类似'http://imgs.****/comics/heartbleed_explanation.png'。看起来很像文件路径。实际上,调用os.path.basename()时传⼊comicUrl,它只返回URL的最后部分:
'heartbleed_explanation.png'。当将图像保存到硬盘时,可以用它作为文件名。
用os.path.join()连接这个名称和xkcd文件夹的名称,这样程序就会在Windows操作系统下使用倒斜杠,在macOS和Linux操作系统下使用正斜杠(/)。
得到了文件名,就可以调用open(),并用'wb'(写⼆进制)模式打开⼀个新文件。
循环处理iter_content()方法的返回值。for循环中的代码将⼀段图像数据写入文件(每次最多10万字节),然后关闭该文件。图像现在保存到硬盘。选择器 'a[rel="prev"]' 识别出rel属性中设置为prev的<a>元素,利用这个<a>元素的href属性可取得前⼀张漫画的URL,然后将它保存在url中。接着,while循环针对这张漫画,再次开始整个下载过程。
六、完整代码
import requests, os, bs4
url = 'https://xkcd.com/'
os.makedirs('xkcd', exist_ok=True)
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, 'html.parser')
# Find the URL of the comic image.
comicElem = soup.select('#comic img')
if not comicElem:
print('Could not find comic image.')
else:
comicUrl = 'https:' + comicElem[0].get('src')
# Download the image.
print('Downloading image %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)
imageFile.close()
# Get the Prev button's url.
prevLink = soup.select('a[rel="prev"]')[0]
url = 'https://xkcd.com/' + prevLink.get('href')
print('Done.')
七、程序输出
八、附录
CSS选择器例子
传递给select()⽅法的选择器 | 将匹配…… |
soup.select('div') | 所有名为<div>的元素 |
soup.select('#author') | 带有id属性为author的元素 |
soup.select('.notice') | 所有使⽤CSS class属性名为notice的元素 |
soup.select('div span') | 所有在<div>元素之内的<span>元素 |
soup.select('div > span') | 所有直接在<div>元素之内的<span>元素,中间没有其他元素 |
soup.select('input[name]') | 所有名为<input>,并有⼀个name属性,其值⽆所谓的元素 |
soup.select('input[type="button"]') | 所有名为<input>,并有⼀个type属性,其值为button的元素 |
九、总结
这个项目是⼀个很好的例子,说明程序可以自动顺着链接从网络上抓取大量的数据。对Beautiful Soup的应用也有了简单认识。更多功能可以从Beautiful Soup的文档了解。
喜欢这篇文章的话记得点赞关注加收藏哦!