単に、指定したセレクタが複数あるだけかも。例えばinput[name="password"]が2つあるときに、await page.typeやawait page.focusなどは上記エラーが出る(ことがある)。そういうときはclass: ElementHandleでオブジェクトのリストを取得し、リストの番号で目的のオブジェクトを操作すればよい。例えば2つ目を扱いたいなら、
let items = await page.$$('input[name="password"]'); items[1].click();
pageクラスと書式が同じものもあれば違うものもある。例えばelementHandle.typeは、セレクタを引数に取らない。puppeteer/api.md at master · GoogleChrome/puppeteer · GitHub
スマートメーターの情報をグラフで表示するページを電力各社は提供している。私が契約している「あしたでんき」は、データをCSV等でダウンロードできないので、画面から取得している。当初、Greasemonkeyで試したが失敗したので、Puppeteerに切り替えた。
このサンプルでは、前月のデータをTSVで書き出している。当月を取得したいなら前月に移動するコードを削除すればよい。
const puppeteer = require('puppeteer'); const fs = require('fs'); const cheerio = require('cheerio'); const path = require('path'); let tsvfile = path.join(__dirname, 'ashitadenki.tsv'); (async() => { const browser = await puppeteer.launch({ headless: false, args: [ '--no-sandbox', ] }); const page = await browser.newPage(); await page.setViewport({width: 1024, height: 2048}); try { await page.goto('https://ashita-denki.jp/login/', { waitUntil: 'networkidle2'}); await page.waitForSelector('input#username', {waitUntil: 'networkidle2', timeout: 10000}); await page.focus("input#username"); await page.type("input#username", 'your ID'); await page.focus("input#password"); await page.type("input#password", 'your password'); await page.waitFor(1000); await page.click('input[type=submit]'); await page.waitForNavigation({waitUntil: 'load'}); await page.waitForSelector('#monthly-report-table', {waitUntil: 'networkidle2', timeout: 2500}); var now = new Date(); var month = now.getMonth(); //1月は0 // 前の月を表示する await page.waitForSelector('#month > form > div:nth-child(1) > select:nth-child(2)'); await page.select('#month > form > div:nth-child(1) > select:nth-child(2)', month.toFixed()); await page.waitFor(3000); await page.waitForSelector('#monthly-report-table', {waitUntil: 'networkidle2', timeout: 2500}); let html = await page.$eval('#monthly-report-table > ul', e => e.outerHTML); $ = cheerio.load(html); let lines = []; $('li').each(function(){ let row = []; // #monthly-report-table > ul > li:nth-child(1) > time row.date = $(this).find('time').text().trim(); if (row.date == '') { return true; //$().eachのnext } row.date = row.date.replace(/日/,''); row.date = month.toFixed() + '/' + row.date; // #monthly-report-table > ul > li:nth-child(1) > ul > li.kwh > span row.kw = $(this).find('ul > li.kwh > span').text().trim(); // #monthly-report-table > ul > li:nth-child(1) > ul > li.cost > span row.cost = $(this).find('ul > li.cost > span').text().trim(); lines.push(Object.values(row).join("\t")); }); fs.writeFile(tsvfile, lines.join("\n"),(err) => { if (err) throw err; }); } catch (e) { console.log('caught'); process.stderr.write(e.toString()); await browser.close(); process.exit(1); } await browser.close(); })();
//table内にあるtableを削除 $('table table').remove(); //8列目が存在しない行を削除。全ての行の内、8列目が不存在の行を全て削除。 $('tr:not(:has(td:nth-child(8)))').remove(); //8列目が空の全ての行を削除。全ての行の内、8列目が空の行を全て削除。 // [注意] 8列目が存在しないと効果なし。8列目がない行を削除という意味(存否判断)では使えない。 $('tr:has(td:nth-child(8):empty)').remove(); // td.blue以外の全てのtdを削除。全てのtdを、td.blueを除き、削除。 $('td:not(td.blue)').remove(); //一行目(見出し行)を削除 $('tr:nth-child(1)').remove();
use utf8; use strict; use warnings; use Encode; use Devel::Peek; my $str = '%E6%97%85%E8%A1%8C'; # 旅行 my $file = 'test.bin'; my $str_decoded = decode_utf8( &urldecode ($str)); print "basis:$str_decoded\n"; Devel::Peek::Dump($str_decoded); print qq(\n); open (OUT, ">$file"); binmode OUT; print OUT $str; close OUT; # ------------------------------------------------------- # デコードせず読み込み open (IN, "<$file"); #binmode IN; my $str1_from_file = <IN>; close IN; &compare($str,$str1_from_file); print qq(\n); # ------------------------------------------------------- # デコードして読み込み open(IN, '<:utf8', $file); #binmode IN; my $str2_from_file = <IN>; close IN; &compare($str,$str2_from_file); # ------------------------------------------------------- # URLデコード sub urldecode{ my $uri = shift(@_); $uri =~ tr/+/ /; $uri =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg; return $uri; } # 比較 sub compare{ my @sample = @_; # URLデコード前 &sameornot(@sample); # URLデコード my @sample_urldecoded = map {urldecode($_)} @sample; &sameornot(@sample_urldecoded); # 内部表現へのデコード my @sample_decoded = map {decode_utf8($_)} @sample_urldecoded; &sameornot(@sample_decoded); sub sameornot { $_[0] eq $_[1] ? print "same\n" : print "differnet\n"; } # 正規表現 if ($sample_decoded[1] =~ /旅行/) { print "1) regex works\n"; } # 念のためdecode_utf8前で試す if ($sample_urldecoded[1] =~ /旅行/) { print "2) regex works\n"; } print "compared:$sample_decoded[1]\n"; print "before decode_utf8\n"; Devel::Peek::Dump($sample_urldecoded[1]); print "after decode_utf8\n"; Devel::Peek::Dump($sample_decoded[1]); }
$ perl test2.pl Wide character in print at test2.pl line 12. basis:旅行 SV = PV(0x1018ee0) at 0x1037980 REFCNT = 1 FLAGS = (PADMY,POK,pPOK,UTF8) PV = 0x110a940 "\346\227\205\350\241\214"\0 [UTF8 "\x{65c5}\x{884c}"] CUR = 6 LEN = 16 same same same 1) regex works Wide character in print at test2.pl line 73. compared:旅行 before decode_utf8 SV = PVMG(0x10d7a20) at 0x1037188 REFCNT = 1 FLAGS = (POK,pPOK) IV = 0 NV = 0 PV = 0x1122220 "\346\227\205\350\241\214"\0 CUR = 6 LEN = 16 after decode_utf8 SV = PV(0x1019740) at 0x1037218 REFCNT = 1 FLAGS = (POK,pPOK,UTF8) PV = 0x1097030 "\346\227\205\350\241\214"\0 [UTF8 "\x{65c5}\x{884c}"] CUR = 6 LEN = 16 same same differnet compared:旅行 before decode_utf8 SV = PVMG(0x10d7a50) at 0x10370e0 REFCNT = 1 FLAGS = (POK,pPOK,UTF8) IV = 0 NV = 0 PV = 0x110acb0 "\303\246\302\227\302\205\303\250\302\241\302\214"\0 [UTF8 "\x{e6}\x{97}\x{85}\x{e8}\x{a1}\x{8c}"] CUR = 12 LEN = 16 after decode_utf8 SV = PVMG(0x10d7960) at 0x10371e8 REFCNT = 1 FLAGS = (POK,pPOK,UTF8) IV = 0 NV = 0 PV = 0x1030240 "\303\246\302\227\302\205\303\250\302\241\302\214"\0 [UTF8 "\x{e6}\x{97}\x{85}\x{e8}\x{a1}\x{8c}"] CUR = 12 LEN = 16
printf "<tr><td $tdclr_host>%s<td>%s<td>%s<td>%.60s<td $tdclr_stts>%s<td>%.50s</tr>\n",substr($host,-30),"$mon $date",$clock,$uri,$stts,$ua;
my @printform = split /\n/, <<END; <tr> <td $tdclr_host>%s @{[substr($host,-30)]} %h host, 式展開の記法 <td>%s $mon $date 日付 <td>%s $clock 時刻 <td>%.60s $uri %rからページ名切り出し <td $tdclr_stts>%s $stts status code <td>%.50s $ua User-Agent </tr> END for (@printform) { @_ = split /\t+/; if (defined $_[1]) { printf qq($_[0]),$_[1]; } else { print qq($_[0]); } }
Package namespace installed latest in CPAN file CGI 3.63 4.38 LEEJO/CGI-4.38.tar.gz