Python instagram 爬虫项目
instagram 爬虫注意点
instagram 的首页数据是 服务端渲染的,所以首页出现的 11 或 12 条数据是以 html 中的一个 json 结构存在的(additionalData), 之后的帖子加载才是走 ajax 请求的
在 2019/06 之前,ins 是有反爬机制的,请求时需要在请求头加了 'X-Instagram-GIS' 字段。其算法是:
1、将 rhx_gis 和 queryVariables 进行组合rhx_gis 可以在首页处的 sharedData 这个 json 结构中获得
注意:很多人学Python过程中会遇到各种烦恼问题解决不了。为此小编建了个Python全栈免费答疑交流.裙 :一久武其而而流一思(数字的谐音)转换下可以找到了,不懂的问题有老司机解决里面还有最新Python教程项目可拿,,一起相互监督共同进步!2、然后进行 md5 哈希
e.g.queryVariables = '{"id":"' + user_id + '","first":12,"after":"' +cursor+ '"}'
print(queryVariables)
headers['X-Instagram-GIS'] = hashStr(GIS_rhx_gis + ":" + queryVariables)但是在在 2019/06 之后, instagram 已经取消了 X-Instagram-GIS 的校验,所以无需再生成 X-Instagram-GIS,上一点内容可以当做历史来了解了
初始访问 ins 首页的时候会设置一些 cookie,设置的内容 (response header) 如下:
set-cookie: rur=PRN; Domain=.instagram.com; HttpOnly; Path=/; Secure
set-cookie: ds_user_id=11859524403; Domain=.instagram.com; expires=Mon, 15-Jul-2019 09:22:48 GMT; Max-Age=7776000; Path=/; Secure
set-cookie: urlgen="{\"45.63.123.251\": 20473}:1hGKIi:7bh3mEau4gMVhrzWRTvtjs9hJ2Q"; Domain=.instagram.com; HttpOnly; Path=/; Secure
set-cookie: csrftoken=Or4nQ1T3xidf6CYyTE7vueF46B73JmAd; Domain=.instagram.com; expires=Tue, 14-Apr-2020 09:22:48 GMT; Max-Age=31449600; Path=/; Secure关于 query_hash,一般这个哈希值不用怎么管,可以直接写死
特别注意:在每次请求时务必带上自定义的 header,且 header 里面要有 user-agent,这样子才能使用 rhx_gis 来进行签名访问并且获取到数据。切记!是每次访问!例如:
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}大部分 api 的访问需要在请求头的 cookie 中携带 session-id 才能得到数据,一个正常的请求头 (request header) 如下:
:authority: www.instagram.com
:method: GET
:path: /graphql/query/?query_hash=ae21d996d1918b725a934c0ed7f59a74&variables=%7B%22fetch_media_count%22%3A0%2C%22fetch_suggested_count%22%3A30%2C%22ignore_cache%22%3Atrue%2C%22filter_followed_friends%22%3Atrue%2C%22seen_ids%22%3A%5B%5D%2C%22include_reel%22%3Atrue%7D
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,en;q=0.8,la;q=0.7
cache-control: no-cache
cookie: mid=XI-joQAEAAHpP4H2WkiI0kcY3sxg; csrftoken=Or4nQ1T3xidf6CYyTE7vueF46B73JmAd; ds_user_id=11859524403; sessionid=11859524403%3Al965tcIRCjXmVp%3A25; rur=PRN; urlgen="{\"45.63.123.251\": 20473}:1hGKIj:JvyKtYz_nHgBsLZnKrbSq0FEfeg"
pragma: no-cache
referer: https://www.instagram.com/
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
x-ig-app-id: 936619743392459
x-instagram-gis: 8f382d24b07524ad90b4f5ed5d6fccdb
x-requested-with: XMLHttpRequest注意 user-agent、x-ig-app-id (html 中的 sharedData 中获取)、x-instagram-gis,以及 cookie 中的 session-id 配置
api 的分页 (请求下一页数据),如用户帖子列表
ins 中一个带分页的 ajax 请求,一般请求参数会类似下面:query_hash: a5164aed103f24b03e7b7747a2d94e3c
variables: {
"id":"1664922478",
"first":12,
"after":"AQBJ8AGqCb5c9rO-dl2Z8ojZW12jrFbYZHxJKC1hP-nJKLtedNJ6VHzKAZtAd0oeUfgJqw8DmusHbQTa5DcoqQ5E3urx0BH9NkqZFePTP1Ie7A"}-- id 表示用户 id,可在 html 中的 sharedData 中获取
-- first 表示初始时获取多少条记录,好像最多是 50
-- after 表示分页游标,记录了分页获取的位置当然 variables 部分里面的参数根据请求的 api 不同而可能不同 (不止这么少),这里只列出与分页相关的参数。
分页请求参数首先是从 html 中的 sharedData 中获取的:
# 网页页面信息
page_info = js_data["entry_data"]["ProfilePage"][0]["graphql"]["user"]["edge_owner_to_timeline_media"]['page_info']
# 下一页的索引值AQCSnXw1JsoV6LPOD2Of6qQUY7HWyXRc_CBSMWB6WvKlseC-7ibKho3Em0PEG7_EP8vwoXw5zwzsAv_mNMR8yX2uGFZ5j6YXdyoFfdbHc6942w
cursor = page_info['end_cursor']
# 是否有下一页
flag = page_info['has_next_page']end_cursor 即为 after 的值,has_next_page 检测是否有下一页
如果是有下一页,可进行第一次分页数据请求,第一次分页请求的响应数据回来之后,id,first 的值不用变,after 的值变为响应数据中 page_info 中 end_cursor 的值,再构造 variables,连同 query_hash 发起再下一页的请求
再判断响应数据中的 page_info 中 has_next_page 的值,循环下去,可拿完全部数据。若不想拿完,可利用响应数据中的 edge_owner_to_timeline_media 中的 count 值来做判断,该值表示用户总共有多少媒体视频帖子和图片帖子数据结构不一样,注意判断响应数据中的 is_video 字段
如果是用一个 ins 账号去采集的话,只要请求头的 cookie 中带上合法且未过期的 session_id,可直接访问接口,无需计算签名。
最直接的做法是:打开浏览器,登录 instagram 后,F12 查看 xhr 请求,将 request header 中的 cookie 复制过来使用即可,向下面:headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
'cookie': 'mid=XLaW9QAEAAH0WaPDCeY490qeeNlA; csrftoken=IgcP8rj0Ish5e9uHNXhVEsTId22tw8VE; ds_user_id=11859524403; sessionid=11859524403%3A74mdddCfCqXS7I%3A15; rur=PRN; urlgen="{\"45.63.123.251\": 20473}:1hGxr6:Phc4hR68jNts4Ig9FbrZRglG4YA"'
}在请求发出的时候带上类似上面的请求头
错误日志记录表在 192.168.1.57 中 zk_flock 库的 ins_error_log,目前比较多 unknow ssl protocol 类型的错误,怀疑是爬取太快的原因,需要一个代理来切换
给出能运行的代码?(设置了 FQ 代理,不需要的可以去掉喔):
# -*- coding:utf-8 -*-
import requests
import re
import json
import urllib.parse
import hashlib
import sys
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
BASE_URL = 'https://www.instagram.com'
ACCOUNT_MEDIAS = "http://www.instagram.com/graphql/query/?query_hash=42323d64886122307be10013ad2dcc44&variables=%s"
ACCOUNT_PAGE = 'https://www.instagram.com/%s'
proxies = {
'http': 'http://127.0.0.1:1087',
'https': 'http://127.0.0.1:1087',
}
# 一次设置proxy的办法,将它设置在一次session会话中,这样就不用每次都在调用requests的时候指定proxies参数了
# s = requests.session()
# s.proxies = {'http': '121.193.143.249:80'}
def get_shared_data(html=''):
"""get window._sharedData from page,return the dict loaded by window._sharedData str
"""
if html:
target_text = html
else:
header = generate_header()
response = requests.get(BASE_URL, proxies=proxies, headers=header)
target_text = response.text
regx = r"\s*.*\s*<script.*?>.*_sharedData\s*=\s*(.*?);<\/script>"
match_result = re.match(regx, target_text, re.S)
data = json.loads(match_result.group(1))
return data
# def get_rhx_gis():
# """get the rhx_gis value from sharedData
# """
# share_data = get_shared_data()
# return share_data['rhx_gis']
def get_account(user_name):
"""get the account info by username
:param user_name:
:return:
"""
url = get_account_link(user_name)
header = generate_header()
response = requests.get(url, headers=header, proxies=proxies)
data = get_shared_data(response.text)
account = resolve_account_data(data)
return account
def get_media_by_user_id(user_id, count=50, max_id=''):
"""get media info by user id
:param id:
:param count:
:param max_id:
:return:
"""
index = 0
medias = []
has_next_page = True
while index <= count and has_next_page:
varibles = json.dumps({
'id': str(user_id),
'first': count,
'after': str(max_id)
}, separators=(',', ':')) # 不指定separators的话key:value的:后会默认有空格,因为其默认separators为(', ', ': ')
url = get_account_media_link(varibles)
header = generate_header()
response = requests.get(url, headers=header, proxies=proxies)
media_json_data = json.loads(response.text)
media_raw_data = media_json_data['data']['user']['edge_owner_to_timeline_media']['edges']
if not media_raw_data:
return medias
for item in media_raw_data:
if index == count:
return medias
index += 1
medias.append(general_resolve_media(item['node']))
max_id = media_json_data['data']['user']['edge_owner_to_timeline_media']['page_info']['end_cursor']
has_next_page = media_json_data['data']['user']['edge_owner_to_timeline_media']['page_info']['has_next_page']
return medias
def get_media_by_url(media_url):
response = requests.get(get_media_url(media_url), proxies=proxies, headers=generate_header())
media_json = json.loads(response.text)
return general_resolve_media(media_json['graphql']['shortcode_media'])
def get_account_media_link(varibles):
return ACCOUNT_MEDIAS % urllib.parse.quote(varibles)
def get_account_link(user_name):
return ACCOUNT_PAGE % user_name
def get_media_url(media_url):
return media_url.rstrip('/') + '/?__a=1'
# def generate_instagram_gis(varibles):
# rhx_gis = get_rhx_gis()
# gis_token = rhx_gis + ':' + varibles
# x_instagram_token = hashlib.md5(gis_token.encode('utf-8')).hexdigest()
# return x_instagram_token
def generate_header(gis_token=''):
# todo: if have session, add the session key:value to header
header = {
'user-agent': USER_AGENT,
}
if gis_token:
header['x-instagram-gis'] = gis_token
return header
def general_resolve_media(media):
res = {
'id': media['id'],
'type': media['__typename'][5:].lower(),
'content': media['edge_media_to_caption']['edges'][0]['node']['text'],
'title': 'title' in media and media['title'] or '',
'shortcode': media['shortcode'],
'preview_url': BASE_URL + '/p/' + media['shortcode'],
'comments_count': media['edge_media_to_comment']['count'],
'likes_count': media['edge_media_preview_like']['count'],
'dimensions': 'dimensions' in media and media['dimensions'] or {},
'display_url': media['display_url'],
'owner_id': media['owner']['id'],
'thumbnail_src': 'thumbnail_src' in media and media['thumbnail_src'] or '',
'is_video': media['is_video'],
'video_url': 'video_url' in media and media['video_url'] or ''
}
return res
def resolve_account_data(account_data):
account = {
'country': account_data['country_code'],
'language': account_data['language_code'],
'biography': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['biography'],
'followers_count': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['edge_followed_by']['count'],
'follow_count': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['edge_follow']['count'],
'full_name': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['full_name'],
'id': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['id'],
'is_private': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['is_private'],
'is_verified': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['is_verified'],
'profile_pic_url': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['profile_pic_url_hd'],
'username': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['username'],
}
return account
account = get_account('shaq')
result = get_media_by_user_id(account['id'], 56)
media = get_media_by_url('https://www.instagram.com/p/Bw3-Q2XhDMf/')
print(len(result))
print(result)
封装成库了!
本次分享大家都看懂了吗? 另外注意很多人学Python过程中会遇到各种烦恼问题解决不了。为此小编建了个Python全栈免费答疑交流.裙 :一久武其而而流一思(数字的谐音)转换下可以找到了,不懂的问题有老司机解决里面还有最新Python教程项目可拿,,一起相互监督共同进步!
本文的文字及图片来源于网络加上自己的想法,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。
————————————————
版权声明:本文为CSDN博主「编程叫兽」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/QQ2352108083/article/details/104406925
Python instagram 爬虫项目的更多相关文章
- 基于python的爬虫项目
一.项目简介 1.1 项目博客地址 https://www.cnblogs.com/xsfa/p/12083913.html 1.2 项目完成的功能与特色 爬虫和拥有三个可视化数据分析 1.3 项目采 ...
- Python开源爬虫项目代码:抓取淘宝、京东、QQ、知网数据--转
数据来源:数据挖掘入门与实战 公众号: datadw scrapy_jingdong[9]- 京东爬虫.基于scrapy的京东网站爬虫,保存格式为csv.[9]: https://github.co ...
- python多线程爬虫+批量下载斗图啦图片项目(关注、持续更新)
python多线程爬虫项目() 爬取目标:斗图啦(起始url:http://www.doutula.com/photo/list/?page=1) 爬取内容:斗图啦全网图片 使用工具:requests ...
- 关于Python网络爬虫实战笔记①
python网络爬虫项目实战笔记①如何下载韩寒的博客文章 python网络爬虫项目实战笔记①如何下载韩寒的博客文章 1. 打开韩寒博客列表页面 http://blog.sina.com.cn/s/ar ...
- 《精通Python网络爬虫》|百度网盘免费下载|Python爬虫实战
<精通Python网络爬虫>|百度网盘免费下载|Python爬虫实战 提取码:7wr5 内容简介 为什么写这本书 网络爬虫其实很早就出现了,最开始网络爬虫主要应用在各种搜索引擎中.在搜索引 ...
- Python即时网络爬虫项目启动说明
作为酷爱编程的老程序员,实在按耐不下这个冲动,Python真的是太火了,不断撩拨我的心. 我是对Python存有戒备之心的,想当年我基于Drupal做的系统,使用php语言,当语言升级了,推翻了老版本 ...
- Python即时网络爬虫项目: 内容提取器的定义(Python2.7版本)
1. 项目背景 在Python即时网络爬虫项目启动说明中我们讨论一个数字:程序员浪费在调测内容提取规则上的时间太多了(见上图),从而我们发起了这个项目,把程序员从繁琐的调测规则中解放出来,投入到更高端 ...
- Python即时网络爬虫项目: 内容提取器的定义
1. 项目背景 在python 即时网络爬虫项目启动说明中我们讨论一个数字:程序员浪费在调测内容提取规则上的时间,从而我们发起了这个项目,把程序员从繁琐的调测规则中解放出来,投入到更高端的数据处理工作 ...
- python爬虫项目-爬取雪球网金融数据(关注、持续更新)
(一)python金融数据爬虫项目 爬取目标:雪球网(起始url:https://xueqiu.com/hq#exchange=CN&firstName=1&secondName=1_ ...
随机推荐
- Django 配置文件 settings.py
1. dubug配置 DEBUG=False 2. 数据库配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', &qu ...
- 《自拍教程19》ffmpeg_音视频图像转码工具
ffmpeg命令介绍 ffmpeg.exe(linux/imac一般不带后缀,ffmpeg), 是一款音视频编解码的命令行工具软件, 常用于多媒体测试的文件制作与转码. 我们常用的:格式工厂,Medi ...
- CentOS7.3下yum安装MariaDB10.3.12并指定utf8字符集
添加MariaDB的yum源,指定安装的版本,然后使用 yum 工具进行安装 参考MariaDB官方网站对应安装方法和步骤 https://downloads.mariadb.org/mariadb/ ...
- 基于webpack的vue开发环境搭建
1.新建并初始化项目(npm int -y),安装webpack,webpack-cli webpack-dev-server 安装eslint,eslint-plugin-vue,配置eslint语 ...
- 方法中this指向的问题
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Android中点击按钮获取星级评分条的评分
场景 效果 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 将布局改为Lin ...
- 重新安装python后,原来在虚拟环境里的django项目启动报错:dyld: Library not loaded: @executable_path/../.Python Referenced from: /Users/mac/.virtualenvs/WYGBlog-env/bin/python Reason: image not found
因为当你创建一个虚拟环境的时候,一些软链接创建到原来的python上. 当用Homebrew更新python后,原来软连接对应的python已经不存在了. 因此需要把软链接指向新的python. 解决 ...
- 解决本地请求不到json问题,老是出现跨域
有时候我们需要在本地模拟请求json数据,但是老是出现谷歌跨域的问题, 网上找了方法,说是把json和HTML文件放在同一目录,还有的是页面引用时加上callback的. 但是不知道咋的,可能是本人不 ...
- Go Web爬虫并发实现
题目:Exercise: Web Crawler 直接参考了 https://github.com/golang/tour/blob/master/solutions/webcrawler.go 的实 ...
- C# Excel导出超出65536行报错 Invalid row number (65536) outside allowable range (0..65535)
C# Excel导出超出65536行报错 Invalid row number (65536) outside allowable range (0..65535) 一:报错 Invalid row ...