python3爬虫爬取必应每日高清壁纸

一、简介

必应是微软推出的搜索引擎,相比于百度具有广告少的显著优点,比较良心。以下为必应的网址:https://cn.bing.com/

经常使用必应应该可以发现,其主页每天都会更新一张图片,博主发现这些图片非常符合博主的审美,希望每天能够下载收藏每张图片。幸运的是已经有人完成了这项工作,具体请看这个网站:必应每日高清壁纸(https://bing.ioliu.cn/)。

这个网站收录了必应每天的主页图片,并且提供直接下载(管理猿太良心了,祝愿少掉一些头发,少写一些bug🤭🤭🤭)。但是博主发现这个网站缺少一个一键全部下载功能,只能一张一张图片手动下载,如果要把所有图片都下载下来,非常麻烦,因此用python写了一个下载网站上所有图片的小爬虫,分享给大家。

二、使用的环境

  • python3.8.1(较新版本都可)
  • requests库(需要使用pip工具下载该库)
  • re库(python自带,不用下载,直接导入就行)
  • bs4库(需要使用pip工具下载该库)

三、网页分析

1、分析网页每一页url形式以及总页数

在主页的最后可以看到有下一页的信息,如下图,
在这里插入图片描述
在这里可以收集到网页的最大页数信息,用以后续生成每一页的url链接。点击下一页,可以看到url变成:
在这里插入图片描述
容易推理出来对于不同页面网页的url形式为:

1
url = 'https://bing.ioliu.cn/?p={}'.format(page_count)

2、网页重要信息收集

打开必应每日高清壁纸,鼠标悬停在任意一张图片上面可以看到出现以下信息:
在这里插入图片描述
这其中对于我们较为重要的信息是图片介绍信息以及网页下载按钮所对应的url链接,因为我们需要把图片介绍信息作为下载图片的文件名,这样容易预览和区分。

3、在源码中寻找所需信息的位置

打开网页调试工具,打开任意一页,通过调试可以发现,每一张图片的信息主要在body标签class属性为container的div标签中:
在这里插入图片描述
打开div标签可以看到子节点并列存放着class属性为item的div标签,每一个子标签存放的就是每一张图片的信息。如下图:
在这里插入图片描述
这其中description属性的div标签存放的就是关于图片的描述信息,而options属性的div标签存放的就有图片的下载链接。如下图:
在这里插入图片描述
在这里插入图片描述
最后我们还需要知道网页的最大页面数的标签的位置,通过调试发现位于span标签下,如图:
在这里插入图片描述
找到了这些重要信息的位置之后,就可以开始敲代码了!

四、代码实现

代码主要包括五个函数:

  • GetHtmlText(url):根据传入的url链接发送http请求,并返回获取的数据,后面很多函数都要用到,这里为了防止被服务器检测到是爬虫,建议尽量多写一些附加信息,尽量模拟浏览器的浏览操作。
1
2
3
4
5
6
7
8
9
10
11
def GetHtmlText(url):
# 根据传入的url请求网站,并返回得到的数据
try:
user_agent = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'}
rspon = requests.get(url, headers = user_agent)
rspon.encoding = rspon.apparent_encoding
rspon.raise_for_status()
except:
print('网页获取失败:', rspon.status_code)
return None
return rspon
  • GetMaxPageCount()根据主页信息获得网页的最大页数,利用BeautifulSoup解析网页结构。
1
2
3
4
5
6
7
8
9
10
11
12
13
def GetMaxPageCount():
# 获取主页信息,并且获取网站的最大页数
max_page_count = 0
url = 'https://bing.ioliu.cn/'
soup = BeautifulSoup(GetHtmlText(url).text, "html.parser")
tag_page = soup.find('div', {'class':'page'})
page_txt = None
for tag_child in tag_page.children:
if(tag_child.name == 'span'):
page_txt = tag_child.string
match = re.search(r'(?<=1 / )\d*', page_txt)
max_page_count = int(match.group(0))
return max_page_count
  • SavePictureInUrl(pic_url,pic_name,pic_path):根据传入的url链接请求并获得图片的数据,然后根据文件名文件路径将图片数据写入到对应的文件中,这里需要注意的是图片属于二进制文件,因此要以二进制文件的方式打开和写入。
1
2
3
4
5
6
7
8
9
def SavePictureInUrl(pic_url,pic_name,pic_path):
# 根据传入的url链接获取图片的二进制数据,并且根据传入的路径和文件名将文件写入到对应的路径中。
source = GetHtmlText(pic_url)
if source == None:
return
file_name = '{}.jpg'.format(pic_name)
file = open(pic_path+file_name, "wb") #以二进制写的方式打开文件。
file.write(source.content)
file.close()
  • GetOnePageJpg(page_count, pic_path):根据当前传入的页码自动生成对应的url链接,然后请求并获得相应的数据,解析结构后获得图片的信息,拼接成文件的名称这里要注意windows规定某些特殊字符不能用作文件名,需要加以剔除,不然会出问题。然后获取图片的下载地址并拼接成完整的url地址,将这些信息传入以上函数就能获得图片。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def GetOnePageJpg(page_count, pic_path):
# 从返回的网页数据中获取每张图片的相关信息以及图片下载的url,然后调用相关函数下载图片
url = 'https://bing.ioliu.cn/?p={}'.format(page_count)
suop = BeautifulSoup(GetHtmlText(url).text, 'html.parser')
tag_container = suop.find_all('div', {'class':'container'})
tag_item = tag_container[1]
url_photo = 'https://bing.ioliu.cn'
for tag_pic in tag_item.children:
# 获取图片的标题和日期信息并且拼接成图片名
tag_title = tag_pic.find('h3')
text_title = tag_title.string
a = re.findall(r'[^\*"/:?\\|<>]', text_title, re.S) #剔除某些不能作为文件名的特殊字符
text_title = ''.join(a)
tag_calendar = tag_pic.find('p', {'class':'calendar'})
tag_em = tag_calendar.find('em')
text_calendar = tag_em.string
text_pic_name = text_calendar + '__' + text_title
# 获取图片的下载url
tag_download = tag_pic.find('a', {'class':'ctrl download'})
url_pic = url_photo + tag_download['href']
#信息保存到图片中
SavePictureInUrl(url_pic, text_pic_name, pic_path)
print('.', end='', flush=True) #输出进度信息
  • GetAllPageJpg(pic_path):传入文件的保存地址,然后获取所有网页的图片,主要是调用前面的函数。
1
2
3
4
5
6
def GetAllPageJpg(pic_path):
# 爬取所有的图片,并保存在输入的路径参数下
max_page_count = GetMaxPageCount()
for page_index in range(1, max_page_count):
GetOnePageJpg(page_index, pic_path)
print('\r', '正在获取,已完成:{:.2f} %'.format(page_index/max_page_count*100), end = '', flush=True) #输出进度信息
  • main():最后编写和调用main函数就能完成。
1
2
3
4
5
def main():
# 程序执行
pic_path = 'E://bing图片/' #文件保存路径
GetAllPageJpg(pic_path)
main() #执行main函数

五、运行爬虫

windows运行该脚本首先win+R打开运行,然后输入cmd打开控制台,将工作文件夹切换到脚本所在文件夹,然后输入命令:

1
python bingjpg.py

即可运行该脚本(前提是电脑已经安装好了python环境)。

万万没想到,这个网站居然有反爬机制?!脚本运行一段时间,下载了21.21%出现403错误🤣🤣🤣,仅仅下载了部分图片。
在这里插入图片描述
在这里插入图片描述
此时在浏览器打开网页也被禁止访问,应该是ip被禁了!!!不过目前大多数宽带都是使用DHCP服务生成动态ip,一般2个小时之后ip会自动更换,或者直接重启路由器也可以立即更换ip,更换了ip地址之后又能访问该网站。
在这里插入图片描述
针对这一情况推测网站很有可能是根据下载请求的速度以及数量来判断爬虫,从而封禁ip,后面可以利用前面已经写好的函数从后续页开始爬取,稍微改动代码就行,不用又从头开始。

六、后续改进

后续该爬虫还有两个方面可以改进:

  1. 爬取一次中断之后重新爬取时,之前爬取的图片又要重新爬取,费时费力,后面可以加一个判断,如果当前文件夹中已经存在该图片,就不再爬取,直接跳过。
  2. 适当降低爬取速度,每爬取一张图片之后sleep一段时间,防止被反爬程序检测到。