質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

1205閲覧

NodeListのchildrenプロパティの中身が空になる

sponge

総合スコア16

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/02/18 14:08

前提・実現したいこと

あるサイト上で定期的に取得したい情報があったのでnode.jsからnpmでpuppeteerをインストールして使っていました。
goto()関数で指定のサイトに飛び、そこからDOMを解析し情報を取得しようと考えていました。
しかし、queryselectorall()関数などを使ってDOMのchildrenなどにアクセスしても中身がありません。
おかしいと思い指定のサイトをchromeで開いて検証ツールで先程の関数などを使ってみるとchildrenの中身は入っていました。

一応puppeteerのwaitforselector()関数を使い取得したいセレクタが生成されてから取得できるようにしていましたが、うまくいきません。
これはなにが原因なのでしょうか?

以下に試したコードを書きます。

javascript

1const items = await page.evaluate( () => { 2 return document.querySelectorAll('.resultitem'); 3}); 4console.log(items[0].children); <-- 複数返ってくるnodelistの内一つを選んでchildrenにアクセスして、またそこからデータを取得したい。

よろしくおねがいします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ブラウザ側で生成保持しているNodeListをnodejs側にそのまま持ってきていじることはできないのでは?
textContentやinnerHTMLなど文字列なら渡せますし、ドキュメント読むとJSONにして取得するメソッドもあるようですよ。

解決方法としてはevaluateの中でやりたいこと(詳しくは知りませんが。)すべて終わらせてから文字列で返すとか・・・

JavaScript

1 const children0Html = await page.evaluate(() => { 2 const items = document.querySelectorAll('.resultitem'); 3 return items[0].children[0].innerHTML; 4 }); 5 console.log(children0Html);

ElementHandleを使って、

JavaScript

1 const items = await page.$$('.resultitem'); 2 const children0Html = await page.evaluate(item => { 3 return item.children[0].innerHTML; 4 }, items[0]); 5 console.log(children0Html);

とか、

JavaScript

1 const items = await page.$$('.resultitem'); 2 const items0Html = await page.evaluate(item => item.innerHTML, items[0]); 3 console.log(items0Html); 4 const children = await items[0].$$('*'); 5 const children0Html = await page.evaluate(child => child.innerHTML, children[0]); 6 console.log(children0Html);

等になるのでしょうか。
しかしやりたいことはブラウザ操作ではなくDOMを解析してquerySelectorで特定の情報を抜き出すことなのですよね?
でしたら私だったらnpm i
<消えたのでここから続き>
でしたら私だったらnpm install jsdomして、chromeにDOM構築させてから文字列で受け取ってあとはnode側でJSDOM使って処理します。以下のように(urlは変えてくださいね)

JavaScript

1const { JSDOM } = require('jsdom'); 2const puppeteer = require('puppeteer'); 3(async () => { 4 const browser = await puppeteer.launch(); 5 const page = await browser.newPage(); 6 await page.goto('https://example.com/'); 7 await page.waitForSelector('.resultitem'); 8 const html = await page.evaluate(() => document.documentElement.outerHTML); 9 browser.close(); 10 11 const document = new JSDOM(html).window.document; 12 const items = document.querySelectorAll('.resultitem'); 13 console.log(items[0].children); 14})();

更にnpm install jqueryして以下のようにするとjQueryも使えますよ。

JavaScript

1const { JSDOM } = require('jsdom'); 2const puppeteer = require('puppeteer'); 3(async () => { 4 const browser = await puppeteer.launch(); 5 const page = await browser.newPage(); 6 await page.goto('https://example.com/'); 7 await page.waitForSelector('.resultitem'); 8 const html = await page.evaluate(() => document.documentElement.outerHTML); 9 browser.close(); 10 11 const window = new JSDOM(html).window; 12 const document = window.document; 13 const $ = require('jquery')(window); 14 15 const $items = $('.resultitem'); 16 console.log($items.children()); 17})();

いつもクライアント側でやるのと同じように書けて分かりやすくないでしょうか?
お求めの答えかは分かりませんが、参考になりましたら幸いです。

投稿2019/02/18 18:41

編集2019/02/18 18:51
shinji709

総合スコア805

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sponge

2019/02/19 05:10

回答ありがとうございます。 shinji709様のお答え頂いた考え方であっています。 早速回答頂いた ```javascript const { JSDOM } = require('jsdom'); const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://example.com/'); await page.waitForSelector('.resultitem'); const html = await page.evaluate(() => document.documentElement.outerHTML); browser.close(); const document = new JSDOM(html).window.document; const items = document.querySelectorAll('.resultitem'); console.log(items[0].children); })(); ``` を適宜URL等を直して試してみましたが、私が書いていたコードと同様に、nodelistは取得できていませんでした。 結果は ``` NodeList { '0': HTMLDivElement {}, '1': HTMLDivElement {}, '2': HTMLDivElement {}, '3': HTMLDivElement {}, '4': HTMLDivElement {} } ``` の様になり、肝心の中身が取得できません。 結果を見る限り私の取得したい要素数は取れているので、それ以降のデータはnode.jsの仕様上取れないのでしょうか? chromeの検証用ツールでは問題なくデータが取得できているのでshinji709様のおっしゃる通り、ブラウザ側で生成保持しているNodeListをnodejs側にそのまま持ってきていじることはできないのではないのかと思います。
shinji709

2019/02/19 06:12

> nodelistは取得できていませんでした。 とおっしゃっていますが、 > 結果は > ``` > NodeList { > '0': HTMLDivElement {}, > '1': HTMLDivElement {}, > '2': HTMLDivElement {}, > '3': HTMLDivElement {}, > '4': HTMLDivElement {} } > ``` とあるように、NodeListが取得できています。 肝心の中身は取得できていますね。おめでとうございます。 あとはもうDOM APIの知識で、ブラウザでやるのと一緒です。 たとえばconsole.log(items[0].children);の次の行に console.log(items[0].children[0].outerHTML);とでも書いてみてください。何が見えますか?
sponge

2019/02/19 16:45

本当だっ!できました! ありがとうございます。 今回の問題解決のキモはouterHTMLを利用して、どんどん自分の知りたいnodeまで掘り下げていくという感じですね。 最後にふと思った疑問なのですが ``` const items = await page.$$('.resultitem'); const items0Html = await page.evaluate(item => item.innerHTML, items[0]); console.log(items0Html); ``` の item.innerHTML の innerHTML を消してみると、普通は変数itemsの中身にnodelistがあるはずなので、3行目のコンソールではオブジェクトの中身が表示されても良いと思うのですが、 {}のような表示しかなく、データが取得できていないと思っていました。 これはこういう仕様なのか、なにか私が勘違いしているのでしょうか? 度々の質問ですがよろしくおねがいします!
shinji709

2019/02/20 01:39

> 普通は変数itemsの中身にnodelistがあるはずなので、3行目のコンソールではオブジェクトの中身が表示されても良いと思うのですが、 普通って、ブラウザの開発者ツールでやると、ってことですか?それだったらブラウザが分かりやすく忖度して表示してくれてるためですね。nodeでやる場合、たいてい標準出力に出すことになるので、console.logするときは文字列にして出すようにしたほうがいいです。 上で例としてouterHTML使ったのは単に、文字列返すからです。別にinnerHTMLでもtextContentでもgetAttribute("<<属性名>>")でもなんでもいいです。 ブラウザのほうが分かりやすいならブラウザで目的の操作ができるコードを作ってからnodeの方に移したらいいのでは。
sponge

2019/02/20 17:48

先程outerHTMLやgetAttributeなどで試してみると普通に動作しました! 何度も丁寧に教えていただきありがとうございました。 もっともっと勉強していきたいと思います!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問