戦うナマケモノのエンジニア作業日誌

記録をつけるのが苦手な初学者です。備忘録です。

<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();

const urls = range.getValues().flat();
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) {
const regex = /<code>(.*?)<\/code>/g;
let match;
let codeContents = ;

// 正規表現で一致するすべての <code> タグを抽出
while *1 !== null) {
codeContents.push(match[1]); // タグの中身を配列に追加
}

return codeContents;
}

function saveToSheet() {
const codeContents = scrapeCodeTags();
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet_ex = ss.getSheetByName('output');

codeContents.forEach*2;
}
},
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]);
return JSON.parse(optionsString[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");
const response = JSON.stringify(sh.getDataRange().getValues());
Logger.log(response);
return response;
}
 

*1:match = regex.exec(html

*2:content, index) => {

sheet_ex.getRange(index + 1, 1).setValue(content); // 各行に書き込み
});
}
 
 
 
 
 
<!DOCTYPE html>
<html style="overflow-y:hidden">

<head>
<base target="_top">
<style>
/* スクロールバーのカスタマイズ */
::-webkit-scrollbar {
background: transparent;
height: 10px;
width: 8px;
}
::-webkit-scrollbar-thumb {
background: #bbb;
border-radius: 8px;
}
thead th {
position: sticky;
top: -1px;
background-color: #ddd;
}
.btn {
position: fixed;
width: 50px;
height: 50px;
color: white;
border-radius: 50px;
display: flex;
justify-content: center;
align-items: center;
opacity: 0.4;
transition: 200ms;
}
.btn:hover {
opacity: 0.9;
}
</style>
</head>

<body>
<div id="app">
<!-- フィルタエリア -->
<div style="width: 100%; height: 10vh; display: flex; padding: 0 20px; overflow: auto;">
<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">
<option value="">選択なし</option>
<option v-for="option in getOptions(key)" :value="option">{{ option }}</option>
</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() {
google.script.run.withSuccessHandler(function(text) {
const response = JSON.parse(text);
app.keys = response[0];
app.records = response.slice(1);
app.initConditions();
}).getSheetData();
},
computed: {
processedKeys: function() {
return this.keys.map(key => key.replace(/:.*$/, ""

uncle-gas.com

qiita.com

tech-fill.net

 

github.com

kashiwanoha-hs.note.jp

web-breeze.net

<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>',
    },
  },
};

qiita.com

 

script.google.com

 

function getQiitaTrend() {
// urlを変数定義
let url = "";
let html = UrlFetchApp.fetch(url).getContentText("UTF-8");
// 希望するタグを抽出
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 で動的スクレイピングできるのは

phantomjscloud.com

 

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 };

let payload = JSON.stringify(option);
payload = encodeURIComponent(payload);
let apiUrl = "https://phantomjscloud.com/api/browser/v2/" + "ak-4mzb6-k101s-8ghcp-s0s7c-zs3qj" + "/?request=" + payload;
let response = UrlFetchApp.fetch(apiUrl);
let json = JSON.parse(response.getContentText());
let source = json["content"]["data"];
return source;
}
参考サイト