NET/www/webプログラミング
の編集
https://over.6pb.info/wiki/?&8694a1ee7e
[
トップ
] [
編集
|
差分
|
履歴
|
添付
|
リロード
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
(no template pages)
#contents *Puppeteer [#puppeteer] -[[Amexの明細(速報)の自動取得>生活/クレジットカード/為替手数料#amex]] -パズル認証や2段階認証は、正面から突破するより、1回は手動で補助してログインし、cookieをやりとりすれば、たいていはしばらく追加認証を回避できる。(毎回、追加認証を課すサイトには通用しない。)cookieはPuppeteerのコードで明示的に取得・保存する必要がある。headlessブラウザに(自動的に)保存されるものは助けにならないようだ。(サイトによって違うかも。) --(毎回の)パズル認証を(簡単に)回避できたサイト: ---三井住友カード -headlessだとうまく巡回できない(headlessを無効にすれば順調な)サイトもある。例えば、三井住友カードはheadlessだといまのところ巡回に成功していない。headlessで動かないと、VPSのCentOSで動かすのが面倒だ(普通、クラウドのCentOSにX Windowは入れない。)。cronで定期的に実行するには、VPSが望ましい。次善の策として、新たに、自宅サーバーにGNOME Desktop付きのCentOS 7を入れた。(CentOS 6ではheadless Chromeは動かないようだ。)これでいまのところ三井住友カードも自動巡回できている。 -cronで動かすならtry-catchを書かないと、時間切れなど何か予期せぬことが起きる度にメモリに貯まっていく。自動終了しない。 -[[Webサイトのクローリングやスクリーンショット撮影が簡単にできるヘッドレスChromeがGCPに登場 - GIGAZINE:https://gigazine.net/news/20180818-gcp-node-puppeteer-headless-chrome/]] **Error: Node is either not visible or not an HTMLElement [#l84b32ff] 単に、指定したセレクタが複数あるだけかも。例えばinput[name="password"]が2つあるときに、await page.typeやawait page.focusなどは上記エラーが出る(ことがある)。そういうときは[[class: ElementHandle:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-elementhandle]]でオブジェクトのリストを取得し、リストの番号で目的のオブジェクトを操作すればよい。例えば2つ目を扱いたいなら、 #pre{{ let items = await page.$$('input[name="password"]'); items[1].click(); }} pageクラスと書式が同じものもあれば違うものもある。例えばelementHandle.typeは、セレクタを引数に取らない。[[puppeteer/api.md at master · GoogleChrome/puppeteer · GitHub:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandletypetext-options]] **sample [#d2d8a5f6] ***新電力 あしたでんき [#add6d35d] スマートメーターの情報をグラフで表示するページを電力各社は提供している。私が契約している「あしたでんき」は、データをCSV等でダウンロードできないので、画面から取得している。当初、Greasemonkeyで試したが失敗したので、Puppeteerに切り替えた。 このサンプルでは、前月のデータをTSVで書き出している。当月を取得したいなら前月に移動するコードを削除すればよい。 #pre{{ 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(); })(); }} *Node.js [#p5f10848] **cheerio [#xf897b68] -不要な要素をcheerioのメソッドで取り除く例: #pre{{ //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(); }} *文字コード [#ff17ab4d] **Perl [#s7554a38] &aname(urlencode); -(先ず簡単な方から。)URLエンコードされている文字列を取り扱う(例えばApacheのログファイルの解析)には、単にURLデコードしただけでは駄目。URLデコード後、直ちにPerlの内部表現に「デコード」する。さもないと他の文字列と整合性がとれなくなる。例えば、一般に全ての文字列は、出力の際、エンコードする。URLデコードした文字列について、デコードされる前にエンコードされると二重エンコードになり、文字化けする。 -(これは意外だった。)URLエンコードされている文字列を含むファイル(例えばApacheのログファイル)を読み取るとき、「&pre(open(IN, '<:utf8'...);」や「&pre(use open IN => ":utf8";);」でデコードすると、(その後、Encode::decode_utf8しようがしまいが)スクリプトのリテラル(&pre(use utf8;);前提)と(正規表現や文字列比較演算子で)マッチしなくなる。リテラルによる正規表現や文字列比較演算子が使えない。文字化けはないので、上記より気付きにくい。use open INやbinmode INを便利なプラグマとして、雛形として使っていることは少なくないのでは。 --「&pre(<:utf8);」ではなく「&pre(<:encoding(UTF-8));」でも変わらず。「:utf8 は、さらなるチェックなしにデータが UTF-8 としてマークしますが、 :encoding(UTF-8) はデータが実際に有効な UTF-8 かどうかをチェックします。 」[[perlfunc - Perl 組み込み関数 - perldoc.jp:http://perldoc.jp/docs/perl/5.24.1/perlfunc.pod#open]] --「binmode IN, ":utf8";」でも同様に駄目になる。 --下記のコードで試せる。 #pre{{ 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]); } }} --途中「Wide character in print」と言われるが、それが正しい。むしろ3番目の出力箇所で出ないことの方が意外だった。 --このような結果になる。 #pre{{ $ 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 }} *メモ [#xe473190] -[[PYPL PopularitY of Programming Language index:http://pypl.github.io/PYPL.html]] -printfの記法は気が滅入る。表で値を与えてみた。 --普通の書き方 >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; --表で #pre{{ 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]); } } }} -CGIプログラムをroot名義で動かしたいことがある。sudoを使えば可能。当初誤解していたのだが、sudoersに適切な記載をすれば、その条件で、自動的に実行ユーザーが変わるのだと思っていた。(mod_rewriteのように。)正しくはsudoコマンドが必要。sudoコマンドで、対象コマンド等を呼んで初めて実行ユーザーが変わる。 --CGIスクリプトそれ自体をsudoで呼び出し実行する(CGIとして)ことは非現実的なので、CGIスクリプトの中で本命のスクリプトないしコマンドをsudoで呼び出すことになる。sudoersに登録する対象コマンド等は、その本命スクリプトないしコマンドになる。ただし、/bin/cpや/sbin/iptablesのようなコマンドを登録するのは影響範囲が広すぎ、危険だ。反面、スクリプトを守るのが要になるので、「&pre(chown root.root スクリプト);」しておくとよい。 -理由不明の文字化けで困っていたが、PerlのCGIモジュールを更新したら直った。同じコードが別環境で問題なかったので気付いた。CGI::escapeHTML()。2018年 4月26日 #pre{{ Package namespace installed latest in CPAN file CGI 3.63 4.38 LEEJO/CGI-4.38.tar.gz }} -use utf8プラグマは、それより前の行に書いてあるリテラルを内部表現にしない。「Perl パーサーに、現在のレキシカルスコープ内の プログラムのテキストに、UTF-8を許すように伝えます」[[utf8 - ソースコード内に、UTF-8(か、UTF-EBCDIC)を有効/無効にするためのプラグマ - perldoc.jp:http://perldoc.jp/docs/perl/utf8.pod]] 普通は習慣で冒頭に書くのだろうけど、冒頭にスクリプトの内容(コメント)や中核的な定数を書いて、use utf8;はその後に書くミスを犯した。
タイムスタンプを変更しない
#contents *Puppeteer [#puppeteer] -[[Amexの明細(速報)の自動取得>生活/クレジットカード/為替手数料#amex]] -パズル認証や2段階認証は、正面から突破するより、1回は手動で補助してログインし、cookieをやりとりすれば、たいていはしばらく追加認証を回避できる。(毎回、追加認証を課すサイトには通用しない。)cookieはPuppeteerのコードで明示的に取得・保存する必要がある。headlessブラウザに(自動的に)保存されるものは助けにならないようだ。(サイトによって違うかも。) --(毎回の)パズル認証を(簡単に)回避できたサイト: ---三井住友カード -headlessだとうまく巡回できない(headlessを無効にすれば順調な)サイトもある。例えば、三井住友カードはheadlessだといまのところ巡回に成功していない。headlessで動かないと、VPSのCentOSで動かすのが面倒だ(普通、クラウドのCentOSにX Windowは入れない。)。cronで定期的に実行するには、VPSが望ましい。次善の策として、新たに、自宅サーバーにGNOME Desktop付きのCentOS 7を入れた。(CentOS 6ではheadless Chromeは動かないようだ。)これでいまのところ三井住友カードも自動巡回できている。 -cronで動かすならtry-catchを書かないと、時間切れなど何か予期せぬことが起きる度にメモリに貯まっていく。自動終了しない。 -[[Webサイトのクローリングやスクリーンショット撮影が簡単にできるヘッドレスChromeがGCPに登場 - GIGAZINE:https://gigazine.net/news/20180818-gcp-node-puppeteer-headless-chrome/]] **Error: Node is either not visible or not an HTMLElement [#l84b32ff] 単に、指定したセレクタが複数あるだけかも。例えばinput[name="password"]が2つあるときに、await page.typeやawait page.focusなどは上記エラーが出る(ことがある)。そういうときは[[class: ElementHandle:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-elementhandle]]でオブジェクトのリストを取得し、リストの番号で目的のオブジェクトを操作すればよい。例えば2つ目を扱いたいなら、 #pre{{ let items = await page.$$('input[name="password"]'); items[1].click(); }} pageクラスと書式が同じものもあれば違うものもある。例えばelementHandle.typeは、セレクタを引数に取らない。[[puppeteer/api.md at master · GoogleChrome/puppeteer · GitHub:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandletypetext-options]] **sample [#d2d8a5f6] ***新電力 あしたでんき [#add6d35d] スマートメーターの情報をグラフで表示するページを電力各社は提供している。私が契約している「あしたでんき」は、データをCSV等でダウンロードできないので、画面から取得している。当初、Greasemonkeyで試したが失敗したので、Puppeteerに切り替えた。 このサンプルでは、前月のデータをTSVで書き出している。当月を取得したいなら前月に移動するコードを削除すればよい。 #pre{{ 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(); })(); }} *Node.js [#p5f10848] **cheerio [#xf897b68] -不要な要素をcheerioのメソッドで取り除く例: #pre{{ //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(); }} *文字コード [#ff17ab4d] **Perl [#s7554a38] &aname(urlencode); -(先ず簡単な方から。)URLエンコードされている文字列を取り扱う(例えばApacheのログファイルの解析)には、単にURLデコードしただけでは駄目。URLデコード後、直ちにPerlの内部表現に「デコード」する。さもないと他の文字列と整合性がとれなくなる。例えば、一般に全ての文字列は、出力の際、エンコードする。URLデコードした文字列について、デコードされる前にエンコードされると二重エンコードになり、文字化けする。 -(これは意外だった。)URLエンコードされている文字列を含むファイル(例えばApacheのログファイル)を読み取るとき、「&pre(open(IN, '<:utf8'...);」や「&pre(use open IN => ":utf8";);」でデコードすると、(その後、Encode::decode_utf8しようがしまいが)スクリプトのリテラル(&pre(use utf8;);前提)と(正規表現や文字列比較演算子で)マッチしなくなる。リテラルによる正規表現や文字列比較演算子が使えない。文字化けはないので、上記より気付きにくい。use open INやbinmode INを便利なプラグマとして、雛形として使っていることは少なくないのでは。 --「&pre(<:utf8);」ではなく「&pre(<:encoding(UTF-8));」でも変わらず。「:utf8 は、さらなるチェックなしにデータが UTF-8 としてマークしますが、 :encoding(UTF-8) はデータが実際に有効な UTF-8 かどうかをチェックします。 」[[perlfunc - Perl 組み込み関数 - perldoc.jp:http://perldoc.jp/docs/perl/5.24.1/perlfunc.pod#open]] --「binmode IN, ":utf8";」でも同様に駄目になる。 --下記のコードで試せる。 #pre{{ 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]); } }} --途中「Wide character in print」と言われるが、それが正しい。むしろ3番目の出力箇所で出ないことの方が意外だった。 --このような結果になる。 #pre{{ $ 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 }} *メモ [#xe473190] -[[PYPL PopularitY of Programming Language index:http://pypl.github.io/PYPL.html]] -printfの記法は気が滅入る。表で値を与えてみた。 --普通の書き方 >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; --表で #pre{{ 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]); } } }} -CGIプログラムをroot名義で動かしたいことがある。sudoを使えば可能。当初誤解していたのだが、sudoersに適切な記載をすれば、その条件で、自動的に実行ユーザーが変わるのだと思っていた。(mod_rewriteのように。)正しくはsudoコマンドが必要。sudoコマンドで、対象コマンド等を呼んで初めて実行ユーザーが変わる。 --CGIスクリプトそれ自体をsudoで呼び出し実行する(CGIとして)ことは非現実的なので、CGIスクリプトの中で本命のスクリプトないしコマンドをsudoで呼び出すことになる。sudoersに登録する対象コマンド等は、その本命スクリプトないしコマンドになる。ただし、/bin/cpや/sbin/iptablesのようなコマンドを登録するのは影響範囲が広すぎ、危険だ。反面、スクリプトを守るのが要になるので、「&pre(chown root.root スクリプト);」しておくとよい。 -理由不明の文字化けで困っていたが、PerlのCGIモジュールを更新したら直った。同じコードが別環境で問題なかったので気付いた。CGI::escapeHTML()。2018年 4月26日 #pre{{ Package namespace installed latest in CPAN file CGI 3.63 4.38 LEEJO/CGI-4.38.tar.gz }} -use utf8プラグマは、それより前の行に書いてあるリテラルを内部表現にしない。「Perl パーサーに、現在のレキシカルスコープ内の プログラムのテキストに、UTF-8を許すように伝えます」[[utf8 - ソースコード内に、UTF-8(か、UTF-EBCDIC)を有効/無効にするためのプラグマ - perldoc.jp:http://perldoc.jp/docs/perl/utf8.pod]] 普通は習慣で冒頭に書くのだろうけど、冒頭にスクリプトの内容(コメント)や中核的な定数を書いて、use utf8;はその後に書くミスを犯した。
テキスト整形のルールを表示する