■
<input v-model="searchQuery" placeholder="allSearch..." class="input is-small">
checkCondition: function(record) {
if (!this.searchQuery) return true; // 検索クエリが空ならすべて表示
return record.some(item =>
item && item.toString().toLowerCase().includes(this.searchQuery.toLowerCase())
);
},
■
function scrapeCodeTags() {
//読み込み
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('input');
const range = sheet.getDataRange();
let allCodeContents = ;
// 各ページから <code> タグを抽出
urls.forEach(url => {
try {
// WebページのHTMLを取得
const response = UrlFetchApp.fetch(url);
const html = response.getContentText();
// <code> タグの中身を抽出
const codeContents = extractCodeTags(html);
allCodeContents.push(...codeContents);
} catch (e) {
Logger.log(`Error fetching URL (${url}): ${e}`);
}
});
Logger.log(allCodeContents); // 抽出結果をログに表示
return allCodeContents;
}
// HTML から <code> タグを抽出する関数
function extractCodeTags(html) {
let match;
let codeContents = ;
// 正規表現で一致するすべての <code> タグを抽出
codeContents.push(match[1]); // タグの中身を配列に追加
}
return codeContents;
}
function saveToSheet() {
const codeContents = scrapeCodeTags();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet_ex = ss.getSheetByName('output');
}
},
methods: {
formatField: function(field, columnIndex) {
if (typeof field === 'string' && columnIndex === 1) {
return field.replace(/\n/g, '<br>');
}
return field;
},
initConditions: function() {
this.conditions = this.keys.map(key => (this.isCheckbox(key) ? [] : ""));
},
checkCondition: function(record) {
for (let i = 0; i < this.keys.length; i++) {
if (this.conditions[i] && !record[i].includes(this.conditions[i])) {
return false;
}
}
return true;
},
isHidden: function(key) {
return /^.*:hidden/.test(key);
},
isSelect: function(key) {
return /^.*:select\[.*\]$/.test(key);
},
isCheckbox: function(key) {
return /^.*:checkbox\[.*\]$/.test(key);
},
getOptions: function(key) {
const param = /:(select|radio|checkbox)\[.*\]$/.exec(key);
const optionsString = /\[.*\]/.exec(param[0]);
},
}
});
</script>
</body>
</html>
const ss = SpreadsheetApp.getActiveSpreadsheet();
function doGet() {
const htmlTemplate = HtmlService.createTemplateFromFile('index');
const title = ss.getName();
htmlTemplate.title = title;
Logger.log(title);
return htmlTemplate.evaluate().setTitle(title);
}
function getSheetData() {
const sh = ss.getSheetByName("error");
Logger.log(response);
return response;
}
sheet_ex.getRange(index + 1, 1).setValue(content); // 各行に書き込み
});
}
<!DOCTYPE html>
<html style="overflow-y:hidden">
<head>
<base target="_top">
<style>
/* スクロールバーのカスタマイズ */
background: transparent;
height: 10px;
width: 8px;
}
background: #bbb;
}
thead th {
position: sticky;
top: -1px;
background-color: #ddd;
}
.btn {
position: fixed;
width: 50px;
height: 50px;
color: white;
justify-content: center;
align-items: center;
opacity: 0.4;
transition: 200ms;
}
.btn:hover {
opacity: 0.9;
}
</style>
</head>
<body>
<div id="app">
<!-- フィルタエリア -->
<h1 class="title is-4">
<span class="material-icons">filter_list</span> Filter
</h1>
<template v-for="(key, index) in keys">
<div v-if="!isHidden(key)">
<label class="label is-small">{{ key.replace(/:.*$/, "") }}</label>
<template v-if="isSelect(key)">
<select v-model="conditions[index]" class="select is-small">
</select>
</template>
<input v-else v-model="conditions[index]" type="text" class="input is-small">
</div>
</template>
</div>
<!-- データエリア -->
<div style="max-width: 100%; height: 100vh; padding: 0 10px; overflow: auto;">
<h1 class="title is-4">
<span class="material-icons">description</span> データ一覧
</h1>
<table class="table is-striped is-hoverable">
<thead>
<tr>
<th v-for="key in processedKeys">{{ key }}</th>
</tr>
</thead>
<tbody>
<tr v-for="record in records" v-if="checkCondition(record)">
<td v-for="(item, index) in record" v-html="formatField(item, index)"></td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
keys: ,
records: ,
conditions: [],
},
mounted: function() {
app.keys = response[0];
app.records = response.slice(1);
app.initConditions();
}).getSheetData();
},
computed: {
processedKeys: function() {
return this.keys.map(key => key.replace(/:.*$/, ""
■
<td v-for="item in record">
<FormattedContent :item="item" />
</td>
export default {
components: {
FormattedContent: {
props: ['item'],
computed: {
formattedContent() {
return `<span class="formatted">${this.autoLink(this.item)}</span>`;
},
},
methods: {
autoLink(item) {
return `<a href="${item.url}">${item.text}</a>`;
},
},
template: '<div v-html="formattedContent"></div>',
},
},
};
■
function getQiitaTrend() {
// urlを変数定義
let url = "";
// 希望するタグを抽出
let titleList = Parser.data(html)
.from('<li id="')
.to('" class="error">')
// タグにおける複数の要素を取得
.iterate();
console.log(`titleList:`+titleList);
let urlList = Parser.data(html)
.from('<a class="button icon-button-transparent" href="')
.to('">')
.iterate();
console.log(`urlList:`+urlList);
let dateList = Parser.data(html)
.from('<li><h4>')
.to('</h4>')
.iterate();
console.log(`dateList:`+dateList);
// 現在のスプレッドシートのIDを取得
//let ss = SpreadsheetApp.getActiveSpreadsheet();
//Logger.log(ss.getUrl());
// 記述するスプレッドシートを指定
let spreadsheet = SpreadsheetApp.openById('')
// スプレッドシートの末尾の行を取得
let lastrow = spreadsheet.getLastRow();
let headerrow = lastrow + 1;
let recordrow = lastrow + 2;
// 新しいスクレイピング結果であることが分かる文言を記載
spreadsheet.getRange("A" + headerrow).setValue('【newInformation】')
for(let i = 0; i <= urlList.length-1; i++){
spreadsheet.getRange("A" + recordrow).setValue(titleList[i]);
spreadsheet.getRange("B" + recordrow).setValue(urlList[i]);
spreadsheet.getRange("C" + recordrow).setValue(dateList[i]);
recordrow++;
}
}
スクレイピング
gas で動的スクレイピングできるのは
function errorsFromAppSheetHelp() {
const url = "";
var html = phantomJSCloudScraping(url);
console.log('html:' + html);
var postsBlocks = Parser.data(html).from('<div class="message-details">').to('</div>').build();
var replyBlocks = Parser.data(html).from('<article class="custom-message-tile').to('</article>').iterate();
console.log('postsBlocks:' + postsBlocks);
console.log('reply:' + replyBlocks);
var newUrls = ;
var reply = ;
for (var i = 0; i < postsBlocks.length; i++) {
var title = Parser.data(postsBlocks[i]).from('<h5 class="message-subject"> <a href="').to('"').build();
var stringPlusPosts = "" + title;
// newUrls[i] = stringPlusPosts;
newUrls.push(stringPlusPosts);
}
for (var y = 0; y < replyBlocks.length; y++) {
var replies = Parser.data(replyBlocks[y]).from('alt="Replies"><b>').to('</b>').build();
//reply[y] = replies;
reply.push(replies || 0);
}
console.log('newUrls:' + newUrls);
console.log('reply:' + reply);
top = 0;
left=0;
let sheet = SpreadsheetApp.openById('1C_Qvnmv5wojsB1klbLMa9WWZuQNgQDwbKB1paFCBeZ8');
// sheet.getRange("A1").setValue("new");
// sheet.getRange("B1").setValue("replies");
let lastRow = sheet.getLastRow();
// sheet.getRange("C1").setValue(Utilites.formatDate(date,'Asia/Tokyo',"MM/DD hh:mm"));
for (var j = 0; j < newUrls.length; j++) {
let rowIndex = lastRow + j +1;
sheet.getRange(rowIndex,1).setValue(newUrls[j]); // A列にURL
sheet.getRange(rowIndex,2).setValue(reply[j]); // B列に返信数
}
}
function phantomJSCloudScraping(URL) {
let key = "ak-4mzb6-k101s-8ghcp-s0s7c-zs3qj";
let option = { url: URL, renderType: "HTML", outputAsJson: true };
payload = encodeURIComponent(payload);
let apiUrl = "https://phantomjscloud.com/api/browser/v2/" + "ak-4mzb6-k101s-8ghcp-s0s7c-zs3qj" + "/?request=" + payload;
let response = UrlFetchApp.fetch(apiUrl);
return source;
}
参考サイト
シフト管理
















