見出し画像

副業情報を自動で集めて通知するツールを作ろう_3【GAS】

 前回までで自動実行と2か所からのスクレイピング、メール送信ができています。

しかし一か所にしかメール送信できないのでこれを複数に送信できるようにしたいと思います。

この記事の対象者

この記事ではこんな方のお悩みに答えます。
・できるだけコピペで簡単に何かを作ってみたい
・スクレイピングのサンプルプログラムが欲しい
・GMailを操作するサンプルプログラムが見たい
・GMailで複数個所に自動メールを送るプログラムが欲しい
・Google Apps Script(GAS)初心者の方もOK!

では前回の続きから行きたいと思います。
手順は
1.メール送信相手の一覧シート作成
2.スプレッドシートの共有化
3.プログラムの更新
になります。

メール送信相手の一覧シート作成

 送信相手リストを記入するシートを追加します。

画像1

スプレッドシートの左下側のシート追加ボタンを押してシート追加します。
シート名は「メール送信」にしてください。
シート「メール送信」の中は次のようにお願いします。

画像2

A1に「送信先」B1に「氏名」。
A2、B2から下は送信先メールアドレスと送信相手氏名のリストになります。何人分登録してもらっても大丈夫です。とりあえず、自分のメールアドレスを入れておきましょう。ちなみに送信元は自分のGmailアドレスになります。

スプレッドシートを共有化

スプレッドシートを他人が見れるように閲覧権限共有にしておきましょう。リンクを知っている人なら誰でも閲覧できるけど編集はできない権限のことです。

Gドライブのファイルリストが表示されている状態で、
共有設定にしたいスプレッドシートを右クリック→「共有」を選択します。

共有_1


「リンクを知っている全員に変更」をクリック

共有_2

「リンクを知っている全員」になっていればOKですが、「制限付き」になっていたら▼マークをクリックして「リンクを知っている全員」に変えます。

共有_3

右下の完了ボタンを押します。

共有_4


完成プログラム

最後にプログラムを入れ替えます。下のプログラムリストにコピー&ペーストで入れ替えてください。

const SHEET = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1');
const MAILSHEET = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('メール送信');
const PAGES = 3;

const main = () => {
	//シートを初期化する
	clearsheet();

	let cnt_Lancers=0,cnt_CrowdWorks=0;
	cnt_CrowdWorks = Scraping_CrowdWorks();
	cnt_lancers = Scraping_Lancers();
	SendMail(cnt_lancers,cnt_CrowdWorks);
}

const Scraping_Lancers = () => {
	let cnt=0;
	//ページ単位でスクレイピング
	for(let page=1;page<=PAGES;page++){
		const url = "https://www.lancers.jp/work/search?sort=client&type%5B0%5D=task&open=1&show_description=1&work_rank%5B0%5D=3&work_rank%5B1%5D=2&work_rank%5B2%5D=0&budget_from=&budget_to=&keyword=%E3%82%A2%E3%83%B3%E3%82%B1%E3%83%BC%E3%83%88&not=&page=${page}".replace("${page}", page);
		const html = UrlFetchApp.fetch(url).getContentText('UTF-8');
		Utilities.sleep(300);
		const workArray = Parser
			.data(html)
			.from('<div class="c-media__content__right">')
			.to('</div>')
			.iterate();

		for(let i =0;i<workArray.length;i++){
			const content = workArray[i];

			//案件タイトル
			const p_title = Parser
				.data(content)
				.from('<span class="c-media__title-inner">')
				.to('</span>')
				.build();
			let workTitle=deleteTag(p_title);

			//詳細リンク
			let workDetailurl='';
			let workNumber='';
			const p_detailurl_pos=content.indexOf('<a class="c-media__title" href="');
			if(p_detailurl_pos != -1){
				const p_detailurl_pos2=content.indexOf('">');
				workDetailurl = "https://www.lancers.jp" + content.substr(p_detailurl_pos+32,p_detailurl_pos2-(p_detailurl_pos+32));
				workNumber = content.substr(p_detailurl_pos+45,p_detailurl_pos2-(p_detailurl_pos+45));
			}

			//報酬額
			let workPayment='';
			const p_workPayment_pos=content.indexOf('<span class="c-media__job-price">');
			if(p_workPayment_pos != -1){
				workPayment = content.substr(p_workPayment_pos);
				workPayment = deleteTag(workPayment);
			}

			//シートに書き込む
			if(workNumber != '' && workTitle!='' && workDetailurl!=''){
				const lastrow = SHEET.getLastRow()+1;
				SHEET.getRange(lastrow,2).setValue(workNumber);
				SHEET.getRange(lastrow,3).setValue(workTitle);
				SHEET.getRange(lastrow,4).setValue(workDetailurl);
				SHEET.getRange(lastrow,5).setValue(workPayment);
				Utilities.sleep(50);
				cnt+=1;
			}
		}
		Utilities.sleep(300);
		//次へのリンクがないときはスクレイピングを終了する
		if(html.indexOf('<span class="pager__item pager__item--next">')==-1){
			break;
		}
	}
	return cnt;
}

const Scraping_CrowdWorks = () => {
	let cnt=0;
	//ページ単位でスクレイピング
	for(let page=1;page<=PAGES;page++){
		const url = "https://crowdworks.jp/public/jobs/search?hide_expired=true&keep_search_criteria=true&order=new&page=${page}&payment_type=task&search%5Bkeywords%5D=%E3%82%A2%E3%83%B3%E3%82%B1%E3%83%BC%E3%83%88".replace("${page}", page);
		const html = retryFetch(url);
		Utilities.sleep(300);
		const workArray = Parser
		    .data(html)
			.from('<div class="job_data_row">')
			.to('<span class="absolute_date">')
			.iterate();

		for(let i =0;i<workArray.length ;i++){
			const content = workArray[i];
			//案件タイトル
			const p_title = Parser
				.data(content)
				.from('<h3 class="item_title">')
				.to('</h3>')
				.build();
			let workTitle=deleteTag(p_title);

			//詳細リンク
			let workDetailurl='';
			let workNumber='';
			const p_detailurl_pos=content.indexOf('<a data-item-title-link="" target="_blank" rel="noopener noreferrer" href="');
			if(p_detailurl_pos != -1){
				const temp =content.substr(p_detailurl_pos);
				const p_detailurl_pos2=temp.indexOf('">');
				workDetailurl = "https://crowdworks.jp/" + temp.substr(76,p_detailurl_pos2-76);
				workNumber = temp.substr(88,p_detailurl_pos2-88);
			}

			//報酬額
			let workPayment='';
			const p_workPayment_pos=content.indexOf('<b class="amount">');
			if(p_workPayment_pos != -1){
				workPayment=content.substr(p_workPayment_pos);
				workPayment = deleteTag(workPayment);
			}

			//シートに書き込む
			if(workNumber != '' && workTitle!='' && workDetailurl!=''){
				const lastrow = SHEET.getLastRow()+1;
				SHEET.getRange(lastrow,2).setValue(workNumber);
				SHEET.getRange(lastrow,3).setValue(workTitle);
				SHEET.getRange(lastrow,4).setValue(workDetailurl);
				SHEET.getRange(lastrow,5).setValue(workPayment);
				Utilities.sleep(50);
				cnt+=1;
			}
		}
		Utilities.sleep(300);
		//次へのリンクがないときはスクレイピングを終了する
		if(html.indexOf('gtm_event_label="「次のページへ」ボタン"')==-1){
			break;
		}
	}
	return cnt;
}

const retryFetch = (url) => {
	let lastError = null;
	for(let i = 0; i < 3; i++) {
		try {
			let res = UrlFetchApp.fetch(url)
			if(res.getResponseCode() == 200) {
				return res.getContentText("UTF-8")
			} else {
				lastError = 'HTTPステータスコードが200以外: ' + res.getResponseCode() + ', ' + url;
			}
		} catch(e) {
			lastError = e;
			Logger.log(e);
		}
		Utilities.sleep(3000);
	}
	throw lastError + ', ' + url
}

const clearsheet = () => {
	const lastrow = SHEET.getLastRow();
	if(lastrow-4 > 0) {
		SHEET.getRange(5,1,lastrow-4,10).setValue("");
	}
}

const deleteTag = (str) => {
	let ret=str.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'')
	ret = ret.replace(/[\s\t\n]/g,"");
	return ret;
}

const SendMail = (cnt_lancers,cnt_CrowdWorks) => {
	//案件検索結果
	// 送信時間
	const today = Utilities.formatDate(new Date(), 'JST', 'yyyy年M月d日 H時m分s秒');
	// メールタイトルを変数で定義
	const subject  = "案件検索結果" + today;
	const link_sheet = SpreadsheetApp.getActiveSpreadsheet().getUrl();
	const body = "ランサーズ"+cnt_lancers+"件\n"+"クラウドワークス"+cnt_CrowdWorks+"件\n\n"+link_sheet;
	// スプレッドシートの最終行取得
	const lastRow = MAILSHEET.getLastRow();
	// 繰り返し処理を実施
	for(let i=2; i<=lastRow; i++){
		const address = MAILSHEET.getRange('A' + i).getValue(); //送信先メールアドレス
		const name = MAILSHEET.getRange('B' + i).getValue(); //送信先氏名
		const mailbody = name+"さん"+"\n\nお疲れ様です。\n本日"+today+"時点での検索結果を送ります。\n"+ body + "\n";
		GmailApp.sendEmail(address,subject,mailbody);
	}
	return;
}

プログラムの動作はシート「メール送信」からgetRange(’A'+i).getValue()で送信先メールアドレスを読み込み、順に送信しています。


今回はここまでです。
これで複数個所へ送信できるようになりました。

予告
 次回はプログラムの解説をしたいと思います。


#スクレイピング #プログラミング #GAS #副業 #自動化ツール #無料配布 #コピペで使えるGAS #GoogleAppsScript #クラウドワークス #ランサーズ

お勧めの本 

GASを勉強したい人におススメの本は「Google Apps Scriptのツボとコツがゼッタイにわかる本」がいいです。基本的な仕組みから実践まで分かりやすくて、教科書的な本がニガテな人には絶対におススメの本です。
このシリーズ「~のツボとコツがゼッタイにわかる本」は他の言語も読みやすくていいですよ。私は他に「PHPとMySQLのツボとコツがゼッタイにわかる本」を持ています。





いいなと思ったら応援しよう!