本家の更新が2018年で止まっていたので、最新版に対応しつつGASでも作ってみる。
GASでスクレイピングするなら「Cheeriosgs」が便利!最低賃金APIを作ってみる
いつもどおり、ソースはGithub。
ソースコード
/**
- データクレンジング
*/
function replaces ( str )
{
return getDate( str ).trim()
.replace( / /g, "" )
.replace( / /g, "" )
.replace( /)/g, "" )
.replace( /(/g, "" )
.replace( /[0-9]/g, function ( word )
{
return String.fromCharCode( word.charCodeAt( 0 ) - 0xFEE0 )
} );
}
function getDate ( str )
{
// 日付でなければやらない
if ( str.indexOf( "年" ) == -1 ) return str;
// 年号を西暦に直す。
var ad = -1; // 元年分を減らしておくため-1
years.forEach( function ( year )
{
if ( str.indexOf( year.name ) > -1 )
{
ad += year.start;
str = str.replace( year.name, "" );
}
} );
// 元年は1年
str = str.replace( "元", 1 )
// 西暦を算出して-でつなげる
var split = str.split( "年" ); // 2桁以上の検出に対応
return ( Number( split[ 0 ] ) + ad )
+ "-"
+ split[ 1 ].replace( "月", "-" ).replace( "日", "" );
}
/**
- メイン
*/
const URL = PropertiesService.getScriptProperties().getProperties().url;
const content = UrlFetchApp.fetch( URL ).getContentText();
const $ = Cheerio.load( content );
var result = [];
var pointer = -1;
const EXCLUDE_COLUMN = 3; // 最初の行だけおかしなものがあるので除外
const COLUMNS = 4;
$( "td" ).each( function ( i, td )
{
if ( i < EXCLUDE_COLUMN ) return;
if ( i % COLUMNS == EXCLUDE_COLUMN )
{
result.push( [] );
pointer++;
}
result[ pointer ].push( replaces( $( td ).text() ) );
} );
</div></details>
スクレイピングでつらいのは、スクレイピングの処理ではなく、取得したデータのクレンジング。
具体的には、
- 必要な部分の括り出し
- 不正な値やデータを取り除く、置き換える
これを如何に頑張らないかが重要だと考える。
pythonだととりあえずpandas辺りに入れてしまえ!となるがJavascriptにそこまで求めるのは酷だろうか。
## Cheeriogs
[公式](https://github.com/tani/cheeriogs)
使い方はCheerioと同じなのでJQueryでスクレイピングをしている人にとっては馴染みやすい。
## 当初案
[Github(History)](https://github.com/shimajima-eiji/Hosting/commit/334503222be20d519d5cc5d9fbf88e163c732da9#diff-210a8378ce33326115f9b72aea5adcb2R3-R96)。
<details><summary>ソースコード</summary><div>
/**
- ユーザー定義
*/
var yearsname2ad = {
"元年": 2019,
};
const PROPERTIES = PropertiesService.getScriptProperties().getProperties();
/**
- システムロジック
*/
function __scraping ()
{
var html = UrlFetchApp.fetch( PROPERTIES.url ).getContentText( 'UTF-8' ).replace( /\r?\n/g, "" ).replace( /[0-9]/g, function ( word )
{
return String.fromCharCode( word.charCodeAt( 0 ) - 0xFEE0 )
} );
var start = "
var end = "";
return __cut( html, start, end );
}
var __match = {
tr: /<tr \w(.?)</tr>/g,
td: /<td \w(.?)</td>/g,
year: /\d元年/g,
month: /年\d(.)月/g,
day: /月\d(.)日/g,
run: function ( str, pattern ) { return str.match( __match[ pattern ] ) },
}
function __cut ( str, sep )
{
return str.substring( str.indexOf( sep ) + sep.length, str.length );
}
function __rsubstring ( str, sep )
{
return str.substring( 0, str.indexOf( sep ) );
}
function __getYMD ( str, pattern )
{
var tmp = __match.run( str, pattern )[ 0 ]
switch ( pattern )
{
case "year":
tmp = yearsname2ad[ tmp ];
break;
case "month":
case "day":
tmp = tmp.substring( 1, tmp.length - 1 );
if ( tmp.length == 1 ) tmp = "0" + tmp;
break;
}
return tmp;
}
function __getDate ( str )
{
var year = __getYMD( str, "year" );
var month = __getYMD( str, "month" );
var day = __getYMD( str, "day" );
return year + "-" + month + "-" + day;
}
/**
- メイン
*/
var table = __scraping()
// var table = test()
/**
- データクレンジング
*/
var tr_items = __match.run( table, "tr" );
var td_items = tr_items.map( function ( tr )
{
var tds = __match.run( tr, "td" );
return tds.map( function ( td )
{
var clean_td = __rsubstring( __cut( td, ">" ), "<" )
.trim()
.replace( / /g, "" )
.replace( / /g, "" )
.replace( /(/g, "" )
.replace( /)/g, "" )
;
if ( clean_td.indexOf( "年" ) > -1 ) clean_td = __getDate( clean_td );
return clean_td;
} );
} );
td_items.shift();
</div></details>
このように、うまいパターンマッチングを考えるしか方法が思いつかなかった。
なお、`getDate`関連と`str.replace`をリファクタリングしているので、そこは目を瞑ってほしい。
# 動くもの
[Web版](https://shimajima-eiji.github.io/Hosting/man-month/)

Chrome拡張機能にも対応している。
# 手引
[最低賃金APIを使って簡易人月計算機をChrome拡張機能で作ってみた](https://note.com/nomuragoro/n/nf11cbbaf8f96)