javascript - how - instagram developer register
¿Cómo realizar el raspado web de Instagram no autenticado en respuesta a los cambios recientes de la API privada? (4)
Meses atrás, Instagram comenzó a inutilizar su API pública al eliminar la mayoría de las funciones y negarse a aceptar nuevas aplicaciones para la mayoría de los ámbitos de permisos. Esta semana se hicieron más cambios que restringen aún más las opciones del desarrollador.
Muchos de nosotros hemos recurrido a la API web privada de Instagram para implementar la funcionalidad que teníamos anteriormente. Un ping/instagram_private_api destacado logra reconstruir la mayor parte de la funcionalidad anterior, sin embargo, con los cambios anunciados públicamente esta semana, Instagram también realizó cambios subyacentes en su API privada, que requieren variables mágicas, agentes de usuario y hash MD5 para realizar raspado web peticiones posibles. Esto se puede ver siguiendo las versiones recientes en el repositorio de git vinculado anteriormente , y los cambios exactos necesarios para continuar recuperando datos se pueden ver aquí .
Estos cambios incluyen:
- Persistiendo el token de agente de usuario y CSRF entre solicitudes.
- Realizar una solicitud inicial a
https://instagram.com/
para obtener una clave mágicarhx_gis
del cuerpo de respuesta. - Configurando el encabezado
X-Instagram-GIS
, que se forma mediante la concatenación mágica de la claverhx_gis
y las variables de consulta antes de pasarlas a través de un hash MD5.
Cualquier cosa menor que esto dará lugar a un error 403. Estos cambios se han implementado con éxito en el repositorio anterior , sin embargo, mi intento en JS sigue fallando. En el siguiente código, estoy intentando recuperar las primeras 9 publicaciones de la línea de tiempo de un usuario. Los parámetros de consulta que determinan esto son:
-
query_hash
de42323d64886122307be10013ad2dcc44
(obtener los medios de la línea de tiempo del usuario). -
variables.id
de cualquier ID de usuario como una cadena (el usuario para obtener los medios de comunicación). -
variables.first
, el número de publicaciones que se van a buscar, como un entero.
Anteriormente, esta solicitud se podía realizar sin ninguno de los cambios anteriores simplemente RECIBIENDO de https://www.instagram.com/graphql/query/?query_hash=42323d64886122307be10013ad2dcc44&variables=%7B%22id%22%3A%225380311726%22%2C%22first%22%3A1%7D
, ya que la URL no estaba protegida.
Sin embargo, mi intento de implementar la funcionalidad para escribir correctamente en el repositorio anterior no funciona, y solo recibo 403 respuestas de Instagram. Estoy usando superagent como mis bibliotecas de solicitudes, en un entorno de nodo.
/*
** 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, csrfTokenCookie, 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`
}));
¿Qué más debo intentar? ¿Qué hace que mi código falle, y el código proporcionado en el repositorio anterior funciona bien?
Actualización (2018-04-17)
Por lo menos por tercera vez en una semana, Instagram ha actualizado nuevamente su API. El cambio ya no requiere que el token CSRF forme parte de la firma hash.
La pregunta anterior se ha actualizado para reflejar esto.
Actualización (2018-04-14)
Instagram ha actualizado de nuevo su API graphql privada. En lo que a nadie se le ocurre:
- User Agent ya no es necesario que se incluya en el cálculo de
X-Instagram-Gis
md5.
La pregunta anterior se ha actualizado para reflejar esto.
Valores para persistir
No está persistiendo el agente de usuario (un requisito) en la primera consulta a Instagram:
const initResponse = await superagent.get(''https://www.instagram.com/'');
Debiera ser:
const initResponse = await superagent.get(''https://www.instagram.com/'')
.set(''User-Agent'', userAgent);
Esto debe persistir en cada solicitud, junto con la cookie csrftoken
.
Generación de encabezados X-Instagram-GIS
Como muestra su respuesta, debe generar el encabezado X-Instagram-GIS
partir de dos propiedades, el valor rhx_gis
que se encuentra en su solicitud inicial y las variables de consulta en su próxima solicitud. Estos deben ser hash md5, como se muestra en su función anterior:
const generateRequestSignature = function(rhxGis, queryVariables) {
return crypto.createHash(''md5'').update(`${rhxGis}:${queryVariables}`, ''utf8'').digest("hex");
};
Entonces para llamar a la consulta de instagram necesitas generar el encabezado x-instagram-gis .
Para generar este encabezado debe calcular un hash md5 de la siguiente cadena " {rhx_gis}: {ruta} ". El valor de rhx_gis se almacena en el código fuente de la página de Instagram en la variable window._sharedData global js.
Ejemplo:
Si intenta OBTENER una solicitud de información de usuario como esta https://www.instagram.com/ {username} /? __ a = 1
Debe agregar el encabezado http x-instagram-gis para solicitar qué valor es
MD5("{rhx_gis}:/{username}/")
Esto está probado y funciona al 100%, así que no dude en preguntar si algo sale mal.
Uhm ... no tengo Node instalado en mi máquina, por lo que no puedo verificarlo con seguridad, pero me parece que falta una parte crucial de los parámetros en la cadena de consulta, ese es el campo siguiente:
const queryVariables = JSON.stringify({
id: "123456789",
first: 4,
after: "YOUR_END_CURSOR"
});
De esas queryVariables
dependen de su hash MD5, eso, entonces, no coincide con el esperado. Intenta eso: espero que funcione.
EDITAR:
Leyendo cuidadosamente su código, desafortunadamente no tiene mucho sentido. Deduzco que está intentando obtener la secuencia completa de imágenes del feed de un usuario.
Entonces, lo que debe hacer es no llamar a la página de inicio de Instagram como lo está haciendo ahora ( superagent.get(''https://www.instagram.com/'')
), sino a la corriente del usuario ( superagent.get(''https://www.instagram.com/your_user'')
).
Cuidado: debe codificar el mismo agente de usuario que usará a continuación (y no parece que usted sea ...).
Luego, debe extraer el ID de consulta (no está codificado, cambia cada pocas horas, a veces minutos; codificarlo es una tontería; sin embargo, para este POC, puede mantenerlo codificado) y el cursor final. Para el cursor final me gustaría algo como esto:
const endCursor = (RegExp(''end_cursor":"([^"]*)"'', ''g'')).exec(initResponse.text)[1];
Ahora tienes todo lo que necesitas para hacer la segunda solicitud:
const queryVariables = JSON.stringify({
id: "123456789",
first: 9,
after: endCursor
});
const signature = generateRequestSignature(rhxGis, csrfTokenCookie, queryVariables);
const res = await superagent.get(''https://www.instagram.com/graphql/query/'')
.query({
query_hash: ''42323d64886122307be10013ad2dcc44'',
variables: queryVariables
})
.set({
''User-Agent'': userAgent,
''Accept'': ''*/*'',
''Accept-Language'': ''en-US'',
''Accept-Encoding'': ''gzip, deflate'',
''Connection'': ''close'',
''X-Instagram-GIS'': signature,
''Cookie'': `rur=${rurCookie};csrftoken=${csrfTokenCookie};mid=${midCookie};ig_pr=1`
}).send();
query_hash
no es constante y sigue cambiando con el tiempo.
Por ejemplo, los scripts de ProfilePage incluían estos scripts:
https://www.instagram.com/static/bundles/base/ConsumerCommons.js/9e645e0f38c3.js https://www.instagram.com/static/bundles/base/Consumer.js/1c9217689868.js
El hash se encuentra en uno de los scripts anteriores, por ejemplo, para edge_followed_by
:
const res = await fetch(scriptUrl, { credentials: ''include'' });
const rawBody = await res.text();
const body = rawBody.slice(0, rawBody.lastIndexOf(''edge_followed_by''));
const hashes = body.match(/"/w{32}"/g);
// hashes[hashes.length - 2]; = edge_followed_by
// hashes[hashes.length - 1]; = edge_follow