首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

  • 24-11-26 09:05
  • 2729
  • 10594
juejin.cn

前情提要

如何下载Instagram视频/图片(一)

基于上一篇的分析,实现一个Instagram下载网站,只要复制帖子的链接到网站,就可以支持图文的显示和下载。

image.png

TL;DR

项目地址:github.com/newwangzhic… 在线演示:instagram-downloader-ten.vercel.app/

流程

由于需要通过接口请求IG的数据,为了突破跨域的限制,需要服务器帮忙代理请求并将数据解析后返回给前端显示,所以项目采用Nextjs一把梭,以下是用户请求的基本流程: image.png

1. shortcode解析

IG的分享链接一般是这样的www.instagram.com/p/DBwQQlRt_… ,其中的DBwQQlRt_2q就是shortcode;p/tv/reel/stories等代表帖子的类型。这里先讨论前三种,stories类型需要特殊处理,下章再表。

首先需要对IG的分享链接验证结构是否合法,http(s),www和searchParams部分都不是必须的;通过一条正则可以直接判断/^(https?:\/\/)?(www\.)?instagram\.com\/(p|reel|tv)\/[a-zA-Z0-9_\-]+(\/(\?[^#]*)?)?(#.*)?$/

验证合法后就可以提取出shortcode了,const matcher = /\/(?:p|reel|tv)\/([a-zA-Z0-9_\-]+)/,shortcode就保存在matcher[1]中

2. 服务端请求

提取到shortcode后就可以直接通过/api/graphql这个POST接口请求IG数据了,上一章有介绍;此处不表。这里直接贴出需要的header和body

js
代码解读
复制代码
// header const headers = { Accept: '*/*', 'Accept-Language': 'en-US,en;q=0.5', 'Content-Type': 'application/x-www-form-urlencoded', 'X-FB-Friendly-Name': 'PolarisPostActionLoadPostQueryQuery', 'X-CSRFToken': 'RVDUooU5MYsBbS1CNN3CzVAuEP8oHB52', 'X-IG-App-ID': '1217981644879628', 'X-FB-LSD': 'AVqbxe3J_YA', 'X-ASBD-ID': '129477', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin', 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36' } // body const requestData = { av: '0', __d: 'www', __user: '0', __a: '1', __req: '3', __hs: '19624.HYP:instagram_web_pkg.2.1..0.0', dpr: '3', __ccg: 'UNKNOWN', __rev: '1008824440', __s: 'xf44ne:zhh75g:xr51e7', __hsi: '7282217488877343271', __dyn: '7xeUmwlEnwn8K2WnFw9-2i5U4e0yoW3q32360CEbo1nEhw2nVE4W0om78b87C0yE5ufz81s8hwGwQwoEcE7O2l0Fwqo31w9a9x-0z8-U2zxe2GewGwso88cobEaU2eUlwhEe87q7-0iK2S3qazo7u1xwIw8O321LwTwKG1pg661pwr86C1mwraCg', __csr: 'gZ3yFmJkillQvV6ybimnG8AmhqujGbLADgjyEOWz49z9XDlAXBJpC7Wy-vQTSvUGWGh5u8KibG44dBiigrgjDxGjU0150Q0848azk48N09C02IR0go4SaR70r8owyg9pU0V23hwiA0LQczA48S0f-x-27o05NG0fkw', __comet_req: '7', lsd: 'AVqbxe3J_YA', jazoest: '2957', __spin_r: '1008824440', __spin_b: 'trunk', __spin_t: '1695523385', fb_api_caller_class: 'RelayModern', fb_api_req_friendly_name: 'PolarisPostActionLoadPostQueryQuery', variables: JSON.stringify({ shortcode: this.shortcode, fetch_comment_count: 'null', fetch_related_profile_media_count: 'null', parent_comment_count: 'null', child_comment_count: 'null', fetch_like_count: 'null', fetch_tagged_user_count: 'null', fetch_preview_comment_count: 'null', has_threaded_comments: 'false', hoisted_comment_id: 'null', hoisted_reply_id: 'null' }), server_timestamps: 'true', doc_id: '10015901848480474' } const body = new URLSearchParams(requestData).toString()

3. 解析出图文链接

接口返回的帖子相关的数据保存在data.xdt_shortcode_media中,称之为mediaData,如果shortcode是错误的或者请求出现限制(如私有帖),mediaData就可能是null或带有错误信息的string。

多个图片/视频

一个帖子可能是单图片/视频。也可能是多个。如果是多个图片/视频,那么所有资源保存在edge_sidecar_to_children中。其中mediaData.edge_sidecar_to_children.edges[i].node也是mediaData的结构。所以我们可以通过递归解析出所有的资源:

ts
代码解读
复制代码
public formatToResourceInfo(mediaData?: MediaData): ResourceInfo[] { if (!mediaData) { return [] } const nodes = mediaData.edge_sidecar_to_children if (nodes) { return nodes.edges.map((node) => ({ ...this.formatToResourceInfo(node.node)[0] })) } if (mediaData.is_video) { return [ { filename: `ig-video-${dayjs().format('YYYY-MM-DDTHH:mm:ss')}.mp4`, width: mediaData.dimensions.width, height: mediaData.dimensions.height, url: mediaData.video_url, type: 'Video' } ] } else { const imageData = mediaData.display_resources.at(-1) return [ { filename: `ig-img-${dayjs().format('YYYY-MM-DDTHH:mm:ss')}.jpeg`, width: imageData?.config_width ?? 0, height: imageData?.config_height ?? 0, url: imageData?.src ?? '', type: 'Image' } ] } }

视频

mediaData.is_video直接告诉了你当前资源是否是视频,视频链接可以直接通过mediaData.video_url获取

图片

mediaData.is_video为false既表示当前资源是图片,其中display_resources数组保存了不同分辨率的图片链接。我们可以直接通过mediaData.display_resources.at(-1).src拿到最高清的一张

4. 显示预览

如果顺利,我们把解析好的数据返回给前端就可以顺利显示IG贴的图片和视频了

视频

视频是mp4格式。放到video中可以直接。

为了让设备可以有“下载”的按钮,可以将链接放在source中

tsx
代码解读
复制代码
className="w-full h-[400px] rounded-b" controls playsInline={true} preload="metadata" muted loop={true} > <source src={info.url} type="video/mp4" />

IOS设备由于系统的限制,是不会显示下载,且无法直接保存到相册中的。只能退而求其次下载链接资源中,使用a标签创建一个下载链接

tsx
代码解读
复制代码
href} download> Long Press Save

图片

很不幸,图片有跨域限制,这代表如果直接把链接直接放在img标签中将无法显示。

同样,我们可以使用服务器帮助我们请求到图片,再通过base64字符串或流传输到前端。

那么有什么方法可以直接前端显示跨域图片吗?还真有!

首先,前端是无法直接显示有跨域限制的图片的。但是我们可以白嫖谷歌Translate的代理服务,它会帮忙代理图片,将header设置成非跨域的。只需要将图片链接转换一下格式就行了。

比如这个图片https://scontent-iad3-2.cdninstagram.com/v/t51.29350-15/465229499_861965882787711_606795556211070811_n.heic?stp=dst-jpg_e35&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMDM3eDEwMzcuc2RyLmYyOTM1MC5kZWZhdWx0X2ltYWdlIn0&_nc_ht=scontent-iad3-2.cdninstagram.com&_nc_cat=103&_nc_ohc=J7-gdDYU9-cQ7kNvgE-u_hO&_nc_gid=78f37573b1be4eaeb6b9c1a323aea5a3&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYD4OLzRnF9umh6cplyabc_zxwQhsPrzSc2QuU-8tiAxaA&oe=6747FF23&_nc_sid=10d13b,是无法直接在我们的网站下使用img显示的。,但是我们将前半部分改造成https://scontent--iad3--2-cdninstagram-com.translate.goog/,经过代理的这张图片是没有跨域限制的,这样我们就可以直接预览和保存啦!改造后完整的链接:https://scontent--iad3--2-cdninstagram-com.translate.goog/v/t51.29350-15/465229499_861965882787711_606795556211070811_n.heic?stp=dst-jpg_e35&efg=eyJ2ZW5jb2RlX3RhZyI6ImltYWdlX3VybGdlbi4xMDM3eDEwMzcuc2RyLmYyOTM1MC5kZWZhdWx0X2ltYWdlIn0&_nc_ht=scontent-iad3-2.cdninstagram.com&_nc_cat=103&_nc_ohc=J7-gdDYU9-cQ7kNvgE-u_hO&_nc_gid=78f37573b1be4eaeb6b9c1a323aea5a3&edm=APs17CUBAAAA&ccb=7-5&oh=00_AYD4OLzRnF9umh6cplyabc_zxwQhsPrzSc2QuU-8tiAxaA&oe=6747FF23&_nc_sid=10d13b,可以试试。

转换的代码如下,感谢开源项目corsDown:

ts
代码解读
复制代码
export function toCorsUrl(url: string): string { const p = url.split('/') let t = '' for (let i = 0; i < p.length; i++) { if (i == 2) { t += p[i].replaceAll('-', '--').replaceAll('.', '-') + atob('LnRyYW5zbGF0ZS5nb29n') + '/' } else { if (i != p.length - 1) { t += p[i] + '/' } else { t += p[i] } } } return encodeURI(t) }

接下来

stories的解析和p/reel还略有不同,是通过用户id直接获取的。敬请期待下一章...

注:本文转载自juejin.cn的jaywangep的文章"https://juejin.cn/post/7440477979761754147"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

104
前端
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top