python爬取Instagram上偶像的帖子(包括图片和视频)

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)

经过多次的调试,发现的几个问题

  1. 数据表插入不了表情符号
  2. 字符串的长度要设计为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,那时候,流行抄歌词,我就把歌词抄在课本上,而且那时候还流行非主流签名,在课本上我还写满了周杰伦三个字,各种手写非主流签名。贼溜贼溜呢哈哈。周杰伦周春春,他的歌总是陪伴在我开心、伤心的每个瞬间,现在每逢坐车的时候,打代码的时候,洗澡的时候,我就打开音乐,打开收藏夹,听周杰伦的歌。还有太多太多跟杰伦有关的时候了,可是,还有一个最重要的时刻还没有完成,那就是跟我的另一半一起去看一场周杰伦的演唱会。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章