爬虫(一)——必应排行榜壁纸图片下载
实现的是一个爬取必应每日壁纸排行榜上的壁纸图片,下载图片,保存图片信息的一个简单的过程。
代码可以直接用,缺模块直接安装就好了,pandas和requests和bs4必装
一、代码
先给出完整版:
crawler_picture.py
import argparse
import os
import timeimport pandas
import requests
from bs4 import BeautifulSoup
from proxy_available import proxy_availableclass Crawler():def __init__(self,args):self.headers = {'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Mobile Safari/537.36'}self.start_page = args.start_page # 起始页self.end_page = args.end_page # 截止页self.url = 'https://bing.ioliu.cn/ranking' # urlself.page_num = self.start_pageself.if_write = args.writeself.if_download = args.downloadself.download_num = 0self.if_analysis = args.analysisself.proxy_available = proxy_availableself.pic_result_list = [] # 图片信息汇总if self.if_write:pic_list = []pic_list.append('name/图片名')pic_list.append('description/描述')pic_list.append('calendar/日期')pic_list.append('location/地点')pic_list.append('view/查看次数')pic_list.append('like/点赞')pic_list.append('download_times/下载次数')self.pic_result_list.append(pic_list)if self.if_download:self.datatype = 'images' # 下载的图片保存至images文件夹下if not os.path.exists(self.datatype):os.makedirs(self.datatype)else:for i in os.listdir(self.datatype):path_file = os.path.join(self.datatype,i)if os.path.isfile(path_file):os.remove(path_file)if self.if_analysis: self.location_name = ['亚洲','欧洲','非洲','美洲','大洋洲']self.location_times = [0,0,0,0,0]def get_page(self, url): if not url:url=self.url+'?p='+str(self.start_page)try:self.req = requests.get(url, headers=self.headers)if self.req.status_code == 200: # 状态码为200时进行解析self.decode(self.req.content)else: # 状态码不为200时选择使用代理ipfor i in range(0, len(self.proxy_available)):proxy = self.proxy_available[i]try:self.req = requests.get(url, headers=self.headers, proxies=proxy)if self.req.status_code == 200:self.decode(self.req.content)breakexcept:print("该代理ip无效")except Exception as e:print("出现错误")print(e)def decode(self, html):soup = BeautifulSoup(html, "html.parser")mask = soup.find('body').find('div', attrs={'class':'mask'})container = mask.next_siblingstart_time = time.time()for picture in container.find_all('div', attrs={'class':'item'}):card = picture.find('div', attrs={'class':'card progressive'})pic = card.find('img')pic_url = pic['src'].split('?')[0]description = card.find('div', attrs={'class':'description'})description_text = description.find('h3').getText()if description.find('p', attrs={'class':'calendar'}): # 获取日期calendar = description.find('p', attrs={'class':'calendar'}).find('em', attrs={'class':'t'}).getText()else:calendar = "0000-00-00"if description.find('p', attrs={'class':'location'}): # 获取拍摄地址location = description.find('p', attrs={'class':'location'}).find('em', attrs={'class':'t'}).getText()else:location = "未知"if description.find('p', attrs={'class':'view'}): # 获取观看量view = description.find('p', attrs={'class':'view'}).find('em', attrs={'class':'t'}).getText()else:view = 0options = card.find('div', attrs={'class':'options'})if options.find('span', attrs={'class':'ctrl heart'}): # 获取点赞量like = options.find('span', attrs={'class':'ctrl heart'}).find('em', attrs={'class':'t'}).getText()else:like = 0if options.find('a', attrs={'class':'ctrl download'}): # 获取下载量download_times = options.find('a', attrs={'class':'ctrl download'}).find('em', attrs={'class':'t'}).getText()else:download_times = 0pic_list = []pic_list.append(pic_url.split('/')[-1])pic_list.append(description_text)pic_list.append(calendar)pic_list.append(location)pic_list.append(view)pic_list.append(like)pic_list.append(download_times)self.pic_result_list.append(pic_list) # 将图片信息保存至列表中if self.if_download:self.download(pic_url, self.datatype) # 下载图片if self.if_analysis:self.analysis(location) # 分析拍摄地址if self.if_download:print('\n'+"第"+str(self.page_num)+"页下载完毕,该页耗时:"+str(round(time.time()-start_time,2))+"s")if self.page_num == self.end_page: # 此页为设定的最后一页if self.if_download:print("共下载 "+str(self.download_num)+" 张图片")if self.if_analysis:self.data_analysis() # 进行拍摄地址的数据分析self.write() # 将所有图片信息写入表格中return else:self.page_num += 1next_page = soup.find('div', attrs={'class','page'}) # 寻找下一页for page in next_page.find_all('a'):if page.getText()=='下一页':if self.page_num == page['href'].split('?')[-1]: # 下一页与当前页相等,表示已经到了最后一页self.write()if self.if_download:print("共下载 "+str(self.download_num)+" 幅图片")if self.if_analysis:self.data_analysis()else: next = self.url + '?' + page['href'].split('?')[-1]self.get_page(next)else:continuedef download(self, pic_url, datatype):img = requests.get(pic_url) # 获取图片数据if img.status_code == 200:img_path = datatype+'/'+pic_url.split('/')[-1]data_count = 0content_size = int(img.headers['content-length'])with open(img_path, "wb") as file:print('\n'+pic_url.split('/')[-1])for data in img.iter_content(chunk_size=1024): # 一块一块以下载,一块的大小为1MBfile.write(data)data_count = data_count + len(data)now = (data_count / content_size) * 50 # 计算下载的进度print('\r'+"已经下载:"+int(now)*"="+" 【"+str(round(data_count/1024/1024,2))+"MB】"+"【"+str(round(float(data_count/content_size)*100,2))+"%"+"】", end='')self.download_num += 1def result(self):print(self.pic_result_list)def write(self):summaryDataFrame = pandas.DataFrame(self.pic_result_list) # 将二位列表转化成DataFrame数据类型summaryDataFrame.to_excel("summary_pictures_biying.xlsx", encoding='utf-8', index=False, header=False)def analysis(self, location):for i in range(0, 5):if self.location_name[i] in location:self.location_times[i] += 1breakdef data_analysis(self):from pyecharts import options as opts # 导入包from pyecharts.charts import Piefrom pyecharts.render import make_snapshotc = Pie() # 绘制饼状图c.add("",[list(z) for z in zip(self.location_name, self.location_times)],radius=["40%", "75%"], # 内半径和外半径占比)c.set_global_opts(title_opts=opts.TitleOpts(title="壁纸拍摄地址分布图"),)c.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))c.render('壁纸拍摄地址分布图.html')# make_snapshot(driver, c.render(), "图片地理位置分布图.png") # 需下载chromedriverparser = argparse.ArgumentParser()
parser.add_argument("-s","--start_page",type=int,default=1,help="起始页")
parser.add_argument("-e","--end_page",type=int,default=1,help="终止页")
parser.add_argument("-d","--download",default=False,help="是否下载图片")
parser.add_argument("-w","--write",default=True,help="是否写入excel")
parser.add_argument("-a","--analysis",default=False,help="是否进行数据分析")
args = parser.parse_args()crawler = Crawler(args)
crawler.get_page('')
# crawler.result()
# crawler.write()
# crawler.data_analysis()
proxy_available.py
(该文件内ip地址无法适用于爬取必应网站,只能用于http开头的网站,放在这里只是为了学习如何使用代理ip)
proxy_available = [
{"http":"http://211.149.172.228:9999","https":"https://116.196.90.181:3128",
},
{"http":"http://211.149.172.228:9999","https":"https://116.196.90.181:3128",
},
]
下面是详细的讲述:
1.1、参数传入
因为最近写了好多代码都用命令行传入参数,直接传感觉有点low low的,就稍微学了一下argparse模块,主要是三个步骤:
- 创建ArgumentParser()对象
- 调用add_argument()方法添加命令行输入的参数
- 使用parse_args()解析输入的参数
代码如下所示:
parser = argparse.ArgumentParser()
parser.add_argument("-s","--start_page",type=int,default=1,help="起始页")
parser.add_argument("-e","--end_page",type=int,default=1,help="终止页")
parser.add_argument("-d","--download",default=False,help="是否下载图片")
parser.add_argument("-w","--write",default=True,help="是否写入excel")
parser.add_argument("-a","--analysis",default=False,help="是否进行数据分析")
args = parser.parse_args()
假如后面想调用起始页的变量,那就args.start_page就好了
1.2、发送请求
向服务器发送请求使用到的是python的requests库,通过Chrome的开发者工具可以看到,必应网使用的是GET请求方式:
因此调用的是requests库的get()方法,获取到返回的包含服务器资源的Response对象后,需要判断请求是否成功,也就是通过查看状态码是否为200来进行判断。如果状态码正常,那就去解析网页。
但是大多网页都有反爬虫机制,所以就需要使用一些策略来绕过这些策略。本代码中主要采用伪装成服务器的方式来进行破解。
同样在开发者工具中可以看到浏览器的代理名称:
通过字典的方式将其写入到get()方法的headers参数中去,这样就可成功打开网页并获得内容。代码如下:
self.headers = {'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Mobile Safari/537.36'}
self.url = 'https://bing.ioliu.cn/ranking'
def get_page(self, url):if not url:url=self.url+'?p='+str(self.start_page) #起始页self.req = requests.get(url, headers=self.headers)if self.req.status_code == 200:self.decode(self.req.content) # 解析网页
除此之外,也可以使用代理ip的方式绕过反爬虫机制:
for i in range(0, len(self.proxy_available)):proxy = self.proxy_available[i]try:self.req = requests.get(url, headers=self.headers, proxies=proxy)if self.req.status_code == 200:self.decode(self.req.content)breakexcept:print("该代理ip无效")
其中proxy为字典,这里会依次从代理ip的列表中选取代理ip并传给proxies参数。鉴于免费且稳定的ip地址并不多,所以这一部分代码在本次爬虫实验中不会被调用到。
1.3、获取并保存图片信息
解析网页用到的是BeautifulSoap库。这里面大多时用到的是find()、find_all()、getText()等方法。其中find()和find_all()可以返回满足条件的子节点,而getText()可以获取数据内容。每一个壁纸图像涉及的信息在下图的节点中:
依次找到需要的节点,再获取包含的信息即可。
但是,鉴于不是所有图片都包含了所有信息,存在着信息缺失的情况,所以这里还需要进行判断:
mask = soup.find('body').find('div', attrs={'class':'mask'})
container = mask.next_siblingfor picture in container.find_all('div', attrs={'class':'item'}):card = picture.find('div', attrs={'class':'card progressive'})pic = card.find('img')pic_url = pic['src'].split('?')[0]description = card.find('div', attrs={'class':'description'})description_text = description.find('h3').getText()if description.find('p', attrs={'class':'calendar'}):calendar = description.find('p', attrs={'class':'calendar'}).find('em', attrs={'class':'t'}).getText()else:calendar = "0000-00-00"if description.find('p', attrs={'class':'location'}):location = description.find('p', attrs={'class':'location'}).find('em', attrs={'class':'t'}).getText()else:location = "未知"......# 其余信息的获取均同理
图片信息获取完毕后,将其保存至excel表格里,用到了pandas库。图片的信息被保存到一个二维列表中,最后将汇总后的二维列表转换成DataFrame数据类型,并使用to_excel()方法就可以写入excel表中:
summaryDataFrame = pandas.DataFrame(self.pic_result_list)
summaryDataFrame.to_excel("summary_pictures_biying.xlsx", encoding='utf-8', index=False, header=False)
1.4、下载图片
通过解析数据,可以找到图片保存的地址,获取到地址后,使用get()方法获取图片的数据并写入文件。
图片的数据用的是二进制表示的方法,写入到.jpg文件里即为下载成功。
为了能够看到图片的下载进度,使用了headers方法来获取图片的总大小,每一块下载的大小为1MB,最后将已下载的大小与总大小的占比展示在终端上。
def download(self, pic_url, datatype):
img = requests.get(pic_url)if img.status_code == 200:img_path = datatype+'/'+pic_url.split('/')[-1]data_count = 0content_size = int(img.headers['content-length'])with open(img_path, "wb") as file:print('\n'+pic_url.split('/')[-1])for data in img.iter_content(chunk_size=1024): # 一块一块以下载file.write(data)data_count = data_count + len(data)now = (data_count / content_size) * 50 # 计算下载的进度print('\r'+"已经下载:"+int(now)*"="+" 【"+str(round(data_count/1024/1024,2))+"MB】"+"【"+str(round(float(data_count/content_size)*100,2))+"%"+"】", end='')self.download_num += 1
1.5、更新网址
如果尚未到达指定的截止页,需要更新网页地址获取新的资源数据。
这里依旧用find()和getText()方法来获取到下一页的网址。
next_page = soup.find('div', attrs={'class','page'})
for page in next_page.find_all('a'):if page.getText()=='下一页':if self.page_num == page['href'].split('?')[-1]: # 下一页与当前页相等,表示已经到了最后一页self.write()if self.if_download:print("共下载 "+str(self.download_num)+" 幅图片")if self.if_analysis:self.data_analysis()else: next = self.url + '?' + page['href'].split('?')[-1]self.get_page(next)
1.6、数据分析
对于抓取到的数据,可以简单地进行一个分析。
在本次爬虫实验中,图片的信息里包含了拍摄地址,可以对拍摄地址来进行一个比较。可以将拍摄地址大体分为亚洲、欧洲、非洲、美洲、大洋洲五类,然后再根据获取到的地址分别进行计数,最后使用pyecharts模块里的Pie()方法,绘制饼状图。
def data_analysis(self):c = Pie()c.add("",[list(z) for z in zip(self.location_name, self.location_times)],radius=["40%", "75%"], #内半径和外半径占比)c.set_global_opts(title_opts=opts.TitleOpts(title="壁纸拍摄地址分布图"))c.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))c.render('壁纸拍摄地址分布图.html')# make_snapshot(driver, c.render(), "图片地理位置分布图.png")
其中self.location_name和self.location_times为列表,分别保存了类别和每一类的数量。
二、结果
首先输入参数:
这里设置了参数,从第一页到第四页,图片下载选项设置为Ture
图片的信息保存在summary_pictures_biying.xlsx中:
图片下载进度显示如下图所示:
图片保存在同目录下的images文件夹中:
拍摄地址的占比结果如下图所示(爬取页数为1-20):
最后:必应是个比较好爬的网站,但是,原先我是打算爬知乎的,后来发现知乎登陆的json竟然加密了,网上那些教程大多已经失去了作用,所以这个可能要费一点时间,那就爬下来后再写吧
爬虫(一)——必应排行榜壁纸图片下载相关推荐
- python 小说爬虫_从零开始写Python爬虫 --- 1.7 爬虫实践: 排行榜小说批量下载
从零开始写Python爬虫 --- 1.7 爬虫实践: 排行榜小说批量下载Ehco 5 个月前 本来只是准备做一个爬起点小说名字的爬虫,后来想了一下,为啥不顺便把小说的内容也爬下来呢?于是我就写了这个 ...
- 豆瓣电影Top 250排行榜海报图片下载
1 需求 豆瓣电影Top 250排行榜海报图片下载,并实现持续化存储. 2 代码实现 import re import requests# 获取网页源代码 headers = {'User-Agent ...
- 从零开始写Python爬虫 --- 1.7 爬虫实践: 排行榜小说批量下载
从零开始写Python爬虫 --- 1.7 爬虫实践: 排行榜小说批量下载 Ehco 5 个月前 本来只是准备做一个爬起点小说名字的爬虫,后来想了一下,为啥不顺便把小说的内容也爬下来呢?于是我就写了这 ...
- 爬虫实践---电影排行榜和图片批量下载
1.目标网址:http://dianying.2345.com/top/ 需要找到的信息:电影的名字,主演,简介,和标题图片 2.查看页面结构: 容易看到,需要的主题部分,都被包裹在'<ul&g ...
- 用python爬虫爬取网页壁纸图片(彼岸桌面网唯美图片)
参考文章:https://www.cnblogs.com/franklv/p/6829387.html 今天想给我的电脑里面多加点壁纸,但是嫌弃一个个保存太慢,于是想着写个爬虫直接批量爬取,因为爬虫只 ...
- python唯美壁纸_用python爬虫爬取网页壁纸图片(彼岸桌面网唯美图片)
参考文章:https://www..com/franklv/p/6829387.html 今天想给我的电脑里面多加点壁纸,但是嫌弃一个个保存太慢,于是想着写个爬虫直接批量爬取,因为爬虫只是很久之前学过 ...
- 苹果5壁纸_iPhone12蓝色橙色壁纸图片下载-iPhone12蓝色橙色壁纸高清图片大全 v1.0...
iPhone12蓝色橙色壁纸图片是一款手机壁纸图片,这款壁纸非常好看,是蓝色和橙色系列的,这两种颜色饱和度很高,看起来很明亮,给人一种生机勃勃的感觉,所以说用来做手机壁纸或者是锁屏都不错,让手机马上变 ...
- 苹果5壁纸_iPhone12蓝色橙色壁纸图片下载_iPhone12蓝色橙色壁纸图片无水印免费分享 v1.0...
iPhone12蓝色橙色壁纸图片无水印免费分享是小编为大家带的全网最新壁纸分享,这里的壁纸都是高清无码无水印,用户可以放心下载使用,而且除了蓝色橙色壁纸外,小编还为大家准备了其他的精美壁纸,快来下载使 ...
- 爬虫实例:从百度图片下载壁纸
一.数据分析 百度图片壁纸网址:http://image.baidu.com/channel/wallpaper 1.打开网址,点击国家地理,打开Chrom浏览器的开发者工具,选中图片图片元素. 获得 ...
最新文章
- python基础-变量运算符(3)
- protect a health
- PXE自动安装FreeBSD
- 玩的时候好好玩,学的时候好好学。不做半吊子
- 包概念与__init__注意事项
- 绑定任意格式的XML文档到WPF的TreeView
- 简练软考知识点整理-建设项目团队
- Broker模块划分
- JVM基础系列开篇:为什么要学虚拟机?
- Oracle和SQL server的数据类型比较
- Node.js简介与架构
- MaxCompute SQL
- uno牌的玩法图解_UNO基本玩法和技巧
- Android三级缓存机制工具类的实现
- windows中的DOS命令及变量
- 阿里妈妈一面简历面-半小时直接挂
- Matlab——行星轨道问题
- 数论基础——数论函数(1)
- pygame一步步玩转圆、矩形、图像、旋转、透明度alpha、colorkey操作(有完整案例)
- BSCI认证咨询 BSCI行为准则主要包含如下几个主要方面