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

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

新規登録して質問してみよう
ただいま回答率
85.47%
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

Q&A

解決済

1回答

248閲覧

Google Apps Script で、スクレイピングをしていますが検索件数によって、結果が異なります。

studiosimply

総合スコア4

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

0グッド

2クリップ

投稿2024/03/14 14:42

編集2024/03/15 06:20

実現したいこと

  • コンパニオンの出勤日を取得したい。

前提

Google Apps Script で、スクレイピングをしています。コンパニオン派遣サービスをWeb上からスクレイピングして特定のコンパニオンの出勤日を取得したいのですが、companionArr の先頭2人のみ実行した場合はうまくいくのですが、3人以上指定した場合、現在は以下の通りです。

先頭2人のみ実行した場合は出勤日を取得できている。

体験入店, 💗りお, (25), 大宮人妻花壇, T163-B83(B)-W57-H84
3月14日(木),3月15日(金)
https://www.deli-more.com/lady/146217
体験入店, 💗りお, (25), 大宮人妻城, T163-B83(B)-W57-H84
3月14日(木),3月15日(金)
https://www.deli-more.com/lady/146218

先頭3人を実行した場合は、出勤日が '理沙' に引っ張られている。

体験入店, 💗りお, (25), 大宮人妻花壇, T163-B83(B)-W57-H84
3月19日(火),3月20日(水),3月21日(木)
https://www.deli-more.com/lady/146217
体験入店, 💗りお, (25), 大宮人妻城, T163-B83(B)-W57-H84
3月19日(火),3月20日(水),3月21日(木)
https://www.deli-more.com/lady/146218
新人, 💗理沙, (28), 大宮人妻花壇, T155-B86(E)-W59-H85
3月19日(火),3月20日(水),3月21日(木)
https://www.deli-more.com/lady/143730

全員を実行した場合は、出勤するはずの先頭3人の出勤日が無くなっている。

体験入店, 💗りお, (25), 大宮人妻花壇, T163-B83(B)-W57-H84

https://www.deli-more.com/lady/146217
体験入店, 💗りお, (25), 大宮人妻城, T163-B83(B)-W57-H84

https://www.deli-more.com/lady/146218
新人, 💗理沙, (28), 大宮人妻花壇, T155-B86(E)-W59-H85

https://www.deli-more.com/lady/143730
----, 💙さつき, (34), 川越人妻花壇, T162-B83(B)-W59-H84
https://www.deli-more.com/lady/110691
----, 💙すず, (30), 川越人妻花壇, T160-B83(B)-W57-H84
https://www.deli-more.com/lady/112026
----, 💙あやめ, (27), 大宮人妻花壇, T163-B83(B)-W57-H85
https://www.deli-more.com/lady/130892
----, 💙有希子, (28), 大宮人妻城, T163-B82(B)-W57-H85
https://www.deli-more.com/lady/135221
----, 💙有希子, (28), 大宮人妻花壇, T163-B82(B)-W57-H85
https://www.deli-more.com/lady/135220
----, 💙蘭, (29), 大宮人妻城, T162-B83(B)-W57-H85
https://www.deli-more.com/lady/140489
----, 💙蘭, (29), 大宮人妻花壇, T162-B83(B)-W57-H85
https://www.deli-more.com/lady/140488

該当のソースコード

GAS

1function companionAttendanceNotification() { 2 3 // コンパニオンの配列 4 var companionArr = [ 5 ['https://www.deli-more.com/lady/146217','りお(25)','大宮人妻花壇','T163-B83(B)-W57-H84'], 6 ['https://www.deli-more.com/lady/146218','りお(25)','大宮人妻城','T163-B83(B)-W57-H84'], 7/* ['https://www.deli-more.com/lady/143730','理沙(28)','大宮人妻花壇','T155-B86(E)-W59-H85'], // ダミー 8 ['https://www.deli-more.com/lady/110691','さつき(34)','川越人妻花壇','T162-B83(B)-W59-H84'], 9 ['https://www.deli-more.com/lady/112026','すず(30)','川越人妻花壇','T160-B83(B)-W57-H84'], 10 ['https://www.deli-more.com/lady/130892','あやめ(27)','大宮人妻花壇','T163-B83(B)-W57-H85'], 11 ['https://www.deli-more.com/lady/135221','有希子(28)','大宮人妻城','T163-B82(B)-W57-H85'], 12 ['https://www.deli-more.com/lady/135220','有希子(28)','大宮人妻花壇','T163-B82(B)-W57-H85'], 13 ['https://www.deli-more.com/lady/140489','蘭(29)','大宮人妻城','T162-B83(B)-W57-H85'], 14 ['https://www.deli-more.com/lady/140488','蘭(29)','大宮人妻花壇','T162-B83(B)-W57-H85'],*/ 15 ]; 16 // 各コンパニオンページへアクセスする 17 var companionInfo = []; 18 var options = { method: "get" }; 19 for (var i=0; i < companionArr.length; i++) { 20 var companionUrl = companionArr[i][0]; 21 var name = companionArr[i][1]; 22 Logger.log(name +'…'); 23 24 // コンパニオンページを取得する 25 response = UrlFetchApp.fetch(companionUrl, options); 26 var src = response.getContentText(); 27 28 // ステータス・名前・年齢を取得する 29 if (/<p class="mgnaviProfileName"><span class=".+?">.+?<\/span>.+?<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/.test(src)) { 30 var status = src.match(/<p class="mgnaviProfileName"><span class=".+?">(.+?)<\/span>.+?<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/)[1]; 31 var name = src.match(/<p class="mgnaviProfileName"><span class=".+?">.+?<\/span>(.+?)<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/)[1]; 32 var age = src.match(/<p class="mgnaviProfileName"><span class=".+?">.+?<\/span>.+?<span class="mgnaviProfileAge">\((.+?)\)<\/span><\/p>/)[1]; 33 } else if (/<p class="mgnaviProfileName">.+?<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/) { 34 var status = '----'; 35 var name = src.match(/<p class="mgnaviProfileName">(.+?)<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/)[1]; 36 var age = src.match(/<p class="mgnaviProfileName">.+?<span class="mgnaviProfileAge">\((.+?)\)<\/span><\/p>/)[1]; 37 } else { 38 Logger.log(name +': 記述ちがい?'); 39 return; 40 } 41 // 店名を取得する 42 if (/<div>\s*?.+?<a class="mgnavProfileShoptellink" href=".+?">.+?<\/a>\s*?<\/div>/.test(src)) { 43 var shop = src.match(/<div>\s*?(.+?)<a class="mgnavProfileShoptellink" href=".+?">.+?<\/a>\s*?<\/div>/)[1].trim(); 44 } else { 45 Logger.log(name +': 店名なし'); 46 return; 47 } 48 // 身長・3サイズを取得する 49 if (/<div class="mgnaviProfileSize">.+?<\/div>/.test(src)) { 50 var size = src.match(/<div class="mgnaviProfileSize">(.+?)<\/div>/)[1]; 51 } else { 52 Logger.log(name +': 身長・3サイズなし'); 53 return; 54 } 55 // 出勤の有無を取得する 56 var isWorkDay = false; 57 if (/<ul class="mgnaviProfileSchedule">[\s\S]+?<\/ul>/.test) { 58 var schedule = src.match(/<ul class="mgnaviProfileSchedule">[\s\S]+?<\/ul>/)[0]; 59 if (/~/.test(schedule)) { 60 isWorkDay = true; 61 } 62 } else { 63 Logger.log(name +': 出勤記述不明'); 64 } 65 66 // 出勤日の配列を初期化する 67 var personalSKD = Array(companionArr.length).fill([]); 68 69 // 出勤日を取得する 70 if (isWorkDay == true) { 71 // 1週間を1日ずつ配列に格納する 72 schedScripts = schedule.match(/<li>\s*?<div class="mgnaviProfileScheduleDate">.+?<\/div>\s*?<div class="mgnaviProfileScheduleTime">\s*?<span>.+?<\/span><span>~<\/span><span>.+?<\/span>\s*?<\/div>\s*?<\/li>/g); 73 74 Logger.log('schedScripts.length: '+ schedScripts.length); 75 for (var j=0; j < schedScripts.length; j++) { 76 Logger.log('schedScripts['+ j +']: '+ schedScripts[j]); 77 } 78 79 // 出勤日を配列に格納する 80 for (var j=0; j < schedScripts.length; j++) { // j は、その人の出勤日数を表す 81 var date = schedScripts[j].match(/<li>\s*?<div class="mgnaviProfileScheduleDate">(.+?)<\/div>\s*?<div class="mgnaviProfileScheduleTime">\s*?<span>.+?<\/span><span>~<\/span><span>.+?<\/span>\s*?<\/div>\s*?<\/li>/)[1]; 82 personalSKD[i].push(date); 83 Logger.log('personalSKD['+ i +']['+ j +']: '+ personalSKD[i][j]); 84 } 85 } 86 // コンパニオンの情報を配列に格納する 87 companionInfo.push([status, name, age, shop, size, isWorkDay, companionUrl]); 88 } 89 90 // 出勤者はいるか 91 var subjectPeople = []; 92 for (var i=0; i < companionInfo.length; i++) { 93 name = companionInfo[i][1]; 94 age = companionInfo[i][2]; 95 isWorkDay = companionInfo[i][5]; 96 // タイトルの作成準備をする 97 if (isWorkDay == true) { 98 subjectPeople.push(name + '('+ age +')'); 99 } 100 } 101 // 出勤者がいなければ終了する 102 if (subjectPeople.length == 0) { 103 return; 104 } 105 106 // タイトルを作成する 107 subject = subjectPeople.join('💗,') +'💗出勤予定'; 108 // メッセージを作成する 109 var message = ''; 110 for (var i=0; i < companionInfo.length; i++) { 111 status = companionInfo[i][0]; 112 name = companionInfo[i][1]; 113 age = companionInfo[i][2]; 114 shop = companionInfo[i][3]; 115 size = companionInfo[i][4]; 116 isWorkDay = companionInfo[i][5]; 117 companionUrl = companionInfo[i][6]; 118 message += status +', '+ (isWorkDay ? '💗' : '💙') + name +', ('+ age +'), '+ shop +', '+ size +'\n'; 119 // 出勤日を追加する 120 if (isWorkDay == true) { 121 Logger.log('personalSKD['+ i +']: '+ personalSKD[i]); 122 message += personalSKD[i] +'\n'; 123 } 124 message += companionUrl +'\n'; 125 } 126 message = message.trim(); 127 128 // E-Mail送信する 129 Logger.log(subject); 130 Logger.log(message); 131 var recipient = 'example@gmail.com', cc = '', bcc = ''; 132 var msgBody = message; 133 //sendEmail(recipient, cc, bcc, subject, msgBody); 134}

試したこと

  • 配列の初期化を for 文ではなく、Array 文にした。
  • Logger.log 文を多用し、変数の値を見える化した。

不正と見られる挙動

companionArr を

'りお(25)','大宮人妻城'
'理沙(28)','大宮人妻花壇'
の上記2人の配列で実行した場合、
ソースコードの83行のコンソール表示は、
りお(25)

personalSKD[0][0]: 3月15日(金)

理沙(28)

personalSKD[1][0]: 3月19日(火)
personalSKD[1][1]: 3月20日(水)
personalSKD[1][2]: 3月21日(木)

と正しいです。

しかしソースコード121行でのコンソール表示は、

personalSKD[0]: 3月19日(火),3月20日(水),3月21日(木)
personalSKD[1]: 3月19日(火),3月20日(水),3月21日(木)

と personalSKD[0] の内容が変わっています。

途中で変化する要素がないのに、中身が変わっているので理解できません。

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

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

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

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

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

ikedas

2024/03/15 03:58

Logger.logで出力されたものも示してはどうですか。なお、このコメント欄に載せるのではなく、質問文を編集して載せてください。
studiosimply

2024/03/15 04:02

コメントありがとうございます。 ・先頭2人のみ実行した場合は出勤日を取得できている。 ・先頭3人を実行した場合は、出勤日が '理沙' に引っ張られている。 ・全員を実行した場合は、出勤するはずの先頭3人の出勤日が無くなっている。 上記3例は、Logger.log(message); で表示された内容です。
ikedas

2024/03/15 04:56

message以外にも、処理の途中でLogger.logを使って出力しているものがありますよね。それをみていけば、処理のどこで想定とちがってしまったのかわかるのではないですか。
ikedas

2024/03/15 10:40 編集

> りお(25) > > personalSKD[0][0]: 3月15日(金) > 理沙(28) > > personalSKD[1][0]: 3月19日(火) > personalSKD[1][1]: 3月20日(水) > personalSKD[1][2]: 3月21日(木) この時点でpersonalSKD[0]の中身を表示してみると、もう変わっているのではないですか。 もしもそうなら、67行目の > // 出勤日の配列を初期化する > var personalSKD = Array(companionArr.length).fill([]); ここで配列の配列を作るのに.fill()を使っているのがいけないのかもしれません。普通にループで人数分だけ空のArray()を作ってpushするほうがいいのでは。 私はこれから移動してしまうので、誰かが回答してくれるのをお待ちください。
studiosimply

2024/03/15 11:38

ikedas さん、ありがとうございます。 自分でも配列の初期化方法とタイミングがあやしいと思っていました。 おかげさまで、改修できたようです。
guest

回答1

0

自己解決

"出勤日の配列を初期化する" を for 文の外へ出し、初期化方法も fill から ループ へ変更しました。

GAS

1function companionAttendanceNotification() { 2 3 // コンパニオンの配列 4 var companionArr = [ 5 ['https://www.deli-more.com/lady/146217','りお(25)','大宮人妻花壇','T163-B83(B)-W57-H84'], 6 ['https://www.deli-more.com/lady/146218','りお(25)','大宮人妻城','T163-B83(B)-W57-H84'], 7 ['https://www.deli-more.com/lady/143730','理沙(28)','大宮人妻花壇','T155-B86(E)-W59-H85'], // ダミー 8 /* ['https://www.deli-more.com/lady/110691','さつき(34)','川越人妻花壇','T162-B83(B)-W59-H84'], 9 ['https://www.deli-more.com/lady/112026','すず(30)','川越人妻花壇','T160-B83(B)-W57-H84'], 10 ['https://www.deli-more.com/lady/130892','あやめ(27)','大宮人妻花壇','T163-B83(B)-W57-H85'], 11 ['https://www.deli-more.com/lady/135221','有希子(28)','大宮人妻城','T163-B82(B)-W57-H85'], 12 ['https://www.deli-more.com/lady/135220','有希子(28)','大宮人妻花壇','T163-B82(B)-W57-H85'], 13 ['https://www.deli-more.com/lady/140489','蘭(29)','大宮人妻城','T162-B83(B)-W57-H85'], 14 ['https://www.deli-more.com/lady/140488','蘭(29)','大宮人妻花壇','T162-B83(B)-W57-H85'],*/ 15 ]; 16 17 // 出勤日の配列を初期化する 18 var personalSKD = []; 19 for (var j=0; j < companionArr.length; j++) { 20 personalSKD.push([]); 21 } 22 // その他の初期化 23 var companionInfo = []; 24 var options = { method: "get" }; 25 26 // 各コンパニオンページへアクセスする 27 for (var i=0; i < companionArr.length; i++) { 28 var companionUrl = companionArr[i][0]; 29 var name = companionArr[i][1]; 30 Logger.log(name +'…'); 31 32 // コンパニオンページを取得する 33 response = UrlFetchApp.fetch(companionUrl, options); 34 var src = response.getContentText(); 35 36 // ステータス・名前・年齢を取得する 37 if (/<p class="mgnaviProfileName"><span class=".+?">.+?<\/span>.+?<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/.test(src)) { 38 var status = src.match(/<p class="mgnaviProfileName"><span class=".+?">(.+?)<\/span>.+?<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/)[1]; 39 var name = src.match(/<p class="mgnaviProfileName"><span class=".+?">.+?<\/span>(.+?)<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/)[1]; 40 var age = src.match(/<p class="mgnaviProfileName"><span class=".+?">.+?<\/span>.+?<span class="mgnaviProfileAge">\((.+?)\)<\/span><\/p>/)[1]; 41 } else if (/<p class="mgnaviProfileName">.+?<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/) { 42 var status = '----'; 43 var name = src.match(/<p class="mgnaviProfileName">(.+?)<span class="mgnaviProfileAge">\(.+?\)<\/span><\/p>/)[1]; 44 var age = src.match(/<p class="mgnaviProfileName">.+?<span class="mgnaviProfileAge">\((.+?)\)<\/span><\/p>/)[1]; 45 } else { 46 Logger.log(name +': 記述ちがい?'); 47 return; 48 } 49 // 店名を取得する 50 if (/<div>\s*?.+?<a class="mgnavProfileShoptellink" href=".+?">.+?<\/a>\s*?<\/div>/.test(src)) { 51 var shop = src.match(/<div>\s*?(.+?)<a class="mgnavProfileShoptellink" href=".+?">.+?<\/a>\s*?<\/div>/)[1].trim(); 52 } else { 53 Logger.log(name +': 店名なし'); 54 return; 55 } 56 // 身長・3サイズを取得する 57 if (/<div class="mgnaviProfileSize">.+?<\/div>/.test(src)) { 58 var size = src.match(/<div class="mgnaviProfileSize">(.+?)<\/div>/)[1]; 59 } else { 60 Logger.log(name +': 身長・3サイズなし'); 61 return; 62 } 63 // 出勤の有無を取得する 64 var isWorkDay = false; 65 if (/<ul class="mgnaviProfileSchedule">[\s\S]+?<\/ul>/.test) { 66 var schedule = src.match(/<ul class="mgnaviProfileSchedule">[\s\S]+?<\/ul>/)[0]; 67 if (/~/.test(schedule)) { 68 isWorkDay = true; 69 } 70 } else { 71 Logger.log(name +': 出勤記述不明'); 72 } 73 74 // 出勤日を取得する 75 if (isWorkDay == true) { 76 // 1週間を1日ずつ配列に格納する 77 schedScripts = schedule.match(/<li>\s*?<div class="mgnaviProfileScheduleDate">.+?<\/div>\s*?<div class="mgnaviProfileScheduleTime">\s*?<span>.+?<\/span><span>~<\/span><span>.+?<\/span>\s*?<\/div>\s*?<\/li>/g); 78 79 // 出勤日を配列に格納する 80 for (var j=0; j < schedScripts.length; j++) { // j は、その人の出勤日数を表す 81 var date = schedScripts[j].match(/<li>\s*?<div class="mgnaviProfileScheduleDate">(.+?)<\/div>\s*?<div class="mgnaviProfileScheduleTime">\s*?<span>.+?<\/span><span>~<\/span><span>.+?<\/span>\s*?<\/div>\s*?<\/li>/)[1]; 82 personalSKD[i].push(date); 83 } 84 } 85 // コンパニオンの情報を配列に格納する 86 companionInfo.push([status, name, age, shop, size, isWorkDay, companionUrl]); 87 } 88 89 // 出勤者はいるか 90 var subjectPeople = []; 91 for (var i=0; i < companionInfo.length; i++) { 92 name = companionInfo[i][1]; 93 age = companionInfo[i][2]; 94 isWorkDay = companionInfo[i][5]; 95 // タイトルの作成準備をする 96 if (isWorkDay == true) { 97 subjectPeople.push(name + '('+ age +')'); 98 } 99 } 100 // 出勤者がいなければ終了する 101 if (subjectPeople.length == 0) { 102 return; 103 } 104 105 // タイトルを作成する 106 subject = subjectPeople.join('💗,') +'💗出勤予定'; 107 // メッセージを作成する 108 var message = ''; 109 for (var i=0; i < companionInfo.length; i++) { 110 status = companionInfo[i][0]; 111 name = companionInfo[i][1]; 112 age = companionInfo[i][2]; 113 shop = companionInfo[i][3]; 114 size = companionInfo[i][4]; 115 isWorkDay = companionInfo[i][5]; 116 companionUrl = companionInfo[i][6]; 117 message += status +', '+ (isWorkDay ? '💗' : '💙') + name +', ('+ age +'), '+ shop +', '+ size +'\n'; 118 // 出勤日を追加する 119 if (isWorkDay == true) { 120 message += personalSKD[i] +'\n'; 121 } 122 message += companionUrl +'\n'; 123 } 124 message = message.trim(); 125 126 // E-Mail送信する 127 Logger.log(subject); 128 Logger.log(message); 129 var recipient = 'example@gmail.com', cc = '', bcc = ''; 130 var msgBody = message; 131 //sendEmail(recipient, cc, bcc, subject, msgBody); 132}

投稿2024/03/15 11:49

studiosimply

総合スコア4

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問