はてなキーワード: parseとは
一度投稿したうえで別タブを開いてプログラム的(fetch)に送信してその別タブが閉じられる仕組み。
// ==UserScript==
// @name PGP未署名検出と別タブ自動編集
// @namespace http://tampermonkey.net/
// @version 1.0
// @description PGP署名がない投稿を自動編集ページへ誘導
// @match https://anond.hatelabo.jp/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM.openInTab
// ==/UserScript==
(function () {
'use strict';
const body = document.getElementById('entry-page');
if (!body) return;
const titleText = document.title;
if (!titleText.includes('dorawii')) return;
const pgpRegex = /BEGIN.*PGP(?: SIGNED MESSAGE| SIGNATURE)?/;
const preElements = document.querySelectorAll('div.body pre');
let hasPgpSignature = false;
for (const pre of preElements) {
if (pgpRegex.test(pre.textContent)) {
hasPgpSignature = true;
break;
}
}
if (hasPgpSignature) return;
const editLink = document.querySelector('a.edit');
const childTab = GM.openInTab(editLink.href, { active: false, insert: true, setParent: true });
})();
// ==UserScript==
// @name 編集ページ処理と自動送信・閉じ
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 編集ページで署名処理と送信、タブ自動閉じ
// @match https://anond.hatelabo.jp/dorawii_31/edit?id=*
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant GM_setClipboard
// @grant GM_notification
// @connect localhost
// ==/UserScript==
(async function () {
'use strict';
const shouldRun = await GM_getValue('open-tab-for-edit', '0');
const textareaId = 'text-body';
const textarea = document.getElementById(textareaId);
if (!textarea) return;
const content = textarea.value;
const pgpSignatureRegex = /-----BEGIN PGP SIGNED MESSAGE-----[\s\S]+?-----BEGIN PGP SIGNATURE-----[\s\S]+?-----END PGP SIGNATURE-----/;
if (pgpSignatureRegex.test(content)) {
console.log('[PGPスクリプト] 署名が検出されたためそのまま送信します');
return;
}
const httpRequest = (url, data) => {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'POST',
url: url,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: `value=${encodeURIComponent(data)}`,
onload: function (response) {
resolve(response.responseText);
},
onerror: function (error) {
reject(error);
}
});
});
};
// textarea の値を取得
// 1. 現在のページのURLからURLオブジェクトを作成
const currentUrl = new URL(window.location.href);
// 2. ベースとなる部分 (例: "https://anond.hatelabo.jp") を取得
const origin = currentUrl.origin;
// 3. 'id' パラメータの値 (例: "20250610184705") を取得
const idValue = currentUrl.searchParams.get('id');
// 4. ベース部分とIDを結合して、目的のURL文字列を生成
// idValueが取得できた場合のみ実行する
let newUrl = null;
if (idValue) {
newUrl = `${origin}/${idValue}`;
}
// 5. 生成されたURLを変数に代入し、コンソールに出力して確認
console.log(newUrl);
const valueToSend = newUrl;
try {
const signatureText = await httpRequest('http://localhost:12345/run-batch', valueToSend);
console.log('バッチ応答:', signatureText);
if (!signatureText.includes('BEGIN PGP SIGNED MESSAGE')) {
alert('PGP署名がクリップボードに見つかりませんでした。');
return;
}
const newText = content.replace(/\s*$/, '') + '\n' + signatureText + '\n';
textarea.value = newText;
console.log('[PGPスクリプト] 署名を貼り付けました。送信を再開します。');
const form = document.forms.edit;
const newForm = form.cloneNode(true);
form.replaceWith(newForm);
newForm.addEventListener('submit', async (e) => {
e.preventDefault(); // HTML標準のsubmitをキャンセル
const bodyText = textarea?.value || '';
// reCAPTCHA トークンの取得
const recaptchaToken = await new Promise((resolve) => {
grecaptcha.enterprise.ready(() => {
grecaptcha.enterprise.execute('hoge', { action: 'EDIT' })
.then(resolve);
});
});
// POSTするデータの構築
const formData = new FormData(newForm);
formData.set('body', bodyText);
formData.set('recaptcha_token', recaptchaToken);
formData.set('edit', '1');
try {
const response = await fetch(newForm.action, {
method: 'POST',
body: formData,
credentials: 'same-origin'
});
if (response.ok) {
console.log('送信成功');
window.close();
} else {
console.error('送信失敗', response.status);
}
} catch (err) {
console.error('送信中にエラーが発生', err);
}
});
// プログラム的に送信トリガー
newForm.dispatchEvent(new Event('submit', { bubbles: true }));
} catch (e) {
console.error('バッチ呼び出し失敗:', e);
}
})();
const http = require('http'); const { exec } = require('child_process'); const querystring = require('querystring'); const server = http.createServer((req, res) => { if (req.method === 'GET' && req.url === '/ping') { res.writeHead(200); res.end('pong'); } else if (req.method === 'POST' && req.url === '/run-batch') { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { const parsed = querystring.parse(body); const value = parsed.value || 'default'; // 値を引数としてバッチに渡す exec(`C:\\Users\\hoge\\Desktop\\makesign.bat "${value}"`, { encoding: 'utf8' }, (err, stdout, stderr) => { if (err) { res.writeHead(500); res.end('Error executing batch: ' + stderr); } else { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end(stdout.trim()); } }); }); } else { res.writeHead(404); res.end('Not found'); } }); server.listen(12345, () => { console.log('Batch server running at http://localhost:12345/'); });
@echo off setlocal enabledelayedexpansion :: 署名するファイル名 set "infile=%~1" set outfile=%TEMP%\pgp_output.asc :: 以前の出力があれば削除 if exist "%outfile%" del "%outfile%" :signloop :: AutoHotkeyでパスフレーズ入力(gpgがパスワード要求するダイアログが出た場合に備える) start "" /b "C:\Users\hoge\Documents\AutoHotkey\autopass.ahk" :: PGPクリア署名を作成 echo %infile% | gpg --yes --clearsign --output "%outfile%" :: 署名が成功していればループを抜ける if exist "%outfile%" ( goto postprocess ) else ( timeout /t 1 > nul goto signloop ) :postprocess powershell -nologo -command ^ "$header = '>|'; $footer = '|<'; $body = Get-Content '%outfile%' -Raw; Write-Output ($header + \"`r`n\" + $body + $footer)" powershell -nologo -command ^ "$header = '>|'; $footer = '|<'; $body = Get-Content 'signed.asc' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)" endlocal exit /b
#Persistent #SingleInstance ignore SetTitleMatchMode, 2 WinWaitActive, pinentry SendInput password Sleep 100 SendInput {Enter} ExitApp
動けばいいという考えで作っているので余分なコードも含んでいるかもしれない。
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 https://anond.hatelabo.jp/20250613185036 -----BEGIN PGP SIGNATURE----- iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaEv1FQAKCRBwMdsubs4+ SHHkAQDUOLgBcdji2T6MJ7h/vlMdFfGlWAzNdXijjE1gIuEPywEAiMNMZqhrMmtl c7UqRuggNJ/UTa5xTIcKp622+7jJQQg= =Lgkl -----END PGP SIGNATURE-----
ようやく(ほぼ)すべてが自動化された。
あとはローカルサーバーの起動をスタートアップに設定する(方法をAIに聞いて指示に従う)だけの消化試合。
署名時要求してくるパスワードを自動入力するahkファイルはドキュメントのAutoHotkey配下に置いた。
バッチファイル(make.sign.bat)はデスクトップに置いた。
#Persistent #SingleInstance ignore SetTitleMatchMode, 2 WinWaitActive, pinentry SendInput お前のパスワード Sleep 100 SendInput {Enter} ExitApp
// run-batch-server.js const http = require('http'); const { exec } = require('child_process'); const server = http.createServer((req, res) => { if (req.url === '/ping') { res.writeHead(200); res.end('pong'); } else if (req.url === '/run-batch') { exec('C:\\Users\\you\\Desktop\\makesign.bat', (err) => { res.writeHead(200); res.end(err ? 'Error' : 'OK'); }) ; } else { res.writeHead(404); res.end('Not found'); } }); server.listen(12345, () => { console.log('Batch server running at http://localhost:12345/'); });
@echo off setlocal enabledelayedexpansion :: ミリ秒単位のUTC時刻を取得 for /f %%a in ('powershell -nologo -command "[int64]::Parse((Get-Date).ToUniversalTime().ToString('yyyyMMddHHmmssfff'))"') do set timestamp=%%a :: 署名するファイル名 set infile=%TEMP%\pgp_input.txt set outfile=%TEMP%\pgp_output.asc :: 以前の出力があれば削除 if exist "%outfile%" del "%outfile%" :: タイムスタンプを原文として保存 echo %timestamp% > "%infile%" :signloop :: AutoHotkeyでパスフレーズ入力(gpgがパスワード要求するダイアログが出た場合に備える) start "" /b "C:\Users\infini\Documents\AutoHotkey\autopass.ahk" :: PGPクリア署名を作成 gpg --yes --clearsign --output "%outfile%" "%infile%" :: 署名が成功していればループを抜ける if exist "%outfile%" ( echo [INFO] 署名成功 goto postprocess ) else ( echo [WARN] 署名失敗、再試行します… timeout /t 1 > nul goto signloop ) :postprocess :: PowerShellで余計な改行なしに |< をつけてクリップボードにコピー powershell -nologo -command ^ "$header = '>|'; $footer = '|<'; $body = Get-Content '%outfile%' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)" echo Done. signed.asc created and clipboard updated (no extra blank line). endlocal exit /b
// ==UserScript== // @name PGP署名自動付加スクリプト(GM_xmlhttpRequest版) // @namespace http://tampermonkey.net/ // @version 1.0 // @description 投稿前にPGP署名を付けてから送信(fetch未使用) // @match https://anond.hatelabo.jp/dorawii_31/edit* // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @grant GM_notification // / @connect localhost // ==/UserScript== (function () { 'use strict'; const submitId = 'submit-button'; const textareaId = 'text-body'; const localServer = 'http://localhost:12345/run-batch'; const pgpSignatureRegex = /-----BEGIN PGP SIGNED MESSAGE-----[\s\S]+?-----BEGIN PGP SIGNATURE-----[\s\S]+?-----END PGP SIGNATURE-----/; const httpRequest = (url) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, onload: function (response) { resolve(response.responseText); }, onerror: function (error) { reject(error); } }); }); }; const interceptClick = () => { const btn = document.getElementById(submitId); if (!btn || btn.dataset.pgpIntercepted === 'true') return; btn.dataset.pgpIntercepted = 'true'; btn.addEventListener('click', async function (e) { const textarea = document.getElementById(textareaId); if (!textarea) return; const content = textarea.value; if (pgpSignatureRegex.test(content)) { console.log('[PGPスクリプト] 署名が検出されたためそのまま送信します'); return; } e.preventDefault(); e.stopImmediatePropagation(); console.log('[PGPスクリプト] 署名が見つからないため処理を停止し、署名を取得します'); try { await httpRequest(localServer); // バッチ実行 const signatureText = await navigator.clipboard.readText(); if (!signatureText.includes('BEGIN PGP SIGNED MESSAGE')) { alert('PGP署名がクリップボードに見つかりませんでした。'); return; } const newText = content.replace(/\s*$/, '') + '\n' + signatureText + '\n'; textarea.value = newText; console.log('[PGPスクリプト] 署名を貼り付けました。送信を再開します。'); btn.click(); // イベント再発火 } catch (err) { alert('PGP署名の取得または貼り付けに失敗しました。\n' + err); } }, true); }; window.addEventListener('load', () => { setTimeout(interceptClick, 1000); }); })();
プロミスメソッドとか全然まだ理解してなくてそのなかに関数代入したその関数にオブジェクトのプロパティにresponseを?いやまあそのあたりのコードが示すデータの流れが全然理解できないような人間でもここまでできちゃった。
AIすごいなと思うよ。そして思うのは今後重要になってくるのは文法とか自体に詳しいことじゃなくて、そのプログラムの処理内容を指示できるシステムエンジニア的な言語化能力のほうじゃないかなと思った。
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 20250609111559680 -----BEGIN PGP SIGNATURE----- iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaEbCbwAKCRBwMdsubs4+ SLueAPwOv7PBk4voAe5qlcCEvs/PJhmKc5QAb/1R43JMQFuDZgD/UTPEKsL/PhK9 jFGv2HDXK1dVjLNwvosgX9uYJh5xxwY= =qiOE -----END PGP SIGNATURE-----
ChatGPTにバッチファイルを作ってもらったのでこれからは署名が捗るぞ。これだけ手軽化できたらレスバに入っても署名つけるのも億劫にならずできそうだ。
なにせ文章を書き折ったらあとはバッチダブルクリックしてCtr+Vするだけだ。
名乗る人が増えることを期待して作らせたものを公開しておく。
@echo off setlocal :: ミリ秒単位のUTC時刻を取得 for /f %%A in ('powershell -nologo -command "[int64]::Parse((Get-Date).ToUniversalTime().ToString('yyyyMMddHHmmssfff'))"') do set timestamp=%%A :: PGPクリア署名を作成 echo %timestamp% | gpg --yes --clearsign > signed.asc :: PowerShellで余計な改行なしに |< をつけてクリップボードにコピー powershell -nologo -command ^ "$header = '>|'; $footer = '|<'; $body = Get-Content 'signed.asc' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)" echo Done. signed.asc created and clipboard updated (no extra blank line).
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 20250608045542542 -----BEGIN PGP SIGNATURE----- iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaEUXzgAKCRBwMdsubs4+ SCvuAQDjRjPVCO1F9DgmAKoqKYG7qB9r4e7y2Ky+/umT/OhnygEA8h2NL8zIczSp hcOk/MaDWJJ2Y7WDA3d6gxFakW8TKAw= =HE4/ -----END PGP SIGNATURE-----
令和7年4月1日以降、官報の帰化情報が90日経過で閲覧不可になった。
「プライバシーに配慮」とのことだが、最近の不自然な戸籍不要発言などと合わせて考えると嫌な予感しかしない。
そこでとりあえず官報を保存できるプログラムを作った。自分でダウンロードして保存すること自体は全く問題ない行為。
官報は平日の8:30に公開されるので、cronで8:31とかに実行すると良いのでは。
# 官報のPDFデータを入手して保存する import requests import os import time from bs4 import BeautifulSoup from urllib.parse import urljoin # 対象URL index_url = "https://www.kanpo.go.jp/index.html" base_url = 'https://www.kanpo.go.jp/' # ダウンロード先フォルダ download_dir = 'pdfs' os.makedirs(download_dir, exist_ok=True) # ページ取得 response = requests.get(index_url) response.encoding = 'utf-8' text = response.text # HTMLを解析 soup = BeautifulSoup(text, "html.parser") results = [] # 「本日の官報」を対象にPDFの情報を取得する today_box = soup.find('div', class_='todayBox') if today_box: dl = today_box.find('dl') dt = dl.find('dt') if dt: # 日付の抽出 date_text = dt.get_text(strip=True).split('92;n')[0].replace(" ","").replace("全体目次はこちら","").replace("※インターネット版官報","").strip() dd = dl.find('dd') if dd: for li in dd.find_all('li', class_='articleBox'): title_tag = li.find('a', class_='articleTop') pdf_link = li.find('a', class_='pdfDlb') if title_tag and pdf_link: title = title_tag.decode_contents().replace("<br/>", "").strip() url = pdf_link['href'] results.append({ '日付': date_text, 'title': title, 'url': url }) # 結果の表示 for r in results: date = r['日付'] title = r['title'] url = r['url'] # pdfファイルのURLを作成 url_parts = url.rsplit("/", 1) url_base = url_parts[0] filename = url_parts[1].replace("f.html", ".pdf") converted_url = f"{url_base}/pdf/{filename}" # pdfのURLとファイル名を作成 full_url = urljoin(base_url, converted_url) base_filename = date + "_" + title + "_" + filename.replace("f.html", ".pdf") # ダウンロードして保存 print(f'Downloading {full_url} ...') try: response = requests.get(full_url) response.raise_for_status() with open(os.path.join(download_dir, base_filename), 'wb') as f: f.write(response.content) print(f'Saved: {base_filename}') time.sleep(10) except Exception as e: print(f'Failed to download {full_url}: {e}')
はてなブックマークのAPIを利用して 非公開 でブックマークする方法をご説明します。
2. OAuthのトークンを発行する。(個人用のスクリプトなら「パーソナルアクセストークン」推奨)
ブックマークを 非公開 にするには、APIのリクエストボディに private フィールドを 1 に設定します。
エンドポイント:
POST https://bookmark.hatenaapis.com/rest/1/my/bookmark
リクエストボディ(JSON): { "url": "https://anond.hatelabo.jp/xxxxxxx", "comment": "自動ブックマーク", "private": 1 }
以下のコードを実行すれば、10分以内の匿名ダイアリー記事をランダムに 非公開 でブックマークできます。
import feedparser import requests import random import time from datetime import datetime, timezone, timedelta # はてなAPIの認証情報 HATENA_API_KEY = "あなたのAPIキー" HATENA_USERNAME = "あなたのはてなID" # はてな匿名ダイアリーのRSS URL RSS_URL = "https://anond.hatelabo.jp/rss" # 10分以内の投稿を取得 def get_recent_entries(): feed = feedparser.parse(RSS_URL) recent_entries = [] now = datetime.now(timezone.utc) for entry in feed.entries: published_time = datetime(*entry.published_parsed[:6], tzinfo=timezone.utc) if (now - published_time) < timedelta(minutes=10): recent_entries.append(entry.link) return recent_entries # はてなブックマークに非公開で追加 def bookmark_entry(entry_url): url = "https://bookmark.hatenaapis.com/rest/1/my/bookmark" headers = { "Authorization": f"Bearer {HATENA_API_KEY}", "Content-Type": "application/json" } payload = { "url": entry_url, "comment": "自動ブックマーク", "private": 1 # 非公開設定 } response = requests.post(url, json=payload, headers=headers) return response.status_code # メイン処理 while True: entries = get_recent_entries() if entries: entry = random.choice(entries) status = bookmark_entry(entry) print(f"非公開ブックマーク: {entry}, ステータス: {status}") time.sleep(600) # 10分ごとに実行
• 実行環境: Python 3.x が必要。requests と feedparser をインストール (pip install requests feedparser)
• 実行間隔: time.sleep(600) で10分ごとに実行
• API制限: はてなAPIにはリクエスト制限があるため、短時間で大量に実行しないように注意
• OAuth認証: APIキーだけでなく、OAuthトークンを使うほうがより安全
このスクリプトを実行すれば、最新の匿名ダイアリー投稿を10分以内のものからランダムに選び、非公開でブックマークする ことができます。
OSのコマンドプロンプトはどうしてスペースを区切り文字としたのだろうか? もしタブを区切り文字として採用していれば、混乱はもっと少なかったと思うのだけど。
と、いう素朴な疑問からスタートし、タブが唯一の由緒正しい区切り文字として採用されていた世界線を考察しました。
https://grok.com/share/bGVnYWN5_aa43b346-0185-4f3e-ac4f-7712a59545c8
この世界線で、TABのみが由緒正しく使える使える区切り文字です。
tsvデータが主流となりcsvデータのparseで悩む人にいなくなる。
スペースとタブを区別するために[TAB]のようなフォントも作られるだろう。
さらに、プログラムのインデントもスペースではなく、すべてタブで行われるようになり、タブ幅は2なのか4なのか8なのか論争も起きなかっただろう。個人のエディタの設定で変えればいいだけですしね。
もちろん、ASCII規格のUS(Unit Separator, ASCII 31):ユニット(フィールド)の区切り、に相当するキーがキーボードに搭載されていたとしたら、それを使うのが最良だったと思う。
しかしながら、多くのキーボードにはスペースキーかタブキーぐらいしかなく、これを使うことはできなかったでしょうね。
まとめ
コマンドの区切り文字にスペースを使ってしまった、インデントにスペースを使ってしまった。
この辺りの失敗が多くの人を悩ませていると思う。
import requests import json from urllib.parse import quote def fetch_bookmarks(url): try: # URLをエスケープ escaped_url = quote(url, safe="") api_url = f"https://b.hatena.ne.jp/entry/json/?url={escaped_url}" response = requests.get(api_url) response.raise_for_status() try: return response.json() except json.decoder.JSONDecodeError as e: print(f"Error decoding JSON from {api_url}: {e}") print("Response content:", response.text) return [] except requests.exceptions.RequestException as e: print(f"Error fetching bookmarks from {api_url}: {e}") return [] def find_common_bookmarks(bookmarks1, bookmarks2, url1, url2): common_users = set(bm["user"] for bm in bookmarks1 if bm["comment"]) & set(bm["user"] for bm in bookmarks2 if bm["comment"]) common_bookmarks = [] for user in common_users: comments = [] for bm in bookmarks1: if bm["user"] == user and bm["comment"]: comments.append({"url": url1, "comment": bm["comment"], "timestamp": bm["timestamp"]}) break for bm in bookmarks2: if bm["user"] == user and bm["comment"]: comments.append({"url": url2, "comment": bm["comment"], "timestamp": bm["timestamp"]}) break if len(comments) == 2: common_bookmarks.append({"user": user, "comments": comments}) return common_bookmarks if __name__ == "__main__": url1 = "https://news.yahoo.co.jp/articles/f9966c4ccc374fc88babbb50175a9ea844c99638" url2 = "https://www.asahi.com/articles/ASN6K7F64N6KUJHB00L.html" data1 = fetch_bookmarks(url1) data2 = fetch_bookmarks(url2) common_bookmarks = find_common_bookmarks(data1["bookmarks"], data2["bookmarks"], url1, url2) print(json.dumps(common_bookmarks, indent=2, ensure_ascii=False))
url1, url2のところを対象としたいものに変えれば使えるよ
バグあったら直して使ってね
※架空言語、という事にしておいてほしいんだが
List<Record> rows = DBから持ってくる();
// 合計金額を求める
String total = "0";
for (Record r : rows) {
BigDecimal temp = BigDecimal.parse( カンマを削除する関数(total) );
BigDecimal temp2 = temp + r.金額;
total = カンマ区切りの文字列にする関数(temp2);
}
// やったー合計金額を計算してカンマ区切りの文字列にできたよー
return total;
とか
// async await は非同期処理を同期してくれる魔法の言葉だって!よく知らんけど
await axios.get('/foo')
.then(function(result) {
// やったー結果が得られたよー
});
とか
正直なところ、こういうコードのお守りするの、そろそろキツい。。。
たとえは、0 / float.Parse("0") とかすると、Infinity になるんじゃね?まずいと思うが。
この時期になるとずっと気象庁の気温ランキングを眺めて「今日も暑いなー」って見てるんだが
https://www.data.jma.go.jp/obd/stats/data/mdrr/rank_daily/data00.html
このページの「観測値」が昔からずっとバグっててめっちゃ気になってる
「35.9 ]」っていう感じで、「]」が入ってる
もうバグの原因はほぼほぼ目に見えてて、ここはJSONで「 [ 34.0, 34.5, 35.0, 35.9 ]」っていう値が入ってて
それをJSON.parseするんじゃなくてsplit()とか使ってしかもlength-1とかで最新値を取ってる
なので後ろの括弧がそのまま入ってしまってる
こんなのめちゃくちゃわかりやすいバグだし、多分気象庁側も把握してるんだろうけど
多分発注しないとダメとかテストが必要だとかでずっと放置されてる
不正解でscore += 0; と書いているのは静的解析がelse節を省略すると指摘してくるから。
何もしていない、はそのとおり。
不等号がいい具合に化けてるのでそのままにしておく。
import java.util.*; public class Main { public static void main(String[] args) { // 入力のparse (中略:int numに問題の数、String list[i][] に問題のリストを格納している) // 採点 int point = 0; for (int i = 0; i < num; i++) { String question = list[i][0]; String answer = list[i][1]; if (question.equals(answer)) { // 完全一致 point += 2; } else { if (question.length() == answer.length()) { // 文字数は等しい(部分点の可能性がある) point += scoring(question, answer); } else { // 不正解 point += 0; } } } System.out.println(point); } // 長さが同じ文字列を採点する // 長さが違う文字列を受け渡したときは正しく動作しない private static int scoring(String question, String answer) { int length = question.length(); int score = 2; for (int i = 0; i < length; i++) { (中略:文字が違うたびにscoreを-1して、socreが0以下になったらそのままreturn) } return score; } }
C#の入門書を読んでいて、第4章くらいに4ページくらい割いて書いてあるキャスト
( )で変換するのと int.Parse() で変換するのとどう違うのか(どちらか片方でいいのではないか)をネットで調べ、
なんか as とか int.TryParse() とか出てきてあーんー参照型と値型がーになってダウンキャストとポリフォーニズムでお昼ごはんが夜ご飯になったところで切り上げた
型の違う数値同士のときが ( ) で、文字列から数値に変換したいときが int.Parse() とかだな
入門書に書いてあるそのまんまだな
もういいや次ページ行こう全然進まん
では横ですが、ワテクシの特技のRを少々……
eval(parse(text=paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(paste0(
いま、この「はてな匿名ダイアリーの自分の全記事を一括削除するスクリプト」は、動かない?
・POSTに与える最後の csrf を取ってくる parse が間違って、 rkm= が空になっている。正規表現で parse していたのを直して、 o+XXXXXXXXXXXXXXX/g を拾ってくるようにした。
・POSTに与える delete=が元々の %8d%ed%8f%9c%82%b7%82%e9 でも削除できないし、「 %e5%89%8a%e9%99%a4%e3%81%99%e3%82%8b 」でも削除できなかったし、これってマジックナンバーなん?
1. こんな感じで使います。
$ python parser.py sample.py
import parser code = ''' a = 1 + 1 print(a) ''' graph = parser.create_graph(code) graph.render("sample")
import ast import sys import graphviz def create_graph(lines): graph = graphviz.Graph(format='png') root = ast.parse(lines) node_list = [root] _setup(graph, node_list) return graph def _setup(graph, node_list): # node node = node_list[-1] node_identity = str(len(node_list)) node_name = type(node).__name__ graph.node(node_identity, node_name) # children for child in ast.iter_child_nodes(node): node_list.append(child) child_identity = str(len(node_list)) graph.edge(node_identity, child_identity) _setup(graph, node_list) if __name__ == '__main__': file_name = sys.argv[1] with open(file_name) as file: lines = file.read() graph = create_graph(lines) graph.render(file_name)
Facebook は VR の雄 Oculus も買っています。今は VR といえば Oculus ですが、VR にはどんどん大きな会社が参入してきています。近いうちに Oculus の影響力は下がっていくことでしょう。それを見た Facebook の経営層は、Parse と同じ判断をするはずです。その時、Oculus 用のソフトを開発していた人たちは、今の Parse で開発していた人たちと同じ辛さを経験するでしょう。これを予見できる開発者たちは、早期に Oculus から撤退するはずです。そうすると VR に対する Oculus の影響力は加速度的に小さくなります。
つまり、何が言いたいかというと、今回の判断は開発者の Facebook 離れを引き起こす判断であり、Facebook に買われたサービスは衰退することを宿命づけてしまった大きな事件の一つだったということです。
Excelを持っているならはてブのJSONデータをそのまま取り込めるそうだからそのデータを使ってブクマが付いた時間のグラフが描けそう。
例えばこんな感じでJSONデータが取れる。 http://b.hatena.ne.jp/entry/jsonlite/https://anond.hatelabo.jp/20180315232737
Excel持ってないならスクリプトでCSVにしてしまえばいい。
rubyスクリプトだとこんな感じ。(Mechanize無し版に差し替え。なぜMechanizeを使っていたかと言うとはてブがUser-Agentが空だと値を返してくれないから。ちょっと長くなるが自前でUAを渡すようにした。)
#!/usr/bin/ruby
site = ARGV[0]
json_uri = URI.parse("http://b.hatena.ne.jp/entry/jsonlite/%s" % [site])
response = Net::HTTP.start(json_uri.host, json_uri.port) do |http|
http.get(json_uri.path, "User-Agent" => "Mozilla/5.0")
end
json_data = JSON.parse(response.body)
json_data['bookmarks'].each do |bookmark|
puts [bookmark['user'], bookmark['timestamp'], bookmark['comment'], bookmark['tags'].to_s].to_csv
end
引数に取得したいページのURLを入れる。hatebuapi-csv.rbという名前で保存したとしたらこんな感じで実行。
% hatebuapi-csv.rb https://anond.hatelabo.jp/20180315######## > 結果.csv
結構簡単にできた。
ここから、ページ切り替えてURLを収集する処理も追加すれば、
クローロング部分は完成。
require 'nokogiri'
url = 'http://ja.aliexpress.com/category/200003482/dresses.html?spm=2114.52010108.6.7.gT0qlW&addpid=32546825642&isOnSale=yes%22'
charset = nil
end
doc = Nokogiri::HTML.parse(html, nil, charset)
num=0
doc.css('a[class = "product "]').each do |product|
p product.attribute("href").text
p num = num+1
end
skrsvideo
今回プログラミング言語はRubyを選択したため、基本的にはVPS・クラウド的なサーバーでLinuxが動作する環境を探しました。
エロサイトを運営するにあたって問題になるのがサーバー選びです。
基本的に日本のレンタルサーバーではアダルトサイトの運用を禁止しています。
普段使っているさくらのVPSが利用できず、AWSもなんだかグレーな感じ(東京リージョン以外なら・・・?)
そんなわけで探し、GMOグループのWebkeepersを使いました。
使っても良いよ〜というお墨付き、
そして価格も手頃だったためここに決定。
| DB | MariaDB |
|---|---|
| Webサーバ | Nginx |
| フレームワーク | Ruby on Rails |
MariaDBを選んだ理由はなんとなく、MySQLとの違いはほぼありません。利用するGEMもmysql2でいけます。
ちなみにJavaScriptは使わずすべてCSSで作る方針にしました。
スマホ・PC両対応のためにMedia Queryでレスポンシブにしています。
↓
↓
↓
という流れです
gem 'nokogiri'
フロントはhamlとsassで、難しいことはしていないのでcompassはいれませんでした。
あとはデバッグ用にrails_config、pry系が入っています。
skrsvideoでは動画のURLを取得するためにクローラーもどきでスケジューリングして収集しています。
コマンドはこんな感じ
Rakeタスクはnokogiriでxvideosへのリンクを集めています。
doc = Nokogiri::XML( open(URI.parse(url)).read )
urls = []
urls.push link[:href] if link[:href] =~ /xvideos.com\/video(\d+)/
end
Nokogiriのスクレイピングでaタグのリンクを取得し、URLがxvideosのものかチェックして保存って感じです。
動画を探し終えるとaタグからランダムでピックアップし次のページに進んでいきます・
動画が見つかったページはドメインをDBに記録して、しばらくしたら再びクローリングをするようにし、収集の効率化。
30分以上の表示はちょっと頑張ったところ
これはタイトルの文字列から部分一致で引っかかったものを表示しています。
AV女優の名前を表示するためにWikipediaからとってきたら、ちょっと膨大な数になってしまったため断念。
DMMのランキングに載っていた方だけをとりあえず入れています。
C# で書くと
var ep = new System.Net.IPEndPoint(System.Net.IPAddress.Parse("255.255.255.255"), 2048);
var soc = new System.Net.Sockets.UdpClient(ep);
とすれば
型 'System.Net.Sockets.SocketException' のハンドルされていない例外が System.dll で発生しました
と例外が発生する。
ここは
var u = new System.Net.Sockets.UdpClient();
と空のUDPソケットを作成しておいて、Send メソッドでアドレスを指定する方法をとる。
ちなみに u.EnableBroadcast には false を代入しても、私の環境ではなぜだかうまく送信された。