Je veux que parseParameter
analyse JSON comme le code suivant.
someCrawledJSCode
est un code JavaScript exploré.
const somecode = 'somevalue'; arr.push({ data1: { prices: [{ prop1: 'hi', prop2: 'hello', }, { prop1: 'foo', prop2: 'bar', }] } });
Je suis en train d'explorer du code JavaScript avec puppeteer et je veux extraire un objet JSON, mais je ne sais pas comment analyser le code JavaScript donné.
Exemple de code JavaScript analysé:
const data = parseParameter(someCrawledJSCode); console.log(data); // data1: {...}
3 Réponses :
Le grattage va être moche. Avec quelques hypothèses sur la chaîne que vous essayez d'analyser, vous pouvez:
Convertissez cette chaîne en JSON valide:
Pour faire cela de manière fiable, vous devez écrire un analyseur qui est tout aussi complexe qu'un analyseur JSON, mais avec certaines hypothèses, il peut probablement être simplifié à ceci:
// Sample data var someCrawledJSCode = ` const somecode = 'somevalue'; arr.push({ data1: { prices: [{ prop1: 'hi', prop2: 'hello', }, { prop1: 'foo', prop2: 'bar', }] } });`; var obj; var notJson = someCrawledJSCode.replace(/\.push\(([^]*?)\)/, (_, notJson) => { // Try to turn the string into valid JSON: // 1. string literals should not be enclosed in single, but double quotes // 2. property names should be enclosed in double quotes // 3. there should be no trailing comma after the last property var json = notJson.replace(/'((\\.|[^\\'])*)'/g, '"$1"') .replace(/(\w+):/g, '"$1":') .replace(/,\s*}/g, "}"); obj = JSON.parse(json); }); console.log(obj);
Les choses peuvent encore mal tourner, mais au moins vous n'utilisez pas eval
. Par exemple, si vous avez une chaîne littérale dont le contenu correspond à (\ w +):
, alors ce qui précède modifierait cette chaîne. On peut bien sûr rendre l'analyse plus fiable ...
Correction d'un problème avec un groupe de capture.
Je déconseille fortement d'évaluer ou d'analyser les données explorées dans Node.js si vous utilisez de toute façon puppeteer pour l'exploration. Lorsque vous utilisez puppeteer, vous disposez déjà d'un navigateur avec un bon bac à sable pour le code JavaScript exécuté dans un autre processus . Pourquoi risquer ce genre d'isolement et «reconstruire» un analyseur dans votre script Node.js? Si votre script Node.js est interrompu, tout votre script échouera. Dans le pire des cas, vous pourriez même exposer votre machine à de sérieux risques lorsque vous essayez d'exécuter du code non approuvé dans votre thread principal.
Au lieu de cela, essayez de faire autant d'analyse que possible dans le contexte de la page . Vous pouvez même faire un appel evil eval
là-bas. Le pire qui puisse arriver? Votre navigateur se bloque ou plante.
Imaginez la page HTML suivante (très simplifiée). Vous essayez de lire le texte qui est placé dans un tableau. La seule information dont vous disposez est qu'il existe un attribut supplémentaire id
qui est défini sur target-data
.
const crawledJsCode = await page.evaluate(() => { const code = document.querySelector('script').innerHTML; // instead of returning this const match = code.match(/some tricky regex which extracts the data you want/); // we run our regex in the browser return match; // and only return the results });
Voici un exemple simple à quoi votre code pourrait ressembler actuellement:
// called via window.dataFound from within the fake Array.prototype.push function await page.exposeFunction('exposedDataFoundFunction', data => { // handle the data in Node.js });
Dans cet exemple, le script extrait le code JavaScript du page. Maintenant, nous avons le code JavaScript de la page et nous n'avons "que" besoin de l'analyser, non? Eh bien, ce n'est pas la bonne approche. N'essayez pas de reconstruire un analyseur à l'intérieur de Node.js. Utilisez simplement le navigateur. Il existe essentiellement deux approches que vous pouvez adopter pour y parvenir dans votre cas.
JSON.parse
, un regex ou eval
(eval uniquement si vraiment nécessaire) Dans cette approche, vous remplacez les fonctions natives du navigateur par vos propres «fausses fonctions». Exemple:
const originalPush = Array.prototype.push; Array.prototype.push = function (item) { if (item && item.id === 'target-data') { const data = item.data; // This is the data we are trying to crawl window.exposedDataFoundFunction(data); // send this data back to Node.js } originalPush.apply(this, arguments); }
Ce code remplace la fonction d'origine Array.prototype.push
par notre propre fonction. Tout fonctionne normalement, mais quand un élément avec notre id cible est poussé dans un tableau, une condition spéciale est déclenchée. Pour injecter cette fonction dans la page, vous pouvez utiliser page.evaluateOnNewDocument . Pour recevoir les données de Node.js, vous devez exposer une fonction au navigateur via page.exposeFunction
:
await page.goto('http://...'); const crawledJsCode = await page.evaluate(() => document.querySelector('script').innerHTML);
La complexité du code du page est, que cela se produise dans un gestionnaire asynchrone ou si la page change le code environnant. Tant que les données cibles poussent les données dans un tableau, nous les obtiendrons.
Vous pouvez utiliser cette approche pour de nombreuses explorations. Vérifiez comment les données sont traitées et remplacez les fonctions de bas niveau traitant les données par votre propre version proxy.
Supposons que la première approche ne fonctionne pas travailler pour une raison quelconque. Les données sont dans une balise de script, mais vous ne pouvez pas les obtenir en utilisant de fausses fonctions.
Ensuite, vous devriez analyser les données, mais pas dans votre environnement Node.js. Faites-le dans le contexte de la page. Vous pouvez exécuter une expression régulière ou utiliser JSON.parse
. Mais faites-le avant de renvoyer les données à Node.js. Cette approche présente l'avantage que si votre code plante votre environnement pour une raison quelconque, ce ne sera pas votre script principal, mais juste votre navigateur qui plante.
Pour donner un exemple code. Au lieu d'exécuter le code à partir de l'exemple de "mauvais code" d'origine, nous le changeons comme suit:
<html> <body> <!--- ... --> <script> var arr = []; // some complex code... arr.push({ id: 'not-interesting-data', data: 'some data you do not want to crawl', }); // more complex code here... arr.push({ id: 'target-data', data: 'THIS IS THE DATA YOU WANT TO CRAWL', // <---- You want to get this text }); // more code... arr.push({ id: 'some-irrelevant-data', data: 'again, you do not want to crawl this', }); </script> <!--- ... --> </body> </html>
Cela ne renverra que les parties du code dont nous avons besoin, qui peuvent alors Soyez plus fruther traité depuis Node.js.
Quelle que soit l'approche que vous choisissez, les deux méthodes sont bien meilleures et plus sûres que d'exécuter du code inconnu dans votre thread principal. Si vous devez absolument traiter les données dans votre environnement Node.js, utilisez une expression régulière pour cela comme indiqué dans la réponse de trincot. Vous ne devez jamais utiliser eval pour exécuter du code non approuvé.
Merci pour une nouvelle suggestion de voie. J'ai résolu ce problème en utilisant evaluer
et en renvoyant arr
.
Je pense que l'utilisation d'un générateur AST comme Esprima ou d'autres outils AST est le moyen le plus simple de lire et de travailler avec le code source.
Honnêtement, si vous trouvez comment exécuter Esprima et générez un «arbre de syntaxe abstraite» à partir du code source, vous trouverez étonnamment facile et simple de lire l'arborescence générée qui représente le code que vous venez d'analyser, et vous trouverez étonnamment facile de lire les informations et de les convertir en tout ce que vous voulez.
Cela peut sembler intimidant au début, mais honnêtement, ce n'est pas le cas. Vous serez surpris: les outils AST comme Esprima ont été conçus exactement pour des objectifs similaires à ceux que vous essayez de faire, afin de vous faciliter la tâche.
Les outils AST sont nés de plusieurs années de recherche sur la façon de lire et de manipuler le code source, je les recommande donc vivement.
Essayez-le!
Pour vous aider à comprendre à quoi ressemblent les différents AST, vous pouvez consulter https://astexplorer.net . C'est très utile pour savoir à quoi ressemblent les arborescences AST de divers outils.
Oh, une dernière chose! Pour parcourir un arbre AST, vous pouvez utiliser quelque chose comme https://github.com/estools/estraverse a>. Cela vous facilitera la vie.
Il existe une solution disponible mais elle est très DÉSÉCURISÉE, d'autant plus que vous récupérez les js d'une source externe en explorant. Vous pouvez essayer d'utiliser
eval
.J'ai pensé à
eval
mais je vais exécuter ce code côté serveur et c'est trop dangereux. Alors je veux une autre solution. Merci d'avoir répondu.