如何执行未经验证的Instagram网页抓取以响应最近的私有API更改?

Rea*_*ues 26 javascript web-scraping instagram instagram-api

几个月前,Instagram开始通过删除大多数功能并拒绝接受大多数权限范围的新应用程序来使其公共API无法运行.本周进行了进一步的修改,进一步限制了开发商的选择.

我们中的许多人已转向使用Instagram的私有Web API来实现我们以前的功能.一个突出的ping/instagram_private_api设法重建大部分先前的功能,但是,随着本周公开宣布的更改,Instagram也对其私有API进行了基础更改,需要魔术变量,用户代理和MD5哈希来进行网络抓取请求可能.这可以通过跟踪先前链接的git存储库上的最新版本来看到,并且可以在此处看到继续获取数据所需的确切更改.

这些变化包括:

  • 在请求之间保留用户代理和CSRF令牌.
  • 发出初始请求以从响应正文中https://instagram.com/获取rhx_gis魔术密钥.
  • 设置X-Instagram-GIS标头,它是通过rhx_gis在传递MD5哈希之前神奇地连接键和查询变量而形成的.

任何小于此值都会导致403错误.这些更改已在上述存储库中成功实现,但是,我在JS中的尝试仍然失败.在下面的代码中,我试图从用户时间轴中获取前9个帖子.确定这个的查询参数是:

  • query_hash42323d64886122307be10013ad2dcc44(取从用户的时间轴媒体).
  • variables.id 任何用户ID作为字符串(从中获取媒体的用户).
  • variables.first,要获取的帖子数,作为整数.

以前,https://www.instagram.com/graphql/query/?query_hash=42323d64886122307be10013ad2dcc44&variables=%7B%22id%22%3A%225380311726%22%2C%22first%22%3A1%7D由于URL不受保护,因此可以通过简单地从GET中获取此请求而无需任何上述更改.

但是,我尝试实现在上述存储库中成功编写的功能不起作用,我只收到来自Instagram的403个回复.我在节点环境中使用superagent作为我的请求库.

/*
** Retrieve an arbitrary cookie value by a given key.
*/
const getCookieValueFromKey = function(key, cookies) {
        const cookie = cookies.find(c => c.indexOf(key) !== -1);
        if (!cookie) {
            throw new Error('No key found.');
        }
        return (RegExp(key + '=(.*?);', 'g').exec(cookie))[1];
    };

/*
** Calculate the value of the X-Instagram-GIS header by md5 hashing together the rhx_gis variable and the query variables for the request.
*/
const generateRequestSignature = function(rhxGis, queryVariables) {
    return crypto.createHash('md5').update(`${rhxGis}:${queryVariables}`, 'utf8').digest("hex");
};

/*
** Begin
*/
const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0.1 Safari/604.3.5';

// Make an initial request to get the rhx_gis string
const initResponse = await superagent.get('https://www.instagram.com/');
const rhxGis = (RegExp('"rhx_gis":"([a-f0-9]{32})"', 'g')).exec(initResponse.text)[1];

const csrfTokenCookie = getCookieValueFromKey('csrftoken', initResponse.header['set-cookie']);

const queryVariables = JSON.stringify({
    id: "123456789",
    first: 9
});

const signature = generateRequestSignature(rhxGis, queryVariables);

const res = await superagent.get('https://www.instagram.com/graphql/query/')
    .query({
        query_hash: '42323d64886122307be10013ad2dcc44',
        variables: queryVariables
    })
    .set({
        'User-Agent': userAgent,
        'X-Instagram-GIS': signature,
        'Cookie': `rur=FRC;csrftoken=${csrfTokenCookie};ig_pr=1`
    }));
Run Code Online (Sandbox Code Playgroud)

我还应该尝试什么?是什么让我的代码失败,上面的存储库中提供的代码工作正常?

更新(2018-04-17)

至少在一周内第3次,Instagram再次更新了他们的API.更改不再需要CSRF令牌构成散列签名的一部分.

上述问题已更新以反映这一点.

更新(2018-04-14)

Instagram再次更新了他们的私有graphql API.至于任何人都可以搞清楚:

  • 不再需要将用户代理包含在X-Instagram-Gismd5计算中.

上述问题已更新以反映这一点.

Ale*_*lex 16

要坚持的价值观

您没有在Instagram的第一个查询中持久保存用户代理(一项要求):

const initResponse = await superagent.get('https://www.instagram.com/');
Run Code Online (Sandbox Code Playgroud)

应该:

const initResponse = await superagent.get('https://www.instagram.com/')
                     .set('User-Agent', userAgent);
Run Code Online (Sandbox Code Playgroud)

这必须与csrftokencookie 一起保存在每个请求中.

X-Instagram-GIS标题生成

当你的回答表明,你必须生成X-Instagram-GIS从两个属性,标题rhx_gis这是在您的初始请求中找到价值,并在你的下一个请求的查询变量.这些必须是md5哈希值,如上面的函数所示:

const generateRequestSignature = function(rhxGis, queryVariables) {
    return crypto.createHash('md5').update(`${rhxGis}:${queryVariables}`, 'utf8').digest("hex");
};
Run Code Online (Sandbox Code Playgroud)

  • 实际上,他们再次更新了api,现在你甚至不需要签名中的csrftoken,只有rhx_gis和变量... @PirateNinja使用相对路径而不是变量,例如对于`https://www.instagram.com/durov/ `它会是`/ durov /`(不要忘记斜线) (5认同)
  • 在初次请求时使用相同的useragent到https:// www.instagram.com /`?因为我在你的例子中看不到 (3认同)