文章目录
可能需要导入的包
import time
import os
import re
import requests
from fake_useragent import UserAgent
from lxml import html as lxml_html
from urllib import parse
from bs4 import BeautifulSoup
1.查看网站结构
1.1 获取网站response信息
必应壁纸的网站应该是把用f12打开工作台,右键这类的操作通过js禁止了,不过依旧可以通过各种方式来查看网页结构。
现在确定了,能够获取图片信息的请求地址为https://bing.ioliu.cn/?p=1,最好把html下来,以此方便之后解析的测试。
# 得到response中的html文件代码
def get_html():
ua = UserAgent()
url = f"https://bing.ioliu.cn/?p=1"
# 请求头
headers = {
"User-Agent": ua.random,
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Host": "bing.ioliu.cn",
}
response = requests.get(url=url, headers=headers)
rs_content = response.content.decode('utf8')
print(rs_content)
with open('./bing_wallpaper.html', 'w+', encoding='utf-8') as fp:
fp.write(rs_content)
print('ok')
get_html()
1.2 观察规律
图片地址的示例如下:
http://h1.ioliu.cn/bing/AlgonquinGrouse_ZH-CN2514966091_1920x1080.jpg
http://h1.ioliu.cn/bing/LastJedi_ZH-CN8789881870_1920x1080.jpg
"""
请求地址
https://bing.ioliu.cn/
https://bing.ioliu.cn/?p=1"
https://bing.ioliu.cn/?p=2"
https://bing.ioliu.cn/?p=3"
壁纸详情地址
https://bing.ioliu.cn/photo/LastJedi_ZH-CN8789881870?force=home_1
图片地址
http://h1.ioliu.cn/bing/AlgonquinGrouse_ZH-CN2514966091_1920x1080.jpg
http://h1.ioliu.cn/bing/LastJedi_ZH-CN8789881870_1920x1080.jpg
"""
结论: 每个图片都有一个id(img_id),例如,LastJedi_ZH-CN8789881870,然后其壁纸1920x1080的拼接规律为:
http://h1.ioliu.cn/bing/LastJedi_ZH-CN8789881870_1920x1080.jpg
http://h1.ioliu.cn/bing/<img_id>_1920x1080.jpg
由于图片地址没有固定规律,应该通过有规律的请求地址获取html代码,再清洗提炼出图片地址
2.提取目标图片链接
2.1 图片url提取思路
经过观察,目前至少有两种方式来提取到:
① 先获取图片的img_id,然后拼接
② 直接在html中提取完整的图片url
2.2 基于正则表达式提取
def extract01(html):
'''
方式: 用xpath提取壁纸链接
:param html: str 需要清洗的html
:return: dict {图片名:图片url}
'''
img_url_list = re.findall('pic=(.*?)\\.jpg', html)
img_title_list = re.findall('<div class="description"><h3>(.*?)</h3>', html)
treated_img_url_list = []
for img_url in img_url_list:
treated_img_url_list.append(img_url + '.jpg')
treated_img_title_list = []
for img_title in img_title_list:
treated_img_title_list.append(img_title.split(' ')[0])
img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
print(img_dict)
return img_dict
结果:
{'极北朱顶雀的巢,芬兰拉普兰': 'http://h1.ioliu.cn/bing/ArcticRedpoll_ZH-CN7968973967_1920x1080.jpg',
'普尔曼附近的帕卢斯一辆拖拉机在耕作时扬起尘土,华盛顿州': 'http://h1.ioliu.cn/bing/PalouseSpring_ZH-CN6803103328_1920x1080.jpg',
'索尔茲伯里大教堂与放牧的羊群,英格兰': 'http://h1.ioliu.cn/bing/SalisburyCathedral_ZH-CN6366350896_1920x1080.jpg',
'一只南美貘幼崽小跑着穿过草地': 'http://h1.ioliu.cn/bing/SouthAmericanTapir_ZH-CN6151058361_1920x1080.jpg',
'红宝石海滩的日落,华盛顿州奥林匹克国家公园': 'http://h1.ioliu.cn/bing/RubySunset_ZH-CN5544596519_1920x1080.jpg',
'福克兰群岛上的南跳岩企鹅': 'http://h1.ioliu.cn/bing/FalklandRockhoppers_ZH-CN5370686595_1920x1080.jpg',
'由哈勃太空望远镜拍摄的大麦哲伦星云': 'http://h1.ioliu.cn/bing/MegellanicCloud_ZH-CN5132305226_1920x1080.jpg',
'正在游水的雄性王绒鸭,挪威特罗姆斯-芬马克郡': 'http://h1.ioliu.cn/bing/KingEider_ZH-CN3559595357_1920x1080.jpg',
'怀波瓦森林中一棵名为Te': 'http://h1.ioliu.cn/bing/KauriTree_ZH-CN3695568740_1920x1080.jpg',
'黄石国家公园的大棱镜泉,怀俄明州': 'http://h1.ioliu.cn/bing/GPS_ZH-CN5160095061_1920x1080.jpg',
'Micheldever': 'http://h1.ioliu.cn/bing/BluebellWood_ZH-CN8128422960_1920x1080.jpg',
'内斯特角灯塔上空的银河': 'http://h1.ioliu.cn/bing/NeistPoint_ZH-CN3115403132_1920x1080.jpg'}
2.3 基于xpath提取
def extract02(html):
'''
方式: 用xpath提取壁纸链接
:param html: str 需要清洗的html
:return: dict {图片名:图片url}
'''
etree = lxml_html.etree
html_parser = etree.HTML(html)
# 原始数据
# 获取图片url列表
img_url_list = html_parser.xpath('//a[@class="ctrl share"]/@href')
# 获取图片标题列表
img_title_list = html_parser.xpath('//div[@class="description"]/h3/text()')
# 清洗、提取url
treated_img_url_list = []
for img_url in img_url_list:
params = parse.parse_qs(parse.urlparse(img_url).query)
treated_img_url_list.append(params['pic'][0].replace('?imageslim', ''))
# 清洗、提取title来为图片命名(解决文件名过长、不能携带‘/’等情况)
treated_img_title_list = []
for img_title in img_title_list:
treated_img_title_list.append(
img_title.split(' ')[0]
)
img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
return img_dict
结果:
{'极北朱顶雀的巢,芬兰拉普兰': 'http://h1.ioliu.cn/bing/ArcticRedpoll_ZH-CN7968973967_1920x1080.jpg',
'普尔曼附近的帕卢斯一辆拖拉机在耕作时扬起尘土,华盛顿州': 'http://h1.ioliu.cn/bing/PalouseSpring_ZH-CN6803103328_1920x1080.jpg',
'索尔茲伯里大教堂与放牧的羊群,英格兰': 'http://h1.ioliu.cn/bing/SalisburyCathedral_ZH-CN6366350896_1920x1080.jpg',
'一只南美貘幼崽小跑着穿过草地': 'http://h1.ioliu.cn/bing/SouthAmericanTapir_ZH-CN6151058361_1920x1080.jpg',
'红宝石海滩的日落,华盛顿州奥林匹克国家公园': 'http://h1.ioliu.cn/bing/RubySunset_ZH-CN5544596519_1920x1080.jpg',
'福克兰群岛上的南跳岩企鹅': 'http://h1.ioliu.cn/bing/FalklandRockhoppers_ZH-CN5370686595_1920x1080.jpg',
'由哈勃太空望远镜拍摄的大麦哲伦星云': 'http://h1.ioliu.cn/bing/MegellanicCloud_ZH-CN5132305226_1920x1080.jpg',
'正在游水的雄性王绒鸭,挪威特罗姆斯-芬马克郡': 'http://h1.ioliu.cn/bing/KingEider_ZH-CN3559595357_1920x1080.jpg',
'怀波瓦森林中一棵名为Te': 'http://h1.ioliu.cn/bing/KauriTree_ZH-CN3695568740_1920x1080.jpg',
'黄石国家公园的大棱镜泉,怀俄明州': 'http://h1.ioliu.cn/bing/GPS_ZH-CN5160095061_1920x1080.jpg',
'Micheldever': 'http://h1.ioliu.cn/bing/BluebellWood_ZH-CN8128422960_1920x1080.jpg',
'内斯特角灯塔上空的银河': 'http://h1.ioliu.cn/bing/NeistPoint_ZH-CN3115403132_1920x1080.jpg'}
2.4 使用beautifulshop提取
def extract03(html):
'''
方式: 用BeautifulSoup提取壁纸链接
:param html: str 需要清洗的html
:return: dict {图片名:图片url}
'''
soup = BeautifulSoup(html, "html.parser")
img_url_list = soup.find_all("a", class_="ctrl share")
img_title_list = soup.find_all("div", class_="description")
treated_img_url_list = []
for one_a_class in img_url_list:
params = parse.parse_qs(parse.urlparse(one_a_class.attrs['href']).query)
treated_img_url_list.append(params['pic'][0].replace('?imageslim', ''))
# 清洗、提取title来为图片命名(解决文件名过长、不能携带‘/’等情况)
treated_img_title_list = []
for img_title in img_title_list:
treated_img_title_list.append(
str(img_title.h3.text).split(' ')[0]
)
img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
return img_dict
结果:
{'极北朱顶雀的巢,芬兰拉普兰': 'http://h1.ioliu.cn/bing/ArcticRedpoll_ZH-CN7968973967_1920x1080.jpg',
'普尔曼附近的帕卢斯一辆拖拉机在耕作时扬起尘土,华盛顿州': 'http://h1.ioliu.cn/bing/PalouseSpring_ZH-CN6803103328_1920x1080.jpg',
'索尔茲伯里大教堂与放牧的羊群,英格兰': 'http://h1.ioliu.cn/bing/SalisburyCathedral_ZH-CN6366350896_1920x1080.jpg',
'一只南美貘幼崽小跑着穿过草地': 'http://h1.ioliu.cn/bing/SouthAmericanTapir_ZH-CN6151058361_1920x1080.jpg',
'红宝石海滩的日落,华盛顿州奥林匹克国家公园': 'http://h1.ioliu.cn/bing/RubySunset_ZH-CN5544596519_1920x1080.jpg',
'福克兰群岛上的南跳岩企鹅': 'http://h1.ioliu.cn/bing/FalklandRockhoppers_ZH-CN5370686595_1920x1080.jpg',
'由哈勃太空望远镜拍摄的大麦哲伦星云': 'http://h1.ioliu.cn/bing/MegellanicCloud_ZH-CN5132305226_1920x1080.jpg',
'正在游水的雄性王绒鸭,挪威特罗姆斯-芬马克郡': 'http://h1.ioliu.cn/bing/KingEider_ZH-CN3559595357_1920x1080.jpg',
'怀波瓦森林中一棵名为Te': 'http://h1.ioliu.cn/bing/KauriTree_ZH-CN3695568740_1920x1080.jpg',
'黄石国家公园的大棱镜泉,怀俄明州': 'http://h1.ioliu.cn/bing/GPS_ZH-CN5160095061_1920x1080.jpg',
'Micheldever': 'http://h1.ioliu.cn/bing/BluebellWood_ZH-CN8128422960_1920x1080.jpg',
'内斯特角灯塔上空的银河': 'http://h1.ioliu.cn/bing/NeistPoint_ZH-CN3115403132_1920x1080.jpg'}
3.写入文件
图片的话,只需要以二进制的方式写入文件,并加上图片的后缀名,即可实现图片的写入
headers = {
"Accept": "image/webp,image/apng,image/*,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Cookie": "_ga=GA1.2.1461883180.1588215214; _gid=GA1.2.1769196419.1588545211; _gat_gtag_UA_61934506_5=1",
"Host": "h1.ioliu.cn",
"User-Agent": str(ua.random)
}
def download_pic(img_dict):
'''
根据img_dict下载并命名img文件
:param img_dict: dict
:return:
'''
try:
for img_name, img_url in img_dict.items():
res = requests.get(img_url, headers=self.headers_02)
with open(f"{self.dir}\\{img_name}.jpg", mode="wb") as f:
f.write(res.content)
print(f"{img_name}, {img_url}, 下载完成")
except Exception as e:
print(img_name)
print("下载出错", e)
4.形成爬虫
import re
import os
import requests
import time
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from urllib import parse
'''
https://bing.ioliu.cn/?p=1"
https://bing.ioliu.cn/?p=2"
https://bing.ioliu.cn/?p=3"
'''
class BingWallpaperSpider01(object):
def __init__(self, pages=1, dir_name='BingWallpaper'):
ua = UserAgent()
# 请求页面时使用的请求头
self.headers_01 = {
"User-Agent": str(ua.random),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "max-age=0",
"Host": "bing.ioliu.cn",
}
# 下载图片使用的请求头
self.headers_02 = {
"Accept": "image/webp,image/apng,image/*,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Connection": "keep-alive",
"Cookie": "_ga=GA1.2.1461883180.1588215214; _gid=GA1.2.1769196419.1588545211; _gat_gtag_UA_61934506_5=1",
"Host": "h1.ioliu.cn",
"User-Agent": str(ua.random)
}
self.pages = pages
self.dir = dir_name
# 创建图片存放的文件夹
if os.path.exists(self.dir):
print('目录已存在')
else:
os.mkdir(self.dir)
print('目录创建成功')
def get_res_content(self, page=1):
'''
获取请求内容
:param page: int 要爬取的页数 一页12张
:return: str res_content
'''
url = f"https://bing.ioliu.cn/?p={page}"
res = requests.get(url=url, headers=self.headers_01)
res_content = res.content.decode('utf8')
return res_content
def get_img_dict_01(self, html):
'''
方式: 用BeautifulSoup提取壁纸链接
:param html: 需要清洗的html
:return: dict {图片名:图片url}
'''
soup = BeautifulSoup(html, "html.parser")
img_url_list = soup.find_all("a", class_="ctrl share")
img_title_list = soup.find_all("div", class_="description")
treated_img_url_list = []
for one_a_class in img_url_list:
# 获取param中[pic]的值
params = parse.parse_qs(parse.urlparse(one_a_class.attrs['href']).query)
treated_img_url_list.append(params['pic'][0].replace('?imageslim', ''))
# 清洗、提取title来为图片命名(解决文件名过长、不能携带‘/’等情况)
treated_img_title_list = []
for img_title in img_title_list:
treated_img_title_list.append(
str(img_title.h3.text).split(' ')[0]
)
img_dict = dict(zip(treated_img_title_list, treated_img_url_list))
return img_dict
def download_pic(self, img_dict):
'''
根据img_dict下载并命名img文件
:param img_dict: dict
:return:
'''
try:
for img_name, img_url in img_dict.items():
res = requests.get(img_url, headers=self.headers_02)
with open(f"{self.dir}\\{img_name}.jpg", mode="wb") as f:
f.write(res.content)
print(f"{img_name}, {img_url}, 下载完成")
except Exception as e:
print(img_name)
print("下载出错", e)
def main(self):
i = 1
while i <= self.pages:
print(f"当前第{i}页,共需要下载{self.pages}页")
res_content = self.get_res_content(page=i)
pics = self.get_img_dict_01(html=res_content)
self.download_pic(pics)
i += 1
print("下载完成")
if __name__ == '__main__':
spider01 = BingWallpaperSpider01(pages=2)
spider01.main()
结果: