python爬取Instagram上偶像的帖子(包括图片和视频)
声明:仅供技术交流,请勿用于非法用途,如有其它非法用途造成损失,和本博客无关
爬取的网站:Instagram上JayChou
周杰伦的所有帖子 点击跳转
本次爬虫使用的是:requests
一、配置环境
python3.7
pycharm
requests
win10
pymysql
二、分析网页
打开之后发现它是AJAX
动态加载的,并且加载完了前面的,再往后加载的话,前面加载完的会消失掉的。
按F12
打开开发者工具查看XHR
,可以找到对应的请求AJAX
的网站,发现其规律主要是after
参数的不断更改来获取对应的数据,但是同时发现这个参数是随机改变的,完全没有规律,那么在哪能找到下一页数据的这个参数呢,答案就是在返回的json
数据里面啦(然鹅我分析了很久怎么生成这个参数的,最后才恍然大悟竟然就在返回的数据里面)
但是又有一个问题来了,就是一开始的after
参数去哪里找呢,答案就是在一开始请求的网页的源代码上哈哈,在杰伦主页查看源代码即可找到
可能又有小朋友会问,源代码这么多这么复杂怎么提取这个字符串呢,答案就是正则匹配啦敢敢单单
s=requests.session()
s.headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}
r=s.get('https://www.instagram.com/jaychou/')
qvf=re.findall('"has_next_page":true,"end_cursor":"(.*?)"},"edges"',r.text)[0]
那么获取到这个一开始的关键参数,后面就是编写在json
数据中提取想要的信息的代码了,先构造请求的url
为:
qvf='{"id":"5951385086","first":12,"after":"%s"}'%qvf
link='https://www.instagram.com/graphql/query/?query_hash=2c5d4d8b70cad329c4a6ebe3abb6eedd&variables='+urllib.request.quote(qvf)
我提取的信息有:
- 每条帖子的唯一id
- 配文
- 点赞人数
- 评论数
- 发布时间
- 地点
- 图片、视频
具体细节请看代码注释
三、数据库设计
- 创建表
import pymysql
conn = pymysql.connect(host='localhost', user='youruser', password='yourpassword', database='yourdatabase')
cursor = conn.cursor()
sql='''
CREATE TABLE IF NOT EXISTS jaychou(
id VARCHAR(255) PRIMARY KEY,
text longtext,
public_time datetime,
location VARCHAR(255),
good_count int,
comment_count int,
imgage_url longtext,
video_url longtext,
video_view_count VARCHAR(255)
)
ENGINE=innodb DEFAULT CHARSET=utf8;'''
cursor.execute(sql)
- 修改表结构(为了能够插入表情)
sql='ALTER TABLE jaychou CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'
cursor.execute(sql)
经过多次的调试,发现的几个问题
- 数据表插入不了表情符号
- 字符串的长度要设计为
longtext
(稳一点)
四、完整代码
import requests
import re
import time
import random
import pymysql
import os
import json
conn = pymysql.connect(host='localhost', user='youruser', password='yourpassword', database='yourdatabase')
cursor = conn.cursor()
sql='''
CREATE TABLE IF NOT EXISTS jaychou(
id VARCHAR(255) PRIMARY KEY,
text longtext,
public_time datetime,
location VARCHAR(255),
good_count int,
comment_count int,
imgage_url longtext,
video_url longtext,
video_view_count VARCHAR(255)
)
ENGINE=innodb DEFAULT CHARSET=utf8;'''
cursor.execute(sql)
sql='ALTER TABLE jaychou CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'
cursor.execute(sql)
cursor.close()
conn.close()
s=requests.session()
s.headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}
r=s.get('https://www.instagram.com/jaychou/')
#拿到第一个after参数
qvf=re.findall('"has_next_page":true,"end_cursor":"(.*?)"},"edges"',r.text)[0]
k=1 #记录循环次数
while True:
conn = pymysql.connect(host='localhost', user='youruser', password='yourpassword', database='yourdatabase')
cursor = conn.cursor()
sql='select * from jaychou'
res=cursor.execute(sql)
res=cursor.fetchall()
temp=[] #避免重复插入
if len(res) != 0:
for i in range(len(res)):
temp.append(list(res[i])[0])
qvf='{"id":"5951385086","first":12,"after":"%s"}'%qvf
link='https://www.instagram.com/graphql/query/?query_hash=2c5d4d8b70cad329c4a6ebe3abb6eedd&variables='+urllib.request.quote(qvf)
response=s.get(link).json()
time.sleep(random.uniform(1,1.5))
qvf=response['data']['user']['edge_owner_to_timeline_media']['page_info']['end_cursor']
edges=response['data']['user']['edge_owner_to_timeline_media']['edges']
print(f'第{k}次,有{len(edges)}个edges')
for edge in edges:
imgage_url = [] #存图片
video_url = [] #存视频
video_view_count = [] #存视频播放数
id=edge['node']['id'] #唯一标识
typename=edge['node']['__typename']
text = '无'
if edge['node']['edge_media_to_caption']['edges'] != []:
text = edge['node']['edge_media_to_caption']['edges'][0]['node']['text'] # 配文
public_time=time.strftime('%Y-%m-%d %H:%M:%S',time.gmtime(edge['node']['taken_at_timestamp'])) #发布时间
good_count = edge['node']['edge_media_preview_like']['count'] # 点赞人数
comment_count = edge['node']['edge_media_to_comment']['count'] # 评论数
location='无' if edge['node']['location'] is None else edge['node']['location']['name'] #地点
if typename == 'GraphImage': #说明只有一张图片
imgage_url.append(edge['node']['display_url']) #图片地址
elif typename == 'GraphVideo': #说明只有一个视频
video_url.append(edge['node']['video_url']) #视频地址
video_view_count.append(str(edge['node']['video_view_count'])) #视频播放数
elif typename == 'GraphSidecar': #说明是组图,或者是组视频,又或者是视频加图片
childrens = edge['node']['edge_sidecar_to_children']['edges']
for children in childrens:
if children['node']['is_video'] is False:
imgage_url.append(children['node']['display_url'])
else:
video_url.append(children['node']['video_url'])
video_view_count.append(str(children['node']['video_view_count']))
if id not in temp:
name=public_time.replace(':','') #文件名不能含有冒号
path=f'./jaychou/{name}/'
if not os.path.exists(path):
os.makedirs(path)
with open(path+'配文.txt','w',encoding='utf-8') as f:
f.write(text)
for i,each in enumerate(imgage_url): #下载图片
with open(path+f'{i+1}.jpg','wb') as f:
res=s.get(each)
f.write(res.content)
time.sleep(random.uniform(1,1.5))
for i,each in enumerate(video_url): #下载视频
with open(path+f'{i+1}.mp4','wb') as f:
res=s.get(each)
f.write(res.content)
time.sleep(random.uniform(1,1.5))
sql='insert into jaychou(id,text,public_time,location,good_count,comment_count,imgage_url,video_url,video_view_count) values(%s,%s,%s,%s,%s,%s,%s,%s,%s)'
cursor.execute(sql,[id,text,public_time,location,good_count,comment_count,','.join(imgage_url),','.join(video_url),','.join(video_view_count)])
conn.commit()
k+=1
cursor.close()
conn.close()
if qvf is None: #没有下一页的时候退出循环
break
五、数据展示
写在最后
很明显,我是杰迷。从小学五年级的时候,还记得那是一个阳光明媚中午,放学的时候,每个班排好队一起从课室走出校门的这一段路上,旁边的一个好朋友在唱:“还记得你说家是唯一的城堡……”,然后我就问他什么歌曲,他骄傲的说:“稻香”,于是乎回到家就上网找稻香这首歌,并把下载到MP3上,反复地听,还跟着唱,然后觉得一首还不够,就把杰伦之前的歌都下载到MP3上,循环播放一遍又一遍……后来,买了一台可以看歌词的MP4,那时候,流行抄歌词,我就把歌词抄在课本上,而且那时候还流行非主流签名,在课本上我还写满了周杰伦三个字,各种手写非主流签名。贼溜贼溜呢哈哈。周杰伦周春春,他的歌总是陪伴在我开心、伤心的每个瞬间,现在每逢坐车的时候,打代码的时候,洗澡的时候,我就打开音乐,打开收藏夹,听周杰伦的歌。还有太多太多跟杰伦有关的时候了,可是,还有一个最重要的时刻还没有完成,那就是跟我的另一半一起去看一场周杰伦的演唱会。