Open2
node.js上のpuppeteerライブラリでHTML表の全行の要素をスクレイピングする
ターゲット
スクレイピング対象のターゲットHTMLは以下のようなWebページです。あるプロジェクトのメンバー一覧です。
結論
HTML表の行数を求めるには、行を表すアイテムセレクター #content > center > table > tbody > tr をブラウザの page オブジェクトの $$() メソッドに指定します。そうしてメソッドの戻り値の length プロパティがHTML表の行数となります。
HTML表の行数を求める
let numOfAccount =
( await page.$$( '#content > center > table > tbody > tr' ) ).length
求めた行数を変数 numOfAccount に格納して利用します。
HTMLの構造
一覧表はcenterタグ内にtableタグがあり、さらにtableタグ内にtbodyタグがあり、さらにtbodyタグ内にtrタグがあります。このtrタグ内の情報をスクレイピングします。
スクレイピング対象のターゲットHTML
<center>
<table border="1" bgcolor="white" rules="all" class="TableColor">
<tbody>
<tr bgcolor="#eeeeff" class="TableHeadingColor">
<th>アカウント</th>
<th>氏名</th>
<th>メールアドレス</th>
<th>所属</th>
<th>ロール</th>
<th>アクセス表示</th>
<th>表示順</th>
<th colspan="2">状態変更</th>
</tr>
<tr bgcolor="#ffffff">
<td>
<a name="9999999"><img src="../images/user/invisible.png"></a>
<a href="javascript:void(0)" class="userEditAnchor" data-account="9999999" data-role="2">9999999</a>
</td>
<td>帆花 風彦</td>
<td>
<a href="mailto:hoge_fuuhiko@baaboo.co.jp">hoge_fuuhiko@baaboo.co.jp</a>
</td>
<td>馬場時時開発本部</td>
<td>開発メンバー</td>
<td class="textCenterTd"><input type="checkbox" class="showaccessmode" name="access_9999999" value="1" checked="">
</td>
<td>
<span id="oarea_9999999">
<input type="hidden" name="order_9999999" value="1" id="order_9999999">
<input type="button" class="disporderbtn" name="order_0272054" value="1">
</span>
<input type="hidden" name="dborder_9999999" value="1">
</td>
<td>
<input type="button" id="invalid_9999999" name="label" value="無効">
</td>
<td>削除不可</td>
</tr>
・・・・・・・・・・以下表の行数分<tr>タグを繰り返し・・・・・・・・・・
</tbody>
</table>
</center>
アイテムセレクター
スクレイピング対象のターゲットHTMLの tr タグのアイテムセレクターはタグの入れ子構造から
- #content > center > table > tbody > tr
となります。
そこで、 TARGET オブジェクトのプロパティ
- TARGET.project.userList.itemSelector.trTag に '#content > center > table > tbody > tr' と定義
して利用することにします。
オブジェクト定義
const TARGET = {
projectList: []
,
url: {
home: 'https://target.hoge.jp/'
, project: {
urlMask: '%ProjectNo%'
, alllist: 'https://target.hoge.jp//manage/list.php'
, userList: 'https://target.hoge.jp/users/list.php?project_id=%ProjectNo%'
}
}
,
project : {
allList: {
rowNo: { top: 2 }
, colNo: { id: 2, name: 3, team: 4 }
, itemSelector: {
rowNoMask: '%rowNo%', colNoMask: '%colNo%'
, trTag: '#content > form > center > table > tbody > tr'
, aTag: ' > a'
, template: {
row: '#content > form > center > table > tbody > tr:nth-child(%rowNo%)'
, col: ' > td:nth-child(%colNo%)'
}
}
}
,
userList: {
topRowNo: 2
, colNo: { account: 1, name: 2, mailAddress: 3, belongs: 4 , role: 5 }
, itemSelector : {
rowNoMask: '%rowNo%', colNoMask: '%colNo%', accountMask: '%account%'
, trTag: '#content > center > table > tbody > tr'
, aTag: ' > a.userEditAnchor'
, invalidButton: 'input[id=invalid_%account%]'
, searchButton: 'searchbtn'
, template: {
row: '#content > center > table > tbody > tr:nth-child(%rowNo%)'
, col: ' > td:nth-child(%colNo%)'
}
}
}
}
}
HTML表の行数の使い方例
page.$$( TARGET.project.userList.itemSelector.trTag ) ).length により求められたHTML表の行数を格納した変数numOfAccountを最大値にしてforループで繰り返して、HTML表に格納されたデータをスクレイピングします。ここでは、アカウント、氏名、メールアドレス、ロールをスクレイピングしています。この処理を以下のgetAccountList()関数で実装します。
getAccountList()
const getAccountList = async function( page, accountList ) {
console.log( '..... getAccountList() .....' )
let [ account, name, mailAddress, belongs, role ] = [ '', '', '', '', '' ]
let accountObj = {}
try {
let numOfAccount =
( await page.$$( TARGET.project.userList.itemSelector.trTag ) ).length
console.log( '<------ Project Member List Start ------>' )
for( let rowNo = TARGET.project.userList.topRowNo; rowNo <= numOfAccount; rowNo++ ) {
[ account, name, mailAddress, belongs, role ] = await getAccountItem( page, rowNo )
if( account == '' ) continue
accountObj = { アカウント : account
, 氏名 : name
, メールアドレス: mailAddress
, ロール : role
}
accountList.push( accountObj )
}
} catch( error ) {
console.log( '------> Project Member List End Over <------' )
} finally {
if( Object.values( accountObj ).length == 0 ){
accountObj = { アカウント : ''
, 氏名 : ''
, メールアドレス: ''
, ロール : ''
}
accountList.push( accountObj )
}
return accountList
}
}
もう少し全体像がわかる説明を付けてクローズしたいと思います。クローズ後は記事として公開する予定です。現在、Qiita もまだ利用しているので💦、Qiita に先に公開いたしました。