Python批量爬取微博素材(一分钟百张大图自动下载)
前言
大家好,我是Samaritan。
这期本来想做个咸鱼 写个学习笔记发的,然后没做成咸鱼 临时变卦,再写一期实战。
事情是这样子的,这周我的好友Brenda老师跟我闲聊:
这么一说确实有不少朋友平时也会用微博找素材,或者是关注一些会发美图的博主。
本着尽早让Brenda老师解放双手为目标,我打算立刻动手,说干就干。
也借此实战机会写一期博文,将我的心得和代码分享给大家。
请注意,这也许只是个简单的爬虫,但如若感到不尽人意,你完全可以在此基础上根据自己能力或针对性学习来改善它,包括但不限于增加功能、自定义需求以及使代码更简洁优美。
就像我在之前所有的博文中提的那样,我抛砖引玉、欢迎交流。
闲话至此,这就跟我一起开始着手创造一个微博爬虫吧。
一、思考逻辑
先不着急写代码。
我的经验是:从冒出新想法,到以代码实现的这个过程里,敲代码部分反而是最简单、快速的。
我通常要先把逻辑理顺、思考实现目标需要用到哪些方法,而实现哪些方法需要用怎样的代码。一个接着一个找到其中难点,通过搜索解决问题,直到在脑海里基本通过,接着才边敲代码边具体调整细节。
现在想想这次的目标是什么?
写一个实现自动下载某位博主的图片的程序。
所以这个程序的逻辑上它得先要知道:
爬取哪位博主
爬取的是图片,且非博主转载的
爬取图片的日期范围
作为用户,为了找素材或保存美图而使用这样一个程序,微博主页预览的小图肯定不能满足需要吧。
那么功能上,在主页爬到了图片之后,需要下载该图片的大图。
当用户下载时,很可能会产生几十数百张图片。我们就需要用代码创建一个文件夹将这些图片打包,而非乱七八糟的堆在某个目录下或桌面上。
剩下的,我想还有一些细节,比如图片批量下载保存时的自动重命名。
这样一来脑海里应该就清楚了我们要写一个怎样的程序。
接着思考这些功能如何实现?
要让计算机知道爬取的方向,用input函数输入
用re正则表达式和字符串拼接构建url
请求url,一般用requests或selenium模块
下载图片用requests模块请求大图链接
创建文件夹用os模块,文件夹名可以自动根据爬取的博主名字生成
图片保存时批量重命名则使用博主名+for…in到的计数变量拼接
到此,整个程序在脑海里是不是更清晰了?
那接着便可以顺着我们的逻辑开始实施行动。
二、观察URL
这个环节主要为了找到我们需要用的url,以及之间的联系以确定如何构建它。
1.找到爬取所需url
先打开微博任意博主主页看看,这里以我测试的博主为例:
https://weibo.com/p/1005052673726341/home?from=page_100505&mod=TAB#place
这里要注意,红框标示部分需要登录可见,所以为了更好爬取,我在博文中是登录状态爬取的。
但主页不能满足我们的需要,很混乱,什么视频、转载都有,而我们本次只想要博主的原创图片。
接着来跟我在浏览器上操作,点击红框标示内的【全部】:
右侧出现了三角按钮,点击打开类似过滤器的功能:
看到可以自定义只搜索我们要的原创图片。
所以学会本期实战以后,你可以根据自己的需要略作修改,用代码爬取视频或是别的内容。
这里还可以设置日期范围,这样一来,上一节中我们列举要提供给程序的三要素都齐了。
我们随便设置一个起始日期,然后点击搜索看看url的变化:
主页:
https://weibo.com/p/1005052673726341/home?from=page_100505&mod=TAB#place
搜索后:
https://weibo.com/p/1005052673726341/home?is_ori=1&is_pic=1&key_word=&start_time=2020-06-15&end_time=2020-06-30&is_search=1&is_searchadv=1#_0
可以直观看到问号连结前都是一样的,搜索需要设置的参数都在问号连接后,互相之间用&连结。
2.构建所需url
访问的url已经观察清楚,如何构建它呢?
可以看做,当用户提交一个博主主页链接,我们提取出问号前的部分:
https://weibo.com/p/1005052673726341/home?
开始构建成我们爬虫要访问的url,接上:
is_ori=1&is_pic=1&key_word=&start_time=
这段是固定的,代表原创图片,接着是起始日期设定:
2020-06-15 (这个是变量,程序里我们用input让用户设置即可)
最后加上一段结束日期,可以默认下载到最近日期:
&end_time=2020-06-30&is_search=1&is_searchadv=1#_0
注意若要end_time后的日期默认为当天,无需用户输入,我们可以使用python内置的时间模块来自动获取,具体想了解请看我之前写过一篇时间相关模块的博文已有较为详细的说明。
将上述字符串拼接完整就是我们要访问的那位博主的从设置时间开始到当天的全部原创图片的url。
3.图片url
来看要下载的图片在哪里?
不用我说了,找到一张图片,直接f12用箭头点它吧:
图片链接就在< img >里,点开发现是只是预览图,清晰度很低,没关系。
先用浏览器操作,点开图片,查看大图,查看原图,跳转到新页面:
继续f12查看元素:
然后就简单了,对比预览图和原图url:
预览图://wx1.sinaimg.cn/orj360/9f5dd385ly1gg5vpwcro3j20v912c16j.jpg
原图:http://wx1.sinaimg.cn/large/9f5dd385ly1gg5vpwcro3j20v912c16j.jpg
可以再点开几张图片对比测试一下,结果都是一样的,可以理解该方法为通用。
**结论:**当我们在主页爬取到预览图url时,只需要提取到最后一段如9f5dd385ly1gg5vpwcro3j20v912c16j.jpg这样的图片编号即可。
在其前面拼接上:http://wx1.sinaimg.cn/large/的固定格式就变成了原图大小的url。
4.url提取和拼接
你可能会问如何从主页url或者图片url里提取到我们要的部分呢?这里使用正则表达式re模块就很方便。
看一下代码:
import re
url = 'https://weibo.com/p/1005052673726341/home?from=page_100505&mod=TAB#place'
urlh = re.search(r"https.*\?",url).group()
print(urlh)
输出结果:
https://weibo.com/p/1005052673726341/home?
这句意思就是提取匹配https开头问号结尾的字符串。
图片url也是同理:
import re
url = '//wx1.sinaimg.cn/orj360/9f5dd385ly1gg5vpwcro3j20v912c16j.jpg'
urlp = re.search(r'\d/.*?jpg',pics).group()
pic = urlp[1:]
输出结果:
0/9f5dd385ly1gg5vpwcro3j20v912c16j.jpg
/9f5dd385ly1gg5vpwcro3j20v912c16j.jpg
这里提取匹配一个数字和/开头,以jpg结尾的字符串,然后在切片把数字除去即可。
要爬取图片url的位置以及爬取图片可下载的url至此都已弄清,终于可以开始着手写爬虫了!
三、微博爬虫
之前有提到,为了方便简单本期我是以登录状态为基础写的爬虫,偷懒到底我还是用了selenium直接准备好手机扫码登陆了。方法并不局限,各位还可以用selenium拿个cookie或者直接requests带着cookie去请求,若有兴趣研究也可以之后找我讨论。
selenium看过我网易云爬虫的朋友肯定也不陌生了,强大的自动化测试工具,那么就开始上代码:
import time,re,requests
from bs4 import BeautifulSoup
from selenium import webdriver
from datetime import datetime
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
#设置请求头,可以换自己浏览器的,也可以多准备一点
now = str(datetime.now()) #获取当前日期
endtime = now[:10] #提取需要格式的当前日期,这个变量就是后面构建url的搜图结束时间参数
pagelist = [] #该变量用来处理最大页数,搜索范围内图片多的话需要翻页
picurllist = [] #该变量用来保存爬到的图片下载url
把要用到的模块全导入并设置变量,然后定义一个方法来和用户交互、设置参数并建构url:
def typeinfo():
global url
home = input('请将搜索用户微博主页链接复制此处:')
print('开始设置下载起始日期:\n【设置提示:请根据提示输入相应数字,全部输入空格使用最早起始日期,直接回车使用当前年/月/日】')
starty = input('输入起始年份:')
if starty == '': #如果直接回车,则默认使用当前日期中的年份
year = now[:4]
else:
year = starty #否则使用用户输入的数字,后面同理
startm = input('输入起始月份:')
if startm == '': #如果直接回车,则默认使用当前日期中的月份
month = now[5:7]
else:
month = startm
startd = input('输入起始日期:')
if startd == '': #如果直接回车,则默认使用当前日期中的日期
day = now[8:10]
else:
day = startd
print(f'选择的搜图起始日期为{year}年{month}月{day}日')
urlh = re.search(r"https.*\?",home).group() #正则提取字符串
urlp = f'is_ori=1&is_pic=1&key_word=&start_time={year}-{month}-{day}&end_time={endtime}&is_search=1&is_searchadv=1#_0'
url = urlh+urlp #字符串拼接成我们需要的url
print(url)
运行以后界面就会是这样的:
为了用户方便增加了功能,如果新关注了一位博主,就可以无需查看起始日期,全部输入空格就可自动生成包括全部时间的原创图片url。而直接回车可以默认使用当天、当月或者当年。
没什么问题,接下去就是定义一个爬取的方法了:
def wbspy():
global uname
#selenium启动浏览器,并访问url
samaritan = webdriver.Chrome()
samaritan.get(url)
samaritan.implicitly_wait(10)
time.sleep(0.1)
#selenium模拟点击登录,使用扫码登录
log = samaritan.find_element_by_class_name('gn_login').find_elements_by_class_name('S_txt1')[-1]
log.click()
time.sleep(1)
logpic = samaritan.find_element_by_link_text("安全登录")
samaritan.execute_script("arguments[0].click();", logpic)
#等待时间根据网页加载状况而异,最好给充足的时间扫码,扫码以后最好等网页显示扫码成功再点击确认登录
time.sleep(12)
samaritan.get(url)
samaritan.implicitly_wait(10)
time.sleep(1)
#模拟滚动液面,至最下方
for i in range(5):
samaritan.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(0.8)
#加载完毕获取该页面内容,用bs解析
time.sleep(0.2)
html = samaritan.page_source
soup = BeautifulSoup(html,'html.parser')
#确定本次搜索页面最大数,如果只有一页就设置为1,否则爬取页面最大数字
maxp = 1
try:#尝试爬去页面最下方是否有翻页,有的话可以找到最大页码
maxpage = soup.find('div',class_="W_pages").find_all('li')
for i in maxpage:
page = i.text.strip()
p = int(re.search('\d+', page).group().strip())
print(p)
pagelist.append(p)#将所有页码添加进页码列表等待处理
maxp = max(pagelist)#max函数处理列表,得到最大页码数
except:
pass
#不必翻页情况下,最大页码数默认为1,这里是在检查一次以防出错
if maxp > 1:
pass
else:
maxp = 1
print(maxp)
#根据最大页码写循环,翻页获取每一页的所有图片url
for i in range(maxp):
piczone = soup.find_all(class_="WB_media_wrap clearfix")
for pp in piczone: #每个带图片的博文内还可能有多张图片
try:
picblock = pp.find('ul').find_all('img')
except:
picblock = pp.find('ul').find('img')
for p in picblock:
try: #找到预览图url
pics = p['src']
pic = re.search(r'\d/.*?jpg',pics).group() #提取图片url部分
hpic = 'http://wx1.sinaimg.cn/large'+pic[1:].strip()#原图url拼接
picurllist.append(hpic) #将图片下载链接添加进列表
except:
pass
#翻到最后一页时,只需提取url无需再次翻页,所以设置条件打破循环
if i == maxp-1:
break
#模拟点击翻页
np = samaritan.find_element_by_link_text("下一页")
samaritan.execute_script("arguments[0].click();", np)
#模拟滚动页面至最下方
for i in range(4):
samaritan.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(0.8)
time.sleep(0.2)
#加载页面并交给bs解析
html = samaritan.page_source
soup = BeautifulSoup(html, 'html.parser')
#爬取任务完成,别忘记关闭浏览器
samaritan.close()
到这一步,需要下载的图片链接就已经全部存放在picurllist列表里等待你的临幸~
磨刀霍霍,开始下载吧!
四、下载保存
第一节里提到了,要下载大量图片,我们需要一个文件夹打包他们,又为了方便,我们最好可以不用手动去命名,无论是对文件夹还是图片。
确定文件夹名字很简单,这里就直接使用博主的名字即可,在他的主页里也可以爬到。
所以只需在上一节的爬取方法里添加两行代码:
def wbspy():#依旧是这个方法,在里面加入以下代码
...
#找到博主昵称,提取字符串
uname = soup.find(class_="username").text
#重构字符串使它成为路径格式
uname = fr'\{uname}'
#自行设置等下需要保存的路径,此处举例为我的桌面
table = r'C:\Users\Desktop'
有了这个信息以后,就可以通过os模块创建文件夹了,接着上述代码:
import os
#别忘记在最开始导入os模块
def wbspy():
...#接着上述代码
try:#在指定路径下创建文件夹
os.mkdir(fr'{table}{uname}')
except:#如已存在,就忽略
pass
四行代码就搞定啦,简单看下os模块的官方文档:
os — 操作系统接口模块
源代码: Lib/os.py
该模块提供了一些方便使用操作系统相关功能的函数。 如果你是想读写一个文件,请参阅 open(),如果你想操作路径,请参阅 os.path 模块,如果你想在命令行上读取所有文件中的所有行请参阅 fileinput 模块。 有关创建临时文件和目录的方法,请参阅 tempfile 模块,对于高级文件目录处理,请参阅 shutil 模块。
…
注解 如果使用无效或无法访问的文件名与路径,或者其他类型正确但操作系统不接受的参数,此模块的所有函数都抛出 OSError (或者它的子类)。
很强大的模块,在批量下载时除了每个文件保存时构建新名称再保存,也可以使用默认名保存后用os的方法,replace()重命名以及listdir()方法得到目录下所有文件名:
import os
#设置要重命名文件所在文件夹路径
path = r'D:\Download'
#获取该目录下所有文件名
files = os.listdir(path)
#遍历所有文件,对每个文件进行操作
for i in range(len(files)):
#重命名
os.repalce(f'{path}\{files[i]}',f'{path}\{i}')
这样对某一类的文件统一批量重命名就很方便,这里举例只是使用数字序号,也可以做的更复杂。
对os模块只是简单介绍,有兴趣依旧可以自行拓展出去学习哦。
回到正题,创建完文件夹以后,终于到了最后下载图片的环节,定义一个下载方法,上代码:
def download(): #定义下载方法
global count #使计数变量全局化,最后还需调用它直观显示下载数
print(f'开始下载!\n预计下载{len(picurllist)}张图片!')
for dp in range(len(picurllist)): #遍历下载列表
count = dp+1 #设置计数器方便查看
picdown = requests.get(picurllist[dp], headers=headers).content
#requests请求链接,下载图片并content处理二进制文件
picsave = open(fr'C:\Users\Desktop{uname}{uname}{count}.jpg', 'wb')
#保存图片到之前我们设置的路径,并用博主名+数字序号命名
picsave.write(picdown)
picsave.close()
print(f'第{count}张图片下载完成!')
运行效果:
最后加一个main方法管理调用所有其他方法即可:
def main():
typeinfo()
start = time.time()
wbspy()
download()
end = time.time()
#这里要调用count计数变量,所以download方法里用了global使它全局化
print(f'下载完成!\n本次用时{end - start}秒!\n总计下载{count}张图片!')
if __name__ == '__main__':
main()
运行结束,赶快查看文件夹…成了!满足感爆炸有没有?
那关于爬取微博任意博主原创图片的代码就到此为止啦,如果看懂了以上所有内容,相信你已经可以轻松愉快的在微博上高效的下载你喜欢的素材咯。
结尾
由于咱的Brenda老师并不会使用python,电脑也自然没有安装环境,要给她使用的话,我还需要将代码封装成一个可以运行的exe。
大家应该也习惯了我喜欢在结尾部分做一些思路引申,如果是像我一样需要做给别人使用,可以安装pyinstaller模块,并自行学习实用方法,搜索一下有很多文章讲解,体验过就知道非常简单好用。
然后对不想局限于selenium的朋友,可以去研究一下用requests怎么找到并解析我们所需图片的下载链接。我也乐意尝试用不同方法解决同样的问题,这里写的博文我只是以简单易操作的方法为主。
在自定义的部分中,大家还可以尝试改变访问的url,来找到并下载视频、音频或是包括转载内容在内的素材。又或是提取到一些对你有用的文字信息,在数量足够多的情况下,做成数据分析以及可视化也会是很酷的事情。
对我来说,在编程当中,思维才是第一的。
最重要的永远不是我会些什么,而是我能用它们去做什么,以及我需要再学习什么来完成我要做的事情?
所以好像继续学习这件事情永远不会停止,也希望大家可以在我的唠叨里收获到自己的东西。
好啦,那本次实战就圆满完成咯,我要去找Brenda老师邀功~
题外话:
Brenda是个尤克里里弹唱超好听的小姐姐,我想想点首什么歌作为奖励比较好呢~有机会放上来给大家欣赏。
以上,
感谢您的耐心阅读,欢迎关注我共同讨论python和爬虫~学习编程这条路,我们一起走一段。
本文地址:https://blog.csdn.net/u010890916/article/details/107011704