Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response. Flask & Angular
angularからflaskにAPIアクセスすると上記のようなヘッダー
Failed to load http://localhost:5000/hoge/: Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
のようなエラーが発生しました。
理由としてはflask側で OPTIONSのレスポンス時に
Access-Control-Allow-Headers
を返却していないためでした。
flask-cors(1.8)というライブラリ使っていたんですが
API Docs — Flask-Cors 3.0.3 documentation
を見ると、allow_headersとか、expose_headersとか記載があったんですが、バージョン違うようで(githubのタグ見ると1.8しかない、かつ、pipで入れると1.8なんですけどね、、、) どうもこれらオプションが使えないと。
なんで、
のソースをおっておくと、オプションが headersというのがありました。
これを、flask生成時に以下のように指定してあげると
CORS(app, headers='Content-Type')
無事、
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
のエラーが発生しなくなりました。
これはハマりぽいんだな。
macのdocker上でre:dash動かす
結構いろんな日本語のドキュメント出回っていてうまくre:dash立ち上げる事ができなかったのでメモ。
Setting up a Redash instance · Redash Help Center
こちらのドキュメントをみればOK。
以下手順
git clone https://github.com/getredash/redash.git cd redash vi docker-compose.production.yml ----- いい感じに直す。自分は REDASH_DATE_FORMAT: "YYYY/MM/DD" <--- 日付フォーマット指定 REDASH_ADDITIONAL_QUERY_RUNNERS: "redash.query_runner.python" <--- pythonをデータソースに加えれる を付け加えただけ ----- docker-compose -f docker-compose.production.yml run --rm server create_db docker-compose -f docker-compose.production.yml up
このあと、
にアクセスすると
のような画面が表示される。(本当は、メアドなど最初は登録画面が表示される、、、)
ログインして以下画面が表示されればOK
PGに簡単なゲームのやり方を学習させる vol1 - まずはQ学習を理解する
Vagrant で 起動時にパスワードを聞かれる場合の対処方法 in Mac
自分のMacの環境でVagrantで起動時に以下のようにパスワードが聞かれてしまいいちいち起動の度にパスワードを入力する必要がありました。
==> default: Preparing to edit /etc/exports. Administrator privileges will be required... Password:
以下ページで似たような質問していた人いたのでページを見てみると
macの場合は以下対応すればよいとのこと。
As of version 1.7.3, the sudoers file in OS X should have these entries: Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports Cmnd_Alias VAGRANT_NFSD = /sbin/nfsd restart Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /usr/bin/sed -E -e /*/ d -ibak /etc/exports %admin ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_EXPORTS_REMOVE
ようはmac上で visudo を行いファイル最後に追記(As of ... は除く)。 これでめでたくパスワードを聞かれなくなりました。
Google Spreadsheet から DBにデータ保存して参照する
Google Spreadsheet は シートに値を埋めていけば簡易DBになるんですが、多少データ量が増えてくると200万セル制限にひっかかったりして面倒なことになります。
このためDBにデータを保存しようとして、いろいろ探してみました。
Google Cloud SQL
Google Cloud SQL を使えばGoogle Spreadsheetから簡単にMySQLにアクセスできるんですが、お金がかかるということで却下
JDBC | Apps Script | Google Developers
Fusion Tables
Fusion Tables というDB(平たくいうとGoogleが提供している地図情報と連携できるデータベースのようなものだそうです)は無料で使えそうとのことで以下ページを参考に試してみました。
Googleドライブでデータベースが使えるという衝撃 | 非IT企業に勤める中年サラリーマンのIT日記
上記サイトでは select, insert は例がのっていましたが、update に関しては記載がなかったので試してみます。
Fusion Tables は上記サイトを参考に使える状態にしておきます。
Fusion Tables に update 文実行
Update する際にどうも ROWID という行番号以外は指定できないようです。 このため事前に以下のようなselect文でROWIDを取得してから
// Utilities.formatString でなぜかサーバーエラーがでるようになるため文字列連結 var sql = "SELECT ROWID FROM " + ファイルID + " " + "WHERE " + "AND PROJECTID = '12345' " ; var rowid = FusionTables.Query.sql(sql).rows[0];
以下のようなUpdate文で更新する必要があります。
- これ誰か他に良い方法が他にあれば教えてください。
var sql = "UPDATE " + ファイルID + " " + "SET value = 1 " + "WHERE ROWID = '12345' ; FusionTables.Query.sql(sql); // UPDATEされるはず
Fusion Tables の1テーブルごとに最大行数ってあるの?
ドキュメントがまだ見つからない、、、
どこかに記載あるのだろうか。
以下記事の情報もガセネタっぽい。
Google Script Api で Webサイトスクレイピング
特定サイトページの商品金額を抜き出してgoogle spreadsheetに書き込むために、スクリプトエディタの Google Script Api で Webサイトスクレイピング やることになったのでいろいろ Google Spreadsheetと連携できるGoogle Script Api で Webサイトスクレイピングできる方法をさぐってみた
HTMLの一部分を抜き出せる
Easy data scraping with Google Apps Script in 5 minutes ~ kutil.org
これだと1ページで何箇所か金額部分だけ抜き出すときなど、苦労しそう。
正規表現でごりごりスクレイピング
正規表現マスターしてると楽なんでしょうが、DOM操作できるようなライブラリが欲しいな、と。
XML Service Service
googleが提供している XML Parserあるんですが、
XML Service Service | Apps Script | Google Developers
以下ページを見ると有効なXMLしか扱えないようで、使い勝手が悪いようです。 * 既存のHTMLベージはだいたい有効なXML形式になってないですからね、下記ページでもそのことに触れられています
結局どの方法がいいんだ? -> Xml.parse で解析したデータに、XmlService.parse して解析する
stackoverflowでも取り上げられてました。
Xml.parse で解析したデータに、XmlService.parse して解析すると良さそうとのこと。 Xml.parse で有効なXML形式に変えてくれるんですね。
var page = UrlFetchApp.fetch(contestURL); var doc = Xml.parse(page, true); var bodyHtml = doc.html.body.toXmlString(); doc = XmlService.parse(bodyHtml); var root = doc.getRootElement();
試しに上記で実際にPGうごかしてもたら body が複数配列で取れてしまう場合もあるらしいのでその場合は
var bodyHtml = doc.html.body[1].toXmlString();
などして切り抜ける
さらにDom操作しやすくする
以下のページのソースを拝借するとDom操作しやすくなりますので、こちらも使用するとさらに使い勝手よくなります。これでしばらくいけるんじゃないか説。
yoshiyuki-hirano.hatenablog.jp
サンプル
function sample() { // dom取得 var contestURLBase = "https://www.makuake.com/discover/projects/search/"; var contestURL = contestURLBase + "1"; var page = UrlFetchApp.fetch(contestURL), doc = Xml.parse(page, true), bodyHtml = doc.html.body[1].toXmlString(), // なぜかbodyが2つとれるので[1]の方のみ対象 xmldoc = XmlService.parse(bodyHtml), xml = xmldoc.getRootElement(), projects = getElementsByClassName(xml, 'projectBox'); for (var i = 0; i < projects.length; i++) { var project = projects[i]; // タイトル取得 var item = getElementsByTagName(project, 'h2'); Logger.log(item[0].getValue()); } }
所有しているサイトの検索キーワード数を取得してGoogle Spreadsheetに値を記載する
はじめに
所有しているサイトが特定のキーワードでどれくらい検索されているかGoogle Search Consoleで調べてGoogle Spreadsheetに記載して日々計測する必要があったため、その際に行った手順を残しておきます。
検索回数や表示回数を取得できる Google Search Console
Google Search Console(以前は Webmaster tool だった気がします)という以下のURLで、自分が所有しているサイトはどのようなキーワードで検索されたのか?また、そのキーワードで何回表示されて何回クリックされたなどの値が取れます。
以下のような感じで確認できます。
こちらの値をなんとか Google Spreadsheetの方に引っ張ってきて、毎日記録させようと思います。
苦戦しながら Google OAuth2 でリフレッシュトークンなどを取得する
Google Search Console の値を取得するために、GoogleDevelopers の Search Console API(Webmaster Tools API)を使用します。
Search Analytics: query | Search Console API (Webmaster Tools API) | Google Developers
さらにAPIを使用するために、Google OAuthを利用する必要があるのですが、これがややこしい手順を踏む必要があるります。
ただ、そのややこしい手順をまとめてくれているサイトがあるので、以下ページを参考に リフレッシュトークンというやつを取ります。
端的にいうと、このリフレッシュトークンがないと長い間(たとえば3ヶ月など)、バッチなどでPGで自動実行してGoogle Search Consoleの値を取得することができなくなってしまいます。
詳しいトークンの説明は以下に譲ります
上記ページに細くすると以下点に注意が必要です
Google Search Console の scopeは以下 https://www.googleapis.com/auth/webmasters
redirect_url は以下ページに詳しく記載がある。 qiita.com
それではGoogle Search Consoleの値にアクセスして値を取得してみます
Googleにログインして、Google Spreasheet で新しいドキュメントファイルを作成します。
[ツール] - [スクリプトエディタ] を選択して、スクリプトエディタを開きます
以下のソースベースにsite_urlなどをカスタマイズして実行すると
function myFunction() { var opt = { "method" : "POST", "payload" : { "client_id" : GS_CLIENT_ID, // 取得した クライアントID "client_secret" : GS_CLIENT_SECRET, // 取得した クライアントシークレット "refresh_token" : GS_OAUTH_REFRESH_TOKEN, // 取得したリフレッシュトークン "grant_type" : "refresh_token" }, // "muteHttpExceptions" : true }; var res = UrlFetchApp.fetch("https://accounts.google.com/o/oauth2/token", opt); var dat = JSON.parse(res.getContentText()); var accessToken = dat["access_token"]; var filters = []; filters.push({ dimension: "query", operator: "contains", expression: "酒" }) var params = { searchType: "web", startDate: "2016-04-17", endDate: "2016-04-17", dimensions: ["query"], dimensionFilterGroups:[ { filters: filters }, ], } var opt = { "method" : "POST", "payload" :JSON.stringify(params), "contentType": "application/json", "headers" : { "Authorization": "Bearer " + accessToken, }, }; var site_url = "http%3A%2F%2Fexmaple.com%2F"; var res = UrlFetchApp.fetch("https://www.googleapis.com/webmasters/v3/sites/"+ site_url +"/searchAnalytics/query", opt); var content = JSON.parse(res.getContentText()); Logger.log(content); }
ログに以下のようなjson文字列が取得することができます。
{ "rows": [ { "keys": [ "果実酒 飲み放題" ], "clicks": 5.0, "impressions": 26.0, "ctr": 0.19230769230769232, "position": 4.8076923076923075 },
最後に取得した値をGoogle Spreadsheetに記載します。 先ほどのjsonの文字列からJSON.parseなどで必要な値を取得して以下ページを参考に必要な箇所に記載していきます。
[GAS][スプレッドシート]シートに値をセットするには : 逆引きGoogle Apps Script
スクリプトを自動化する
リフレッシュトークンを取得しているので、何度も認証する必要がないため、あとは定期的にトリガーで上記のPGを実行できるようにすれば、毎日値をGoogle Spreadsheetに追記していくことが可能です。