Python环境配置网上一搜一大把,最好用PyCharm来搞方便点!废话不多说先来一个完整的Python3直接可以运行的代码,本代码爬的图片ins源地址为https://www.instagram.com/explore/tags/記念日/
:
# -*- coding: utf-8 -*-
import os
import json
import requests
import urllib.parse
import random
import time
def getheaders():
user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]
UserAgent=random.choice(user_agent_list)
headers = {'User-Agent': UserAgent}
return headers
def find_pic_url(after,tag_name):
url = "https://www.instagram.com/graphql/query/?query_hash=f92f56d47dc7a55b606908374b43a314&variables="
variables = {"tag_name": tag_name, "first": 2, "after": after}
_variables = urllib.parse.quote(json.dumps(variables))
url = url + _variables
headers = getheaders()
r_after=after
try:
data = requests.get(url=url,headers=headers,timeout = 5).json()
r_after = data['data']['hashtag']['edge_hashtag_to_media']['page_info']['end_cursor']
print(str.format("拿到after:%s" % (r_after)))
for d in data['data']['hashtag']['edge_hashtag_to_media']['edges']:
imgurl = d['node']['display_url'].replace('https', 'http')
print('找到链接:\n' + imgurl + '\n开始下载...')
try:
urllib.request.urlretrieve(imgurl,
str.format("/Users/jingliang/Desktop/images/box-%ld.jpg" % (time.time() * 1000)))
print('下载完成\n')
except OSError as err:
print("下载失败 OS error: {0}".format(err))
continue
except KeyError:
print('返回的数据异常 - 直接return重试')
return r_after
def download(url):
urllib.request.urlretrieve(url, str.format("/Users/jingliang/Desktop/images/box-%ld.jpg" % (time.time() * 1000)), Schedule)
def Schedule(a, b, c):
# a:已经下载的数据块
# b:数据块的大小
# c:远程文件的大小
per = 100.0 * a * b / c
if per > 100:
per = 100
print('>>> %.2f%%' % per)
if __name__ == '__main__':
after = "QVFDblVNOU5fNlhkRl9LY1hHeEhfNW9KaHBDWHJPSEtXTU9HOGM1cVBvSmVJQXU1Qk9FQ0Jlb2NTRjUzdE41Nk1vMWViV0h4cUdqZGJEME51MFJzNGVrQg=="
tag_name = "記念日"
# 如果没桌面上的 images文件夹就创建一个 jingliang是我的电脑用户名 改成你自己的
if not os.path.exists("/Users/jingliang/Desktop/images") :
os.mkdir("/Users/jingliang/Desktop/images")
r_after = find_pic_url(after, tag_name=tag_name)
for i in range(20000):
r_after = find_pic_url(r_after, tag_name=tag_name)
如果就是想来拷贝个代码那就可以走了。俗话说授人以鱼不如授人以渔,下面会详细的分析分析这个过程。
之前简单看过一些Python的代码,是公司的多媒体小哥哥写的将视频、图片之类的转换为WebP格式,这个爬图需求出来的时候,是公司已经离职的运维小哥哥帮忙搞的,由于爬的图不够,但是呢又不太好意思老麻烦小哥哥,再加上近来任务也不太重,就要了一份来细细的研究了一下,拿到的时候是打印出了所有的图片链接,估计小哥哥是想全部打印出来,然后在一个单独的服务去一起下载掉,我就比较懒了,不管过程,最终目的就是上头要求的下载好的图片,直接下载保存,再完善一些细节,容错了一些因为下载图片过程中偶尔错误的情况能不间断继续下载,过程中的确是遇到了这种情况:
这样一来就不会偶尔科学上网的网络抖动就中断了,也能继续爬图了,顺带好好研究下这个爬虫的过程细节,形成这么一个简单的博客记录一下。
下面就来一步步分析如何来写这个爬图脚本的过程,来个完整的效果图:
1. 通过Chrome来拿到真正的网络请求
打开Chrome贴上链接https://www.instagram.com/explore/tags/記念日/走你~ 进入检查,动动你的小手多滑动几页看到有一个名字一毛一样的请求来了好几次,没错那个就是我们要找的加载更多的请求:
2. 分析请求参数
将连着的两个请求对应的link address
,和前面一个的response
复制出来:
看完这张图,相信心里大致有个想法了,那就是参数的
query_hash
可以固定不变,下一个请求的 after
参数就是上一页返回的结果中的data
->hashtag
->edge_hashtag_to_media
->page_info
->end_cursor
,和edge_hashtag_to_media
同级别的edge_hashtag_to_top_posts
顾名思义就是置顶的数据,我们直接丢弃,没关系没多少图片。至于请求中的first
鬼知道是什么,直接去Google一把,肯定有人会问这个问题,找到一个靠谱点的就是说这个first
,是要拿到的节点数,越大,拿到的数据越多,后来试了试还真是,传个差不多的就行了。
3. 代码实现
没毛病!开始写代码(解析一下,每一行给注释),首先进入main
:
if __name__ == '__main__':
#定义一个变量`after` 保存请求中的after参数值 ,并且给一个咱们抓到的一个值作为初始值
after = "QVFDblVNOU5fNlhkRl9LY1hHeEhfNW9KaHBDWHJPSEtXTU9HOGM1cVBvSmVJQXU1Qk9FQ0Jlb2NTRjUzdE41Nk1vMWViV0h4cUdqZGJEME51MFJzNGVrQg=="
#定义一个变量`tag_name` 保存请求中的tag_name参数值 ,我们这里爬的是标签: 記念日
tag_name = "記念日"
# 如果没桌面上的 images文件夹就创建一个防止写入找不到路径而失败 jingliang是我的电脑用户名 改成你自己的
if not os.path.exists("/Users/jingliang/Desktop/images") :
os.mkdir("/Users/jingliang/Desktop/images")
# 定义一个用来循环赋值的`r_after` 来接收每一页返回的`end_cursor` 直接调请求方法一次 也可以直接 r_after = after 给个初始值
r_after = find_pic_url(after, tag_name=tag_name)
# 来吧 让暴风雨来的更猛一点 循环调20000页
for i in range(20000):
r_after = find_pic_url(r_after, tag_name=tag_name)
find_pic_url
方法详细解释:
def find_pic_url(after,tag_name):
# 定义一个`url` 预备来拼接参数 after和tag_name
url = "https://www.instagram.com/graphql/query/?query_hash=f92f56d47dc7a55b606908374b43a314&variables="
# 定义一个`variables` 来将两个参数装进map
variables = {"tag_name": tag_name, "first": 2, "after": after}
# 定义一个`_variables` 将map对象转换我json字符串
_variables = urllib.parse.quote(json.dumps(variables))
# 将 url和json字符串进行拼接为完整的请求URL
url = url + _variables
# 装的更像点 随机一个User-Agent作为请求头 防止同一个 一直刷被封
headers = getheaders()
# 定义一个临时变量准备存储返回的 end_cursor 并给个初始值,毕竟后面要返回过去 如果出错 可以直接返回 不至于空置
r_after=after
try:
# 拿到请求URL返回的数据 直接转换为json 超时时间设为5秒
data = requests.get(url=url,headers=headers,timeout = 5).json()
# 取到end_cursor 赋值
r_after = data['data']['hashtag']['edge_hashtag_to_media']['page_info']['end_cursor']
# 打印一下 如果特殊原因中断 直接将打印出来的值 在main里面粘贴上 继续run 数据最多重复一点点
print(str.format("拿到after:%s" % (r_after)))
# 遍历 edges 数组
for d in data['data']['hashtag']['edge_hashtag_to_media']['edges']:
# 取到 edges 数组中对象的图片链接
imgurl = d['node']['display_url'].replace('https', 'http')
print('找到链接:\n' + imgurl + '\n开始下载...')
try:
# 下载图片到目的路径 用时间戳命名防止冲突覆盖
urllib.request.urlretrieve(imgurl,
str.format("/Users/jingliang/Desktop/images/box-%ld.jpg" % (time.time() * 1000)))
print('下载完成\n')
except OSError as err:
# 下载失败 就继续下一条 避免直接中断
print("下载失败 OS error: {0}".format(err))
continue
except KeyError:
# 请求有问题 就直接返回 重试 同样避免直接中断
print('返回的数据异常 - 直接return重试')
return r_after
一切的一切就是这么的完美,不得不说Python这个默认的线程串行执行有时候还真是方便,这个爬虫需要科学上网,并发执行的意义不大,流量本来就他么慢,何必再来个并发。源码中
Schedule
方法,是个下载的进度回调,调动方法:urllib.request.urlretrieve(url, str.format("/Users/jingliang/Desktop/images/box-%ld.jpg" % (time.time() * 1000)), Schedule)
,urlretrieve
方法里多传一个回调方法进去即可。
网友评论