プロクラシスト

今日の寄り道 明日の近道

コピペで(ほぼ)一発!jQueryでオシャレな地図ツールを作ったよ。

こんにちは、ほけきよです!

私のブログにもいくつか旅行記事や都市の紹介記事があります。
それらを一括してはてなブログ上でもおしゃれな地図を表示させたい...!
そう思ってここ数日はずっとjQueryを使った地図作成をしてました。

作る上でのポイントは

  • おしゃれ感がある
  • ユーザーが使いやすい
  • 別ページに誘導できる

ということ。

まだまだ難はあれどある程度の水準まで作ることができたので、コードと一緒に使い方を紹介したいと思います。
また、コードをそのままコピペすると動くようになっていますので、ぜひお試しください。

世界地図






日本地図


コピペで一発!コードと使い方

全体の注意事項

またこちらのコードはgithubにもあげています

github.com

どうぞお使いください

そしてpull-requestお願いします!

世界地図編

コードをみる

  <!--map plugin-->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jvectormap/2.0.4/jquery-jvectormap.css" type="text/css" media="screen"/>
  <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
  <script src="http://jvectormap.com/js/jquery-jvectormap-2.0.3.min.js"></script>
  <script src="http://jvectormap.com/js/jquery-jvectormap-world-merc.js"></script>
  <!--for Japanesemap-->
  <script src="https://rawgit.com/hokekiyoo/jVectormapJP/master/jquery-jvectormap-jp-merc.js"></script>
  <script type="text/javascript" src="https://jqvmap.com/js/vmap/jquery.vmap.sampledata.js?v=1.5.0" charset="utf-8"></script>
  <center>
  <div id="world-map" style="width: 600px; height: 400px"></div>
  </center>


  <script>
    var sample_dataL = {};
    for( var tag in sample_data ){
      sample_dataL[tag.toUpperCase()] = sample_data[tag];
    }
    var pin_black = "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170729/20170729233434.png";
    var pin_red   = "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170729/20170729233449.png";
    var markers = [
      { latLng: [51.160523, 71.470356], country:"Kazakhstan", city: "Astana",
        img : "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170116/20170116013149.png",
        title : "今年はカザフスタンが熱い!日本人だからこそ行くべき『超近未来都市』",
        page:true, url : "http://www.procrasist.com/entry/kazakhstan",
      },
      { latLng: [41.715138, 44.827096], country: "Georgia", city: "Tbilisi", 
        img : "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170121/20170121204324.png",
        title : "【旅好き必見!!】個性的な建築物の宝庫!ジョージア(グルジア)の魅力",
        page:true, url : "http://www.procrasist.com/entry/georgia", },
      { latLng: [35.011636, 135.768029], country: "Japan", city: "Kyoto", 
        img : "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170702/20170702111519.jpg",
        title : "京都の最強パワースポット『三千院』の雰囲気が最高だった",
        page:true, url : "http://www.procrasist.com/entry/3000-temple" },
    ];
    $( () => {
      var map = new jvm.Map({
        container: $("#world-map"),
        map: "world_merc",
        markers: markers,
        series: {
          regions: [{
            values: sample_dataL,
            scale: ["#c8eeff", "#0071a4"],
            normalizeFunction: "polynomial"
          }],
        },
        backgroundColor: "#ffffff",
        enableZoom: false,
        showTooltip: false,
        regionsSelectable: false,
        markersSelectable: true,
        hoverOpacity: 0.7,
        regionStyle: {
          selected: {
            fill: "#666666"
          }
        },
        markerStyle: {
          initial:{
            image:pin_black
          },
          hover:{
            image:pin_red
          }
        },
        onRegionSelected: () => {
          var xs = map.getSelectedRegions();
          console.log( xs.map( x => map.mapData.paths[x].name+" ("+x+")" ) );
        },
        onMarkerTipShow:(e, el, code) =>{
          if(markers[code].page){
            el.html("<div id='marker'>"+
                    "<span style='font-size:15px;font-weigh:bold;'>"+markers[code].title+"</span>"+
                    "<br>Country : "+markers[code].country+
                    "<br>City    : "+markers[code].city+
                    "<center><img width='80%', height='80%' src='"+markers[code].img +"'>"+
                    "</center></div>"+
                    "<style type='text/css'> div#marker{width:240px; height:100%; background-color: #222222;}"
            );
          }
          else{
            el.html("<div id='marker'>"+
                    "<span style='font-size:15px;font-weigh:bold;color:#666666'>"+markers[code].title+
                    "<br>Country : "+markers[code].country+
                    "<br>City    : "+markers[code].city+
                    "<center><img width='80%', height='80%' src='"+markers[code].img +"'>"+"</span>"+
                    "</center></div>"+
                    "<style type='text/css'> div#marker{width:240px; height:100%;}"
            );
          }
        },
        onMarkerClick: (e, code) => {
          if(markers[code].page){
            console.log(markers[code].url); 
            open(markers[code].url,"blank_")
          }
        }
      });
      console.log(map);
    });
  </script>  



このコードで修正するところは次の箇所です
自分が地図上に乗せたい場所の情報をどんどんと記入していきます.

  var markers = [
    { latLng: [51.160523, 71.470356], country:"Kazakhstan", city: "Astana",
      img : "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170116/20170116013149.png",
      title : "今年はカザフスタンが熱い!日本人だからこそ行くべき『超近未来都市』",
      page:true, url : "http://www.procrasist.com/entry/kazakhstan",
    },
    ...
  ]

以下要素の説明です

  • latLng : その土地の緯度経度を示す。*1
  • country/city : 国名/都市名を表す
  • img : その場所のアイキャッチとなる画像のリンクを貼る
  • title : タイトルを記入する.記事なら記事タイトル
  • page : 記事リンクがあるかどうか.あればtrue/なければfalse
  • url : (記事リンクがあるときに)記事リンクを貼る


また
ピンの画像は私のはてなフォトライフから持っていってるので変えたい人はここをいじってくださいな

  var pin_black = "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170729/20170729233434.png";
  var pin_red   = "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170729/20170729233449.png";

日本地図編

コードをみる

  <!--map plugin-->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jvectormap/2.0.4/jquery-jvectormap.css" type="text/css" media="screen"/>
  <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
  <script src="http://jvectormap.com/js/jquery-jvectormap-2.0.3.min.js"></script>
  <!--for Japanesemap-->
  <script src="https://rawgit.com/hokekiyoo/jVectormapJP/master/jquery-jvectormap-jp-merc.js"></script>
  <center>
  <div id="jp-map" style="width: 600px; height: 350px;"></div>
  </center>
  <script>
    var pin_black = "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170729/20170729233434.png";
    var pin_red   = "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170729/20170729233449.png";
      var sample_dataJ = {"Hokkaido":54.2, "Kyoto":46.3, "Tokyo":35.1, "Okinawa":32.7, "Kanagawa":30.2, "Nara":28.7, "Osaka":24.1, "Nagano":24.0, "Ishikawa":23.3, "Fukuoka":21.4, "Nagasaki":20.0, "Shizuoka":17.6, "Aichi":17.2, "Miyagi":16.8, "Oita":16.5, "Hyogo":16.3, "Aomori":15.8, "Toyama":15.4, "Akita":14.9, "Chiba":14.4, "Kumamoto":14.2, "Kagoshima":14.2, "Hiroshima":14.0, "Miyazaki":13.4, "Ehime":13.1, "Mie":12.6, "Shimane":12.4, "Wakayama":12.0, "Kochi":11.9, "Yamanashi":11.7, "Okayama":11.4, "Yamaguchi":11.3, "Shiga":10.9, "Kagawa":10.8, "Niigata":10.5, "Iwate":10.3, "Yamagata":10.0, "Saga":9.4, "Fukushima":9.2, "Saitama":9.2, "Fukui":9.1, "Gifu":9.0, "Tottori":9.0, "Tokushima":9.0,"Gunma":8.5, "Tochigi":7.8, "Ibaraki":7.7};
    var markersJP = [ 
      { latLng: [35, 135], country: "Japan", city: "Kyoto", 
        img : "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170702/20170702111519.jpg",
        title : "京都の最強パワースポット『三千院』の雰囲気が最高だった",
        page:true, url : "http://www.procrasist.com/entry/3000-temple" },
      { latLng: [35, 140], country: "Japan", city: "Ibaraki", 
        img : "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170426/20170426044454.jpg",
        title : "美しすぎる青の絨毯、ひたち海浜公園の『ネモフィラ』",
        page:true, url : "http://www.procrasist.com/entry/hitachi-nemophila" },
      { latLng: [36.16, 136.5], country: "Japan", city: "Gifu", 
        img : "https://cdn-ak.f.st-hatena.com/images/fotolife/i/imslotter/20170406/20170406232850.jpg",
        title : "【旅と写真】魅惑の岐阜県物語 in 高山・白川郷",
        page:true, url : "http://www.procrasist.com/entry/gifu-1" },
      { latLng: [24.2, 123.5], country: "Japan", city: "Okinawa", 
        img : "https://lh3.googleusercontent.com/AMTniNf0dj3P1xss1J2zOH-fNtOAI6IefqNTUHQ5jpPKG6XpO_0ZXvlzTg9--3S5O1cECYqvsUYvWOQIXuPESHYKB31x4QFcEfoFivCigz5IVEfHeCpz5A065s8LHlh2Lon8sfgngFYVzAvfT0LI4WIn-dUi5iMJCvZQqmNrNITdeiJ10iO2GopmoxW9QgIfh_W4QtlEK-qn5FA1UJI0dFZKdJL7YH7PiUNAv8p2RfpXMMHv2M5NxD733bZLS8qACggux6npmBqMlByl21ZJpAxkNrqJanpfctey2cd4qe6vzOgVLscLSgkwl-cJR_xdkgQ7qJTKQb7VRm0NQ_hh-GLrqXqmbfJ4TaiqVIj7w67ZT5qubdGrX7_PMGXpquosm6mYL721eg9ydIu1AqVIe9H75tbwSXnBnxLhZjflKj8tOhDDC2ZqXTlUjA_lZesX00Sw4ZeERYFtPjyxWv3dAorJBytvdbL6qfdZUr1-3usRHg_cW83aHOphVApx7-o4dzidk0Yz1K5P9mgvu8Rdqt_zMop3tvv_yIWkT1lyLLhZdQgC67h4p2j84AymyE0bhqbeU-E2Bk_kHQDXhWKyDWD47Og8yNl_kO_D7fibaE77X2lg-iE9nVmL=w1690-h1126-no",
        title : "西表の海",
        page:false,
      },
    ];
    $( () => {
      var map2 = new jvm.Map({
        container: $('#jp-map'),
        map: 'jp_merc',
        onRegionTipShow: ( e, el, code ) => {
          el.html( el.html() );
          console.log(code);
        },
        backgroundColor: null,
        enableZoom: false,
        showTooltip: false,
        regionsSelectable: true,
        markersSelectable: true,

        hoverOpacity: 0.7,
        regionStyle: {
          selected: {
            fill: '#666666'
          }
        },
        series: {
          regions: [{
            values: sample_dataJ,
            scale: ["#c8eeff", "#0071a4"],
            normalizeFunction: "polynomial"
          }],
        },
        markerStyle: {
          initial:{
            image:pin_black
          },
          hover:{
            image:pin_red
          }
        },
        markers: markersJP,
        onRegionSelected: () => {
          var xs = map.getSelectedRegions();
          console.log( xs.map( x => map.mapData.paths[x].name+" ("+x+")" ) );
        },
        onMarkerTipShow:(e, el, code) =>{
          if(markersJP[code].page){
            el.html("<div id='marker'>"+
                    "<span style='font-size:15px;font-weigh:bold;'>"+markersJP[code].title+"</span>"+
                    "<br>Country : "+markersJP[code].country+
                    "<br>City    : "+markersJP[code].city+
                    "<center><img width='80%', height='80%' src='"+markersJP[code].img +"'>"+
                    "</center></div>"+
                    "<style type='text/css'> div#marker{width:240px; height:100%; background-color: #222222;}"
            );
          }
          else{
            el.html("<div id='marker'>"+
                    "<span style='font-size:15px;font-weigh:bold;color:#666666'>"+markersJP[code].title+
                    "<br>Country : "+markersJP[code].country+
                    "<br>City    : "+markersJP[code].city+
                    "<center><img width='80%', height='80%' src='"+markersJP[code].img +"'>"+"</span>"+
                    "</center></div>"+
                    "<style type='text/css'> div#marker{width:240px; height:100%;}"
            );
          }
        },
        onMarkerClick: (e, code) => {
          if(markersJP[code].page){
            console.log(markersJP[code].url); 
            open(markersJP[code].url,"blank_")
          }
        }
      });
    });
   </script>


日本地図編の場合もほとんど同じです.ただしcountryは必要ないので削除しました

見栄え

こんな感じです

  • ポインタを置いたピンが赤色になり先程設定した必要情報がポップアップ的に出てくるようになります
  • リンクがあるものは白色リンクのないものはグレーで説明文を書いています

ちなみに、この日本地図ですが、読者のid:Ysmr_Ryさんが実装してくれました!!
技術力が強い人、素晴らしい...!ありがたく使わせてもらいます~。


彼のgithub(GitHub - asSqr/jVectormapJP)の、jquery-jvectormap-jp-merc.jsが日本地図に当たる部分なので、
皆さんも是非使ってみてください!

注意点(TODO)

使いやすさのためにいくつかまだ改良しないといけないところもあります。
TODOということで今後の予定を書き記しておきます!

レスポンシブ対応

現在大きさを固定しての提供になっています
速度の兼ね合いもありますが
レスポンシブ対応にしてよしなな大きさで表示されるようにしたいです

スマホでの操作性向上

いかんせんスマホでポインタが触りづらい!とりあえず現在はデスクトップ推奨のマップになっています

ここが直れば良さそう!っていう目星はあるのですがまだ手をつけられていません
そのうちつけてgithubにあげます

そのときは記事の更新とtwitterのアナウンスをするので登録しておいてね!

まとめ


いかがでしたか?
(PCで見ると)割と使い勝手の良い地図ツールができたと思います

htmlを触っているだけなんではてな以外でもおそらく使えます

私自身地図を作るにあたってほとんど初めてjavascriptを勉強してたのですが
なかなか便利ですね!ややこしいけど使えるようになると幅が広がるなって感じです
今後もいろいろ作っていけたらなと思います

私の地図奮闘記は過去記事にもあるのでどうぞ

みなさんも旅の思い出をおしゃれなマップとともに保存しましょう!ではでは

*1:緯度経度の表示にはをこちらを使うととても便利

10ヶ月で10万PV達成!したこととか今後の目標とか

こんにちは!ほけきよです。

以前の記事で「次は10万PVいくまで運営報告はしない」 といって早数ヶ月。ようやく10万達成しました!!

ブログ始めた当初からの目標でもあったので、素直に嬉しいです。あと、いろんなことがあった一ヶ月でした。 というわけで、いろいろ振り返ってみる

サマリ

差分
PV 105846 +49183
収益 諭吉2.5人 +1人
読者 436 +157

とりあえず、10万めでたい。嬉しい!

軌跡

どういう感じで10万PVまでいったのか、軽くグラフにしてみた。

赤が全体、青が検索流入。 検索流入は堅調に伸びている。今月の伸びは、4~5記事のバズが大きかった!

今月振り返り

振り返ると、大きくいろいろ動いた一ヶ月だったかも。

デザイン変更 : codomisuカスタマイズを依頼

6月の終わりくらいかな。以前から手をつけようと思っていたサイトカスタマイズですが、ちょうど id:codomisuさんがカスタマイズサービスを始めるとのこと。

hitsuzi.hatenablog.com

価格もリーズナブルなので、すぐ依頼が殺到する気がしたので、速攻でフルカスタマイズを依頼!

こどみすさん、めちゃくちゃレスポンス早い。初めての外注でしたが、おかげで良いものが作れました。日々のモチベーションにもなるし、読者の方に「デザインかっこいい」って言われるのがめっちゃ励みになります!やはりその道のプロに依頼するべきだなと思いました。ありがとうございます!

オフ会@上野動物園

twitterで仲良くしているみるたん(id:salawab)とけいろーさん(id:ornith) が主催してた上野動物園オフ会に。これがすごかった。 「いつか会ってみたい」クラスのブロガーさんたちしかいなかった。強い。

上野動物園にみんなで行って、最後ワロさんとヒトデちゃん、あさえちゃんとテニスして帰宅。テニスすごい、セレブっぽかった。 翌日から手首が壊れたけど、また行きたい!

オフ会の様子は彼らのオフレポに書かれています。「記事が面白い人は、リアルでも面白い」「ブロガーとしてPVや収益出しているひとは行動力高い」と思った。ブログの相談に乗ってくれたりもして、めちゃくちゃ刺激をもらったので、頑張る!

blog.gururimichi.com

miruokalife.hatenablog.com

バズ

自慢させてくださーい☺️

何件かホッテントリ入りしました。 今月はブログSEO関連の勉強していて「あ、めんどくせー。自動化しよ」 程度のモチベーションでいろいろ作ったものが、結果として良かったみたいです。

人が困っていることを記事にするのは、やはり大事だなぁと思いました。とりあえず今月の自ブログはてブ数ランキングです。

記事 ブクマ
1 大学の数学/物理を無料で学べるおすすめサイト・サービス6選 1432
2 【コードで一発】ブログ最適化/SEO対策で面倒なことは全てPythonにやらせよう 475
3 ブロガー1年間生存率は?読者数の伸びは?2万件のはてなブログで分析する 397
4 もう試験で困らない!√2の求め方10選 157
5 SEO対策!自分のブログの内部リンクを自動で可視化する 93

また、週間ブクマ数ランキング1位も獲得できました。うれp

僕は文章力がないので、技術にたよっちゃいます。けれど、技術を身近なものに応用したり、わかりやすく伝える努力はブログを通して頑張っています。 これからも、役に立つ&みんなに楽しんでもらえる記事を書きたいな。

ネタ募集、お仕事の依頼

そういや連絡先書いてなかったので、書いてみます。

やってほしいネタとか、募集してみます!

imslotter1あっとgmail.com

是非是非、気軽にお声がけください!

今後の目標

最後に今後の目標です。 今までが10万PV/月だったので、 次は

『検索流入10万/月』

です! やはり、「バズ流入はカンフル剤、検索流入精神安定剤」なわけです。 バズを何回か経験して、限られたコミュニティでの拡散の限界を感じました。 安定して10万流入を達成したいです。

また、データをみると、検索流入が、一番アドセンスコスパが良いのです。 これは私の流入アドセンス収益です。1PVあたりどのくらい儲かるか(PV単価) を見ています。

くらいの内訳ですね。これを見ると、明らかに検索流入はすごいことがわかります。 なので、今後はここの強化をしていきます!

難しいですが、なんらかの策は講じられると思っています。

最後に

いつも見に来てくださる皆さんありがとうございます。

わりと技術に偏っている節のあるブログですが、これからも楽しんでもらえるよう、また皆さんにとってちょっとでも有益な記事をつくっていきます。これからもよろしくお願いします!

jQueryでブログ内にオシャレな地図を表示したいんじゃ

【※重要!】地図ツール改良版作りました

www.procrasist.com

この記事をサラッと見た後に、↑のに行くとすんなり見られると思います。なかなか出来は良いと思いますので是非是非!

こんにちは、ほけきよです。

先日へそちゃさん(id:hesocha)がステキな固定ページの使い方を紹介してました。

おっ、これは良い。

と思い、導入を検討。 今回は、

  • (わかれば)自分でいろいろといじることが出来る
  • オシャレ

という理由から、javascriptで地図表示を試みて見ました。 しかし、雑魚なので色々と手詰まりなところもあったり...

この辺の知見がある方は、教えてください👶
  • javascript, jQuery初心者ですので、もっといい方法があるよという人は優しく教えてください。
  • オシャレですが、動作は遅くなりそう。

オシャレなjQueryのMapライブラリ : "jQuery vector map"

イマドキのワカモノはじゃばすくりぷとというのでなんでもオシャレにするらしい。じゃあ地図もあるのでは?

と思い調べるとナウなヤングにピッタリのjQueryプラグインがあるっぽい?

なので、jQueryで悪戦苦闘してみる。ちなみにいじったことはない。

github.com

*1

どうやってjsスクリプト埋め込むのよ?

まずこれ。ブログ内にjavascriptって埋め込めるの??という疑問。

しらべると、チャートグラフでしたいことをやってる人いた。ありがてぇ。

lance104.hatenablog.jp

HTMLを普段から触っている人にしてみれば簡単なことかもしれませんが、 <script>タグでスクリプトを埋め込めるんですね!たのしー

ほむほむ。記事には

  • インポートするライブラリはデザインタイトル下にぶち込む
  • はてな記法にする

と書かれていましたが

です。

あとは、javascriptで実行したい処理を、次の記述で挟むことがポイントのようです。

$(document).ready(function(){
  //何かしらの処理
});

コチラの記事が参考になりました。

jQueryの基本 - $(document).ready - Qiita

MAPを埋め込みたい

これでjQueryを埋め込めることがわかったので、後はマップをぶち込みましょう。

ライブラリの読み込み厄介?

さっきのmapのTOPページに行くと

...
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="../dist/jquery.vmap.js"></script>
<script type="text/javascript" src="../dist/maps/jquery.vmap.world.js" charset="utf-8"></script>
....

この../distってのが厄介。そもそもローカルに落として使う想定のようなので、はてなのようなサーバを借りているとこのままじゃアカンみがある。そこで、直接リンクさせて、ライブラリを読み込むことにする

便利なのがこいつ。

RawGitでGithubファイルをCDN

rawgit.com

参考記事はこれ

GitHub上のファイルをCDNとして参照する - Qiita

  • CDNとは、Content Delivery Networkのこと
  • ファイルなどのWebコンテンツを配信するために最適化されたネットワークのこと。有名なライブラリだと、インターネットで公開されており、JavaScrptやCSSファイルをダウンロードしなくても、読み込むだけで利用できるようになっていたたりする。
  • GitHub上のファイルは、インターネットで公開されていても、テキストとして読み込まれるため、直接URLを指定してもJavaScriptCSSとしては利用できない。
  • Rawgitを使えば、GitHub上のファイルのURLを指定するだけで、CDN化してくれる。
  • ふむ、良さげ。これを使おう。

    ちず

    オシャレ、ええね。

    コピペでOK!コード

    こういう地図を表示させたい方のために、コードです。はてなならたぶん動きます。

    コードをみる
    <link href="https://rawgit.com/manifestinteractive/jqvmap/master/dist/jqvmap.css" media="screen" rel="stylesheet" type="text/css">
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script type="text/javascript" src="https://rawgit.com/manifestinteractive/jqvmap/master/dist/jquery.vmap.js"></script>
    
    <script type="text/javascript" src="https://rawgit.com/manifestinteractive/jqvmap/master/dist/maps/jquery.vmap.world.js" charset="utf-8"></script>
    <script type="text/javascript" src="https://jqvmap.com/js/vmap/jquery.vmap.sampledata.js?v=1.5.0" charset="utf-8"></script>
     
    <script>
    (function($){
    $(document).ready(function(){   
        $('#vmap').vectorMap({
            map: 'world_en',
            backgroundColor: null,
            color: '#ffffff',
            hoverOpacity: 0.7,
            selectedColor: '#666666',
            enableZoom: true,
            showTooltip: true,
            scaleColors: ['#C8EEFF', '#006491'],
            normalizeFunction: 'polynomial',
            values: sample_data,
            markerStyle: {
                initial: {
                    fill: '#F8E23B',
                    stroke: '#383f47'
                }
            },
            markers: [
                {latLng: [41.90, 12.45], name: 'Vatican City'},
                {latLng: [43.73, 7.41], name: 'Monaco'}
            ],
        })
    });
    })(jQuery);
    </script>
    <div id="vmap" style="width: 300px; height: 200px;"></div>
    

    にほんちず

    ↑のは世界地図。日本地図ってないのかな。 探すと作ってくれている人がいました。すてち!

    GitHub - takemaru-hirai/japan-map: jQuery plugin to show clickable map of prefectures (or area) of Japan.

    ところで、助けて欲しい。

    表示は出来たのですが、それ以降が全然進まず手詰まりなのです。 jQueryに詳しい方々に助けてほしいです。

    世界地図

    表示以外の細かな調整をしようとしたら何故か出来ないんですよね。むずかしい。。。

    • Configuration Settingsが上手くいかない*4
      • マーカーを打ちたいけど、打てない (参考)
      • クリックしたら情報を表示させたいけど、出来ない(参考)

    日本地図

    • クリックの位置が地図の位置と同期が取れていない

    まとめ

    いかがでしたでしょうか。 jQueryをうまく使うことで、表現の幅が広がりそうですね。 皆さん是非試してみてください!そして助けてください!笑

    オシャレなの、作りたい~~!

    *1:もう一つ、Mapaelというプラグインもありました。こちらも便利そうなので試したい

    *2:むしろある特定の記事だけに使いたいならこっちのが良いかも?

    *3:間違いなくHTML記法でもOKでしょう。面倒でやってませんが。

    *4:ページのソースにjsのコードがあります。タスケテクダサイ~~

    【書評】恋愛を数学する : 数学で突きつけられる恋愛観は意外と深い...?

    こんにちは、ほけきよです。

    めっちゃ面白そうな本を友人に教えてもらい、秒で購入し、読んでみました。 予想通り面白かったので、紹介します。

    恋愛を数学する

    恋愛を数学する (TEDブックス)

    恋愛を数学する (TEDブックス)

    私も日常を良く科学のネタにしています。

    身近な現象を科学で再考してみるっていうのが、かなり楽しいんですよね! 今回の本は、そんなワクワク感をそのまま凝縮したような本!

    恋愛ですよ!これは読まずにはいられない。というわけでKindleでポチィ

    「恋愛」の指南書ではない

    まずはじめに!これは言っておかなければいけないかな。 この本の対象を筆者の言葉を借りながら。

    恋愛がもたらす感情を表現するのに、私はふさわしい人間でもありません。もしそれらをお探しでしたら、過去5000年にわたって創作されてきた絵画、詩、彫刻、歌といった芸術は殆どがそうですので、そちらの本棚に目を向けたほうがいいでしょう。

    この本の目的は、あなたの恋愛人生を明るくすることではありません。私の願いは、数学がいかに美しくてためになるかを明らかにすることにあります。

    この本は、一般向けに、かなり噛み砕いて数学の説明をしてくれています。 それをネタにして、恋愛の過程で起こる数々の現象をデータやモデルから明らかにしようとしています。

    なので、数学がわからなくても読めるが、冗談がわからないと読めない本です。

    ちょっとおもしろそう!読んでみよう!

    くらいの気持ちで読んでみることをオススメします。きっと数学の美しさにのめり込むし、新たな知見を得られることでしょう!

    目次がすでに魅力的

    目次を見るだけでもかなり面白いと思ったので、掲載!! ついでにどんな理論を使って語るかを、脚注に載せてみました。気になる方は是非脚注を。更に気になる方は書籍をどうぞ。

    1. 恋人が出来るチャンス*1
    2. 美しさはどれくらい重要か*2
    3. 夜遊びの喜びを最大化するには*3
    4. オンラインデート*4
    5. 恋の駆け引き*5
    6. セックスの数学*6
    7. いつ身を固めればいい?*7
    8. 結婚式を最適化する方法*8
    9. 末永く幸せに暮らすために*9

    *10

    恋愛の過程が章ごとに並べられています。これらの各過程が数学で語られるなんて、なんとも生々しく興味深くありませんか??6章なんて…ねぇ?(o^-^o)

    無味乾燥な数学から現れる結果は、意外と深い…?

    ただの数遊びかと思えば、実は侮れなかったりもします。

    筆者の文中の言葉を借りながら(一部文章を改変しています)、どんな結果が現れるのかをチラ見せします。

    合コンは誰と一緒に行けばいいか

    2章にて。経済学の囮効果を引用しながら、こんな結果を出してしまいました。

    自分をより魅力的に見せるためにはどうすればいいか、その教えは明らかですね。パートナーを探しに行くときは、一緒に行く友達を選びましょう。あなたになるべく似ていて、ちょっとだけ魅力に欠ける友達をつれていけば、あなたがより良い選択肢になることでしょう。(中略)こうした判断は私達が皆本能的にやっていることを思い出してください。

    そういやオレあの時数合わせで合コンさそわれたな。本能。。。(´;ω;`)ブワッ

    自分をどう表現すればよいか。

    4章にて。あるマッチングサイトでのメッセージデータを多変数回帰を用いて分析すると、次のような実に非自明な結果が導かれます。

    「マッチングサイトでは、評価が5点満点中3点に固まっているひとよりも、1点と5点に分かれている人のほうがメッセージを得られやすい

    この結果から、次のようなアドバイスをしています。

    あなたのことを綺麗だと思ってくれる人が多少ともいるなら、その他には醜いと思われるような極端な評価の顔の人のほうが、誰からもそこそこ可愛いと思われるよりもずっといいのです。自分の魅力を表現するならば、人から好まれなさそうなところも含めて、あなたが他人と違って見えるところを取り入れるべきなのです。

    八方美人として生きてきたワイ、刺さる(´;ω;`)

    恋愛において妥協をすべきか

    7章にて。秘書問題と呼ばれる問題。出会った何人目の人と結婚すればよいかというのは、数学で定式化できるのです。

    この問題を拡張して、「妥協すればお見合いや合コンの成功確率がどのくらい上がるのか」というのを、シミュレーションしています。その結果がコチラ

    完璧を求めると、完璧なパートナーと一緒になれる確率は37%です、しかし、選り好みをせずにトップ15%に入る人と一緒になれたら幸せとすると、幸せになれる確率は78%にも跳ね上がります

    妥協、、、数字を出されると弱いアタシ...😥

    幸せな家庭を気づくために

    9章にて。最後は結婚後どう暮らしていくかですね。これはコミュニケーションをモデル化してシミュレーションしています。そのシミュレーション結果として、夫婦が仲良くくらすには、次のような結論が導かれたといいます。

    末永く幸せに暮らすというのは、単に気兼ねなく不満を漏らせるということだけではないからです。会話における言葉遣いは、不満を言うにしても寛容で理解あるものにすべきです。(中略)「日が暮れるまで起こったままではいけません」という古来の叡智を、数学は改めて教えてくれるのです。

    そういうもんかねぇ。ボクには分かんないや!👶

    まとめ

    いかがでしたか?このように、数学をこうやって使うんだという再発見もできますし、また恋愛についても意外と深い洞察を与えてくれていたりします。難しい数式はできるだけ簡略化し、読みやすくなっているので苦手意識を持つ方もきっと読めると思います。

    ここまでの内容に興味を持った方は、ぜひ一読してみてください。きっと数学にも、恋愛にも面白い洞察が得られると思います!!

    恋愛を数学する (TEDブックス)

    恋愛を数学する (TEDブックス)

    ただし、あくまでもこれは数学を楽しむための本です。数学の持つ力を勘違いしないように、最後は エピローグの筆者の言葉で締めくくります。ではではっ!

    この本で取り上げた例について、中身がなく軽薄でつまらないと言って取合わなかったり実際の恋愛にどこまで当てはまるのかとケチをつけるのは簡単かもしれません。そういう態度では、こうした例から得られる真の洞察を逃してしまうのではないのでしょうか。(中略) 数学とは現実を抽象化することであって、現実を複製することではありません。そのプロセスの中にこそ、数学の本当の価値があるのです。世界を抽象的な観点から眺めてみることで、普通なら隠されたままのパターンやメカニズムを捉えて記述できる、またとない言語が得られます。

    *1:フェルミ推定

    *2:経済学の囮効果

    *3:ゲーム理論/安定結婚問題

    *4:多変数回帰

    *5:囚人のジレンマ しっぺ返し戦略

    *6:べき分布

    *7:最適停止理論

    *8:組合せ最適化(一般化割当問題)

    *9:夫婦喧嘩の数理モデル

    *10:脚注を見ていただければわかりますが、割と経済学の理論が多めです。人を扱う学問として応用が効くってことなんですかね。興味深いです。

    もう試験で困らない!√2の求め方10選

    こんにちは!ほけきよです。

    ○○の求め方シリーズ第二弾! 第一弾はコチラ

    私の大学の統計学のテストでは、関数電卓*1を持ち込むことが可能でした。 √やlogの計算が必須だからです。 しかし、ある友人は関数電卓をテスト当日に忘れたのです。

    「お前www\sqrt{2}とか\log{2}とかどうやって計算するの??www」

    と煽ったら、彼は

    「そ、そんなん\sqrt{x}とか\log{x}をテーラー展開すればええやんか!!」

    と言い放ちました。天才。彼のひらめきに脱帽しました。*2

    しかし、\sqrt{2}のためにテーラー展開をするのも、なんだか大層なものですね。 そこで今回は、\sqrt{2}の求め方を10個紹介します!!

    TPOに適した\sqrt{2}の求め方を学びましょう!

    0. 語呂合わせ

    まず、\sqrt{2}の暗記法をば。

    \sqrt{2} =  1.41421356 237309504880\cdots

    「ひとよひとよにひとみごろ」

    と覚えたと思いますが、インパクトが足りません。これじゃ覚えられません。

    「いよいよ、にいさんころす」

    と覚えましょう

    紙を使う

    あなたがもしものさしを持っているならば、長さを図るだけで\sqrt{2}を求められます。ものさしの精度は高いほど、良い近似値が得られるので、なるべく精巧なものさし を用意しましょう!

    1. 折り紙

    最も身近な\sqrt{2}です!三平方の定理を使いましょう。冒頭の画像の通り,正方形の折り紙を対角線上におると、斜めの長さが\sqrt{2}になりますね! ものさしで斜めの長さと、折り紙の一辺の長さを求めて、比を出しましょう。

    \frac{斜め}{一辺}=\frac{\sqrt{2}l}{l}=\sqrt{2}

    2. プリンタ用紙

    テスト用紙で配られるの、大体A3用紙とかじゃないですか。 そうすれば話は早いです。縦と横の長さを測り、比を取ると\sqrt{2}となります。

    縦横比が1:\sqrt{2}となる比を白銀比と呼ばれています。実は我々の身近なところに存在しているのですね!

    方程式の解として

    二次方程式x^{2}-2 = 0の解は\sqrt{2}なので、精度良く求められたらOKです!!

    3. 二分法

    解探索のもっともナイーブな方法です。アルゴリズムは以下の通り

    • グラフ上で、解を正負で挟むように2点を求める
    • 2点の中点を求める
    • グラフ上で、中点と正負が一致する方の点を置き換える
    • 繰り返し

    Wikipediaより引用

    実装はコチラ

    コードをみる
    def f(x):
        return x**2-2
    def bisection(iteration):
        # 初期値. 1<√2<2 は自明なのでそこから始める
        a = 1.0
        b = 2.0
        for i in range(iteration):
            c = (a+b)/2.0 # 中点を出す
            if f(a)*f(c) < 0:
                b = c
            if f(b)*f(c) < 0:
                a = c
        return c
    print("iteration:{} sqrt2:{}".format(10, bisection(10)))
    print("iteration:{} sqrt2:{}".format(20, bisection(20)))
    print("iteration:{} sqrt2:{}".format(100, bisection(50)))
    

    結果はこんな感じに

    iteration:10 sqrt2:1.4150390625
    iteration:20 sqrt2:1.4142141342163086
    iteration:100 sqrt2:1.414213562373095

    だんだんと値が近づいていっているのがわかりますね。

    4. ニュートン法

    これも有名な方法ですね、接線を利用するのです!

    • 一点を選ぶ
    • グラフの接線を求める
    • 接線とx軸との交点を次の点のx座標にする

    Wikipediaより引用
    コードをみる
    def f(x):
        return x**2-2
    
    def f_diff(x):
        return 2*x
    
    def newton(iteration):
        a = 1000 #初期値
        for i in range(iteration):
            a -= f(a)/f_diff(a)
        return a
    print("iteration:{} sqrt2:{}".format(1, newton(10)))
    print("iteration:{} sqrt2:{}".format(5, newton(20)))
    print("iteration:{} sqrt2:{}".format(10, newton(100)))
    

    結果はこんな感じ

    iteration:1 sqrt2:1.5795487524060154
    iteration:5 sqrt2:1.4142135623730951
    iteration:10 sqrt2:1.4142135623730951

    関数の微分が必要ですが、初期値が1000とか的はずれでも、5回もイテレーションを回せばほとんど\sqrt{2}になってるので、収束は二分法より早く、かなり優秀ですね。

    反復的に求める

    √がわからなくても、四則演算を繰り返し行うだけで近似値を出す方法はいくつかあるのです!覚えておいて損はないかも!?

    5. 開平法

    √を外す方法と言えばまずこれだろう!という有名な方法ですね。 中学校で聞いたことある人も多いのではないでしょうか。

    「開平法しらん!」という方は、コチラの記事を見ましょう。やり方が載っています(開平法 )

    手書きですが、手順的なものも書いてみました。

    アルゴリズムはこちら。*3

    コードをみる
    def solve_b(a,e):
        for b in range(10):
            if (a+b)*b > e:
                return b-1
        return 9
    
    def kaihei_2(iteration):
        # 初期値だけ
        a = 1
        b = 1
        sqrt2 = 1
        c = a*b
        e = (2-c)*100
        for i in range(iteration):
            a = (a + b) * 10
            b = solve_b(a,e)
            a += b
            c = a * b
            e = (e - c) * 100
            sqrt2 += b*0.1**(i+1)
            print(sqrt2)
        return sqrt2
    kaihei_2(10)
    

    実行結果はこんな感じ

    1.4
    1.41
    1.414
    1.4142
    1.41421
    1.414213
    1.4142135
    1.41421356
    1.414213562
    1.4142135623

    繰り返せば繰り返すほど、精度が1桁ずつ上がるというわけです!すごい!

    複雑だけど、良く出来てるよなぁ。

    6. 相加相乗平均の大小関係を利用

    \frac{a+b}{2} \ge \sqrt{ab}

    相加相乗平均の大小関係をうまく利用すれば、√の近似値を精度良く求めることが出来ます

    • a(正の数)を適当に選ぶ
    • a*bが2になるようにbを選ぶ
    • \frac{a+b}{2}を次の点にする
    • 繰り返し
    コードをみる
    def sokasojo(iteration):
        # 初期値a. a>0ならなんでもOK
        a = 1000
        for i in range(iteration):
            b = 2/a #a*b=2となる様にbを選ぶ
            a = (a+b)/2
        return a
    print("iteration:{} sqrt2:{}".format(1, sokasojo(10)))
    print("iteration:{} sqrt2:{}".format(5, sokasojo(20)))
    print("iteration:{} sqrt2:{}".format(10, sokasojo(100)))
    

    結果はこんな感じ

    iteration:1 sqrt2:1.5795487524060154
    iteration:5 sqrt2:1.4142135623730951
    iteration:10 sqrt2:1.4142135623730951

    実は、ニュートン法と同じ結果になります。これは、反復のときに返す式が全く同じだからです。ただし、こっちの方法は、\sqrt{x}のみに有効となっています。

    7. 連分数

    こんな関係式が成り立ちます。*4

    これも繰り返し処理に落とし込めそうですね。実装はコチラ

    コードをみる
    def renbunsu(iteration):
        a = 1 #初期値
        for i in range(iteration):
            a = (2+1/a)
        return 1+1/a    
    print("iteration:{} sqrt2:{}".format(1, renbunsu(10)))
    print("iteration:{} sqrt2:{}".format(5, renbunsu(20)))
    print("iteration:{} sqrt2:{}".format(10, renbunsu(100)))
    
    iteration:1 sqrt2:1.4142135731001355
    iteration:5 sqrt2:1.4142135623730954
    iteration:10 sqrt2:1.4142135623730951

    物理を使う

    8. ボールを落とす

    ボールとスピードガンを持っているのならば、√2を求めることが出来ます。 エネルギー保存則を使いましょう!

    v = \sqrt{gh}

    つまり、二倍の高さから物を落とすと、スピードは\sqrt{2}倍になることがわかります。 なので、高さを変えて2回ボールを落とすことで、\sqrt{2}の近似値を求めることができます!

    9. 振り子

    紐とおもりとストップウォッチを持っているなら、物理的に\sqrt{2}を求められます。振り子の周期を用いましょう。 振り子の振り幅が小さければ、周期は以下の式になります。

    T=2\pi\sqrt{\frac{l}{g}}

    つまり、長さを変えて周期を求めることで、近似値を求めることが出来ます。 ただし、振幅が大きすぎると、上記の周期の式にずれが生じます*5

    なるべく小さな振幅で測りましょう!

    10. テーラー展開

    最後に、話の冒頭にあったテーラー展開について。

    テイラー展開っていうのは、ざっくりいうと難しい式を数学的に簡単な式に近似するヤツです。 知りたい方はこのへんの説明をみながら雰囲気を掴んでください*6。とりあえず、y=\sqrt{x}x=1でテーラー展開を実施すると、

    \sqrt{x} = 1 + \frac{(x-1)}{2}-\frac{(x-1)^{2}}{8}+\frac{(x-1)^{3}}{16}-\frac{5(x-1)^{4}}{128}+\frac{7(x-1)^{5}}{256}\cdots

    これにx=2を代入すると、

    となります。

    \sqrt{x} = 1 + \frac{1}{2}-\frac{1}{8}+\frac{1}{16}-\frac{5}{128}+\frac{7}{256}\cdots

    となります。これなら計算できそうですよね!この計算結果は、

    > 1.42578125

    うーん、イマイチ精度が悪いですね。ちなみに、この式は無限に続けられます。無限に精度を良くすることが可能というわけです。興味ある方はより高次の係数まで求め、より正確な\sqrt{2}を求めましょう!!

    ボク、面倒だからしない^^

    まとめ

    いかがでしたか??

    これでどんなときに\sqrt{2}が出ても困らないですよね! みなさんも求め方を覚えて、是非是非自薦してみてください!ではではっ!

    *1:いろんな計算が出来る高機能な電卓

    *2:ちなみに彼は評価ABCDで言うところの評価Cでした。

    *3:コードが雑でごめんなさい!

    *4:共通で現れる部分をうまく漸化式で表すと、なんでそうなるのかがわかると思います。

    *5:楕円関数

    *6:微分を知っている必要があります

    ブログは一年続くの?読者数は?2万件のはてなブログで分析する

    こんにちは、ほけきよです!

    私はそれほど実績がないので、ブログ論を語ることは出来ません。でも、データに語ってもらうことは出来ます。

    良くある「ブログは継続が難しいし、大事だ」といいますね。 そりゃそうなんですが、どのくらい難しいのか、また、どのくらい大事なのか というのはあんまりわかんないですよね。

    なので、今回は、はてなブログをぶっこ抜いて、軽く分析してみました!!

    ブロガーの生存率はどのくらいか

    今回は結論からいきましょう!

    下のグラフは何ヶ月ブログが続いているかです。縦軸がブログ数、横軸が継続期間、綺麗な曲線を描き、だんだんと下がっていっているのがわかります。

    また、次のグラフは、何ヶ月続けたらブロガー全体の何%に入るかというグラフです。

    大体

    • 3ヶ月続くブログは70%
    • 1年間続くブログは30%
    • 2年間続くブログは10%

    くらいとなってます。しかもこの条件、3ヶ月に一回書けばとりあえず継続とみなすという激アマ条件ですからね。 いかに継続だけでも困難かが分かります。

    継続は力なり?継続期間と読者数の相関

    読者数のヒストグラム

    次に、読者数もグラフにしてみましょう。

    はい、ドン!

    実は読者10人に満たないブログがほとんどなんですね。

    また、計算をすると、

    • 100人以上 : 15%
    • 200人以上 : 5.6%
    • 300人以上 : 3%
    • 1000人以上 : 0.4%

    となりました。ナルホドナルホド…自分がどのくらいの位置にいるか気になる方は、是非調べてみてください。

    継続期間と読者数

    最後に、「継続は力なり」を可視化していきます。

    下のグラフは、継続期間と平均読者数をプロットしたものです。 縦軸が継続期間、横軸が平均読者数です。

    どうです?きれいな直線になっていることがわかりますね。

    赤の線が、継続期間30ヶ月までで線形回帰をした直線になっています。*1係数を調べると、

    読者数 = 3.5×継続期間+25

    となっています。つまり、コツコツと続けると、平均的に3.5人読者が伸びていくことを示しています。まさに「継続は力なり」ですね!

    どうやって調べたのか

    調べ方が気になる方もいると思います。 その方々のために、どういう方法で調べたかを簡単に書いておきます。

    ブログリストを作る

    分析にあたり、まずはたくさんのブログをかき集める必要があります。 らくからちゃさん(id:lacucaracha)のコチラの記事に載っているアルゴリズムを参考にしました。

    読者登録数ランキング ・改 - ゆとりずむ

  • 適当なブログの読者一覧ページ(hoge/about)から読者IDを取得*2
  • 取得したIDを使って、プロフィール情報からブログのURLを取得
  • これらを繰り返す
  • 超ざっくり言うとこんな取り方をしてる。詳しくはgithubに乗せておくので、どうぞ。(get_hatena_blogger.py)

    github.com

    完全に全てのブログを網羅できるわけではありませんが、傾向を分析したいので、今回は2万件程度にしておきます。

    デフォルトでは1000件のブログデータが集まるようになっていますが、増やすことも可。使い方が分からない人は適当に聞いてください。

    色々と必要なデータを取る

    さっきの集めたリストを使って、いろんなデータをとって行きましょう。(ΦωΦ)フフフ…

    ここで取ったデータは、

    • メインブログURL
    • ブログ開始時期
    • 記事の数
    • ブログ継続期間
    • 読者数

    です。今回は生存率に一番関わってくる、継続期間をどうやって出したのか、抽出アルゴリズムを簡単に説明します。

    継続期間を抽出する

    ブロガーの状態を4つの状態に分けています。

    1. Yet まだ始める前の状態。ブロガーになる前
    2. Active ブログを頑張っている状態
    3. Pause 活動休止中。まだ間に合う
    4. Dead 死亡。

    アルゴリズムはコチラ

  • 2012年1月から、月別アーカイブから記事数を取り出す
  • 記事が初めて0になったところからStart
  • 1ヶ月の記事数が0なら、`Active`->`Pause`
  • 3ヶ月以内にまた記事を書けば `Pause`->`Active`
  • 4ヶ月以上記事数が0なら `Pause`->`Dead`
  • StartからDeadまで(死んでいなかったら現在まで)の期間を抽出する
  • 生き続ける最低条件は3ヶ月に一回は書くということです。 皆さんは生きられていますか?

    分析する

    集めたデータをpandasを使って分析します。 詳しくはGith(略)

    まとめ

    いかがでしたか? やっぱり続けるのって難しいし、続けていくことで成果は着実にでることがデータからわかりましたね。

    またなんか面白いネタ思いついたら記事にします!

    私はとりあえず1年を目指しますっ、ではでは!

    *1:それ以上はサンプルが少なくてばらついているため、省きました。

    *2:maxで100人

    大学の数学/物理を無料で学べるおすすめサイト・サービス6選

    高校生のほけきよ少年にとって、得られる大学以上の物理や数学の情報はwebサイトだけでした。

    物理や数学の専門書って高いんですよね。あと、大きな本屋じゃないと取り扱っていない。 今ではamazonでいろいろな書籍が手に入るようになりましたが、高いしどんな内容がかかれているかは分からないので、買うのもためらわれます。

    そこで今日は

    • 好奇心溢れる高校生
    • お金はない、単位が危ない、やる気に溢れた大学生
    • 社会人になってから物理や数学を趣味で始めたい

    たちのために、無料で大学以上の内容を学べるサイト/サービスを紹介します!

    ※ここでいう数学は「物理学のための数学」の範疇を超えません。

    1. 物理のかぎしっぽ

    hooktail.sub.jp

    物理学に興味を持った人は、一度は目にしたことがあるでしょう。そのくらい有名なサイト。 物理の内容を調べると、このサイトにぶつかることが多いです。

    変分法」で、Wikipediaを抜いて検索順位一位って、すごくない?つよい。*1

    このサイトは、複数の執筆者が共同で運営しています。そのため、バックグラウンドが多様で扱う内容も様々。しかもみんなわかりやすい。 幅広い内容を眺めることが出来るので、勉強に加えて、物理の専門分野に悩んでいる人などもオススメ

    2. EMANの物理学

    こちらも同様に超有名サイト。

    EMANの物理学

    物理のかぎしっぽがある種色んな人によるコラム的に書かれたサイトであるならば、こちらは一人で運営しているサイトなので、書籍のように体系だった知識が得られる本。書籍のレベルの内容が無料で手に入るのは、本当にすごい。まあ、書籍になったんですけど。

    趣味で物理学

    趣味で物理学

    量子論、相対論などは、体系立った本は平気で3000円-4000円とかするので、このサイトで勉強するのもアリだと思います!

    3. MITの物理学講義(Youtube)

    www.youtube.com

    もともと"iTunes U"で無料で見られたMITの物理学講義*2。噂が噂を呼び、いつの間にか書籍化までされていました。

    これが物理学だ! マサチューセッツ工科大学「感動」講義

    これが物理学だ! マサチューセッツ工科大学「感動」講義

    授業はもちろん英語ですが、この人の素晴らしいところは、物理を生々しく講義する所。 自らが体を張って物理学というものを講義していきます。

    「英語がわからない、物理はもっとわからない」って人でも、一度は見て欲しい。きっと物理に鳥肌が立ち、見る前よりも確実に興味が湧くと思います!

    4. 現代数学観光ツアー 物理のための解析学探訪

    phasetr.com

    相転移Pという人が運営しているメルマガです。ニコ動やtwitterでも活動していて、その界隈ではとても有名です。 東大の数学科の修士ということもあり、数学の知識が深い。

    学部までは物理を学んでいたこともあり、その両方の架け橋的なメールマガジンの内容です。しかし、きちんと数学を教えるスタンスは崩さず、抽象的な集合論の話までしっかりと説明されています。

    メールマガジンに登録すると、まずはじめにどういう話をするかの概略を送ってくれるので、それを見ながら判断してみてもいいのではないでしょうか。また、Kindle Unlimitedでも一気に読むことが出来るようになりました。

    5. 数学:物理を学び楽しむために

    田崎晴明 数学:物理を学び楽しむために

    著名な物理学者、田崎晴明さんのサイト。この人、研究はもちろんのこと、物理を学ぶ人たちへの説明のわかりやすさが他の物理学者の追随を許さないほど、上手です。熱力学・統計力学を学ぶものはこの本を一度は目にしたことがあるのではないでしょうか。ない人は買いましょう。マジで名著です。

    熱力学―現代的な視点から (新物理学シリーズ)

    熱力学―現代的な視点から (新物理学シリーズ)

    統計力学〈1〉 (新物理学シリーズ)

    統計力学〈2〉 (新物理学シリーズ)

    その田崎氏が、無料で公開しているのが上記のサイト。なんと650ページ超。 さらに、今でも定期的に整備している

    なんと言っても説明の丁寧さがすごい。間違いなく、しかし具体的なイメージを持って学ぶことができます。

    正直、変な参考書を買うんだったら、このpdfを読み込めばいいよ…と思うほど素晴らしいです。世にある参考書を駆逐できるレベル。

    6. 高校数学の美しい物語

    mathtrain.jp

    「大学の数学なのに、高校数学やんけ」

    と思う方もいるでしょう。このサイト、大学以上の内容も結構扱っています。 サイトのレイアウトも見やすく、内容がスッと頭に入ってくる。

    レベル別にまとめられているので、数学がニガテで高校の内容からやり直したい!という方にも超オススメです!

    大学以上の内容から扱いたいひとはコチラからどうぞ

    大学数学レベルの記事一覧

    まとめ

    数学/物理学を学びたい皆さん、是非これらのサイトで学んでみてはいかがでしょうか。

    物理や数学を学ぶと、色々なことが考えられるようになります。科学は実に面白いですよ!

    紹介するにあたって久しぶりに見たら、いろいろと書籍化されててすごい...! どれもオススメなので、是非是非!ではではっ

    *1:変分法」私も何度も調べてます…

    *2:ちなみに、iTunes Uは意外と充実していて、Stanford Univ. の物理学講義(Leonard Susskind)なんかは、かなり高度な物理学も学べて良い。

    Windows10でpythonを使う環境を作る①:AnacondaとPycharm

    Windowsでちょっとしたpython環境をつくる

    現在の環境

    最近Desktopを買い替えて、諸般の事情でOSはWindows10を入れてみました。
    ここ数年はプライベートでwindowsを使っていなくて、 *1 前に使ったwindowsVistaとかだったので、玉手箱状態である。

    普段はubuntuでコードを書いたりしているのだが、numpyとかmatplotlibとかが使えればいいくらいのときに わざわざubuntuじゃなくてもよいかなーと思って、Windowsでも軽くpythonのコードが書けるように環境を作ってみました

    ワンチャンpythonはいってるのでは??

    Windows10だし、なんかbashが使えるようになったらしいし、ナウいからpythonくらい入っているのではないか?? と思い、とりあえずpowershelというものを開きpythonと打ち込んでみた

    Oh, windows

    実際に環境を構築する

    環境を構築すると言っても、やることはたったの2つ

    • Anacondaをインストー
    • Pycharm(エディタ)をインストー

    今回は、これらの手順を紹介します!

    Anacondaのインストー

    調べていると、

    windowspythonはnumpyやscipyを入れる時にコケることが多い”

    とのこと。 情弱の私はコケるの嫌なので。そういうpythonで使う便利なモジュールがまるっと入っているAnacondaを使ってpythonをインストールします。

    • AnacondaのHPの、windows用ページに行く。ダウンロードページ
    • 自分のPCに合わせて3系をインストール(今回は64bit。結構ファイルサイズが大きいので注意)

    ※2GBくらいあるので容量のチェックをしておきましょう。

    インストールして、基本的にはNextを押していけばOK

    一つだけ、Advanced Optionsで以下の2つにチェックが入っていることだけ確認しておいてください!

    きちんとインストールlできていると、powershellを開いたときにこうなります

    numpy, scipy, matplotlib, pandasなど、

    よく使うモジュールがデフォルトで入っているので楽です!

    これでpythonが使えるようになりました。

    Pycharmのインストー

    次はエディタ

    Vim!!!!11Emacsコロス』とか『emacs!!!!!!Vimは滅ぶべき』

    とか、特にこだわりがなければ最強のpython IDEであるPycharmを平和的に使うのがいいと思います。

    まず、Pycharmのページに行きます。

    今回はWindowsなので、WindowsCommunity Downloadをクリック。

    ダウンロードが完了したらインストールに進みます

    • pycharm-community~~.exeを開き、基本Nextボタンをポチる

    ただ、↓だけ注意。create associationsの.pyにチェックを入れましょう

    インストールが終わったら実行

    ここでもともと使ってた設定ファイルなどがない場合はそのまま↓画像のボタンを押します

    始めに色の設定などをしてくれる。
    個人的なおすすめはDarcula。かなりクールな色合いです。

    PycharmとAnacondaをつなぐ

    ここまできたら最後の仕上げ。PycharmでAnacondaを動かせるように設定します。

    • Pycharmを再び起動させ、create new projectを押す

    • New Project画面で、locationのところにフォルダ名を書く。ここではtestとしている。
    • Interpreterのところに、Anaconda3/python.exeがある場所を入力

    わからなかったらwindowsの検索で『Anaconda3』と打つとOK。そしてCreateボタンを押す。

    • 新しくpythonファイルを作る。
      • 左上のFileからNewを選択し,Python Fileをクリック

    ↓画像のようにして、新しいファイルを作ります。(ここで名前はhello.py)

    Pycharmのいいところは、何もしなくても補完がしっかりしているところ。
    試しに printを打とうとすると、こうやってすぐに補完してくれる。すごい。

    コードが書き終わったら、Shift+Ctrl+F10キーを押すことで、実行できる。

    Pythonを動かしてみる

    さあ、これでPythonを使うことが出来るようになりました! 早速、コードを動かしてみましょう。

    本ブログには、コード記載の記事がたくさんあります。 この方法で環境を整えたのであれば、ほとんどが問題なく動くと思います!!

    とりあえずコピペからでも、試してみてください!

    Gitの設定はしておきたい

    細かい設定しなくても、これだけでちょちょっとコードを書くくらいには十分すぎる環境が整います ただ、Gitの登録だけはしておいた方がいいです。Gitについては下の関連記事を参照ください!

    意外と簡単だった。さあ、プログラミングを始めましょう!

    *1:だからと言ってプログラミングをしていたわけではない。Macでスタバドヤしたかっただけ

    Python/Javascriptを始めたい方に最適な『CheckIO』の遊び方!

    こんにちは!ほけきよです。

    データ分析業務では、プログラムを書くことが多いです。特に最近はpythonpythonは覚えやすく使いやすい初学者にはとてもいい言語だと思います。

    しかし、それにも関わらず自分の実装力の無さに絶望する日々です。

    どうやってコーディング力をつければ良いのか、相談した所、先輩に『CheckIO』というのを紹介してもらいました。今日はその紹介と、最初の遊び方について説明します!

    • 15秒でわかるCheckIO
    • 始め方
      • HPに行き、登録をする
      • 登録が済むと初期画面へ
    • 課題を始める
    • 課題を見てみる
    • 解き始める
      • def CheckIO(data):
      • assert
    • まとめ
    続きを読む

    【コードで一発】ブログ最適化/SEO対策で面倒なことは全てPythonにやらせよう

    こんにちは、ほけきよです。

    ここ数回に分けて書いた『面倒がすぎる内容をpythonにさせよう』シリーズの集大成。

    ブログ最適化のために必要なものを『全て』『一気に』抜き出すプログラム、作りました!

    この記事を読めば、下記の情報がゲットできます

    ※ 現在ははてなブログのみ対応となっています。WordPress用にも作成中なので、少々お待ちを。 *1

    ・記事とURLとブックマークの情報
    ・記事内画像を全て抜きとったもの
    ・自サイトの内部リンクがどうなっているかを可視化したもの
    ・リンク切れリスト
    はてなブックマークがどのような伸び方でついたかを可視化したもの

    使い方(情報技術に明るい人)

    情報技術に明るい人と、そうでない人向けに使い方を分けて書きます。

    githubからtaikutsu_blog_worksリポジトリをcloneしてきて、all_in_one.py実行してください。

    git clone https://github.com/hokekiyoo/taikutsu_blog_works.git

    requirement、引数等はREADMEに書いています。 *2 *3

    この説明で???という方は次章からの説明をどうぞ

    使い方(一般向け)

    環境構築

    MacLinuxユーザの方は、探せばいくらでもpython3の導入説明記事があると思います。

    Windowsで動かしたい!という方には、私の過去記事を参考に環境構築してみてください。他にも方法はありますが、この方法が一番手っ取り早いと思います。(容量が2GBほど必要であることにご注意ください。)

    実行するコード

    下記コードをコピペしてall_in_one.pyなどで保存してください。

    コードをみる
    from argparse import ArgumentParser
    from urllib import request 
    from urllib import error
    from bs4 import BeautifulSoup
    import os
    import csv
    import json
    import datetime
    import time
    import matplotlib.pyplot as plt
    
    def extract_urls(args):
        page = 1
        is_articles = True
        urls = []
        while is_articles:
            try:
                html = request.urlopen("{}/archive?page={}".format(args.url, page))
            except error.HTTPError as e: 
                # HTTPレスポンスのステータスコードが404, 403, 401などの例外処理
                print(e.reason)
                break
            except error.URLError as e: 
                # アクセスしようとしたurlが無効なときの例外処理
                print(e.reason)
                break
            soup = BeautifulSoup(html, "html.parser")
            articles = soup.find_all("a", class_="entry-title-link")
            for article in articles:
                urls.append(article.get("href"))
            if len(articles) == 0:
                # articleがなくなったら終了
                is_articles = False
            page += 1
        return urls
    
    def make_directories(args):
        directory = args.directory
        if not os.path.exists(directory):
            os.mkdir(directory)
        if args.image:
            if not os.path.exists(directory+"/imgs"):
                os.mkdir(directory+"/imgs")
        if args.graph:
            if not os.path.exists(directory+"/graph"):
                os.mkdir(directory+"/graph")
        if args.hatebu:
            if not os.path.exists(directory+"/hatebu"):
                os.mkdir(directory+"/hatebu")
    
    def articles_to_img(args, url, soup, name):
        """
        各記事内の画像を保存
        - gif, jpg, jpeg, png
        - 記事ごとにフォルダ分けして保存される
        - imgs/{urlの最後の部分}/{0-99}.png
        """
        # ディレクトリの作成
        article_dir = os.path.join(args.directory+"/imgs", name)
        if not os.path.exists(article_dir):
            os.mkdir(article_dir)
        entry = soup.select(".entry-content")[0]
        imgs = entry.find_all("img")
        count=0
        
        
        for img in imgs:
            filename = img.get("src")
            if "ssl-images-amazon" in filename:
                # print("amazon img")
                continue
            # 拡張子チェック
            if filename[-4:] == ".jpg" or filename[-4:] == ".png" or filename[-4:] == ".gif":
                extension = filename[-4:]
                print("\t IMAGE:",filename)
            elif filename[-5:] == ".jpeg":
                extension = filename[-5:]
                print("\t IMAGE:",filename,extension)
            else: 
                continue
            try:
                image_file = request.urlopen(filename)
            except error.HTTPError as e: 
                print("\t HTTPERROR:", e.reason)
                continue
            except error.URLError as e: 
                print("\t URLERROR:", e.reson)
                continue
            # ValueErrorになった場合に試す(httpで始まらないリンクも貼れるっぽい?)
            except ValueError:
                http_file = "http:"+filename
                try: 
                    image_file = request.urlopen(http_file)
                except error.HTTPError as e: 
                    print("\t HTTPERROR:", e.reason)
                    continue
                except error.URLError as e: 
                    print("\t URLERROR:", e.reason)
                    continue
            # 画像ファイルの保存
            with open(os.path.join(article_dir,str(count)+extension), "wb") as f:
                f.write(image_file.read())
            count+=1
    
    def make_network(G, args, url, urls, soup):
        entry_url = args.url + "/entry/"
        article_name = url.replace(entry_url,"").replace("/","-")
        entry = soup.select(".entry-content")[0]
        links = entry.find_all("a")
        for link in links:
            l = link.get("href")
            if l in urls:
                linked_article_name = l.replace(entry_url,"").replace("/","-")
                print("\t NETWORK: 被リンク!{} -> {}".format(article_name, linked_article_name))
                j = urls.index(l)
                G.add_edge(article_name, linked_article_name)
            else: 
                continue
    
    def url_checker(url, urls):
        #変なリンクは除去したい
        flag1 = "http" in url[:5]     
        #ハテナのキーワードのリンクはいらない
        flag2 = "d.hatena.ne.jp/keyword/" not in url    
        #amazonリンクはダメ
        flag3 = "www.amazon.co.jp" not in url and "http://amzn.to/" not in url
        #rakutenリンクはダメ
        flag4 = "rakuten.co.jp" not in url
        #もしももダメ
        flag5 = "af.moshimo" not in url
        return flag1 and flag2 and flag3 and flag4 and flag5
    
    
    def check_invalid_link(args, urls, url, soup, writer):
        import re
        from urllib.parse import quote_plus
        regex = r'[^\x00-\x7F]' #正規表現    
        entry_url = args.url + "/entry/"
        entry = soup.select(".entry-content")[0]
        links = entry.find_all("a")
        for link in links:
            l = link.get("href")
            if l == None:
                continue
            #日本語リンクは変換
            matchedList = re.findall(regex,l)
            for m in matchedList:
                l = l.replace(m, quote_plus(m, encoding="utf-8"))
            check = url_checker(l, urls)
            if check:
                #リンク切れ検証
                try:
                    html = request.urlopen(l)
                except error.HTTPError as e:
                    print("\t HTTPError:", l, e.reason)
                    if e.reason != "Forbidden":
                        writer.writerow([url,  e.reason, l])
                except error.URLError as e: 
                    writer.writerow([url, e.reason, l])                        
                    print("\t URLError:", l, e.reason)
                except TimeoutError as e:
                    print("\t TimeoutError:",l, e)
                except UnicodeEncodeError as e:
                    print("\t UnicodeEncodeError:", l, e.reason)
    
    def get_timestamps(args, url, name):
        """
        はてブのタイムスタンプを取得
        """ 
        plt.figure()
        data = request.urlopen("http://b.hatena.ne.jp/entry/json/{}".format(url)).read().decode("utf-8")
        info = json.loads(data.strip('(').rstrip(')'), "r")
        timestamps = list()
        if info != None and "bookmarks" in info.keys(): # 公開ブックマークが存在する時に、それらの情報を抽出
            bookmarks=info["bookmarks"]
            title = info["title"]
            for bookmark in bookmarks:
                timestamp = datetime.datetime.strptime(bookmark["timestamp"],'%Y/%m/%d %H:%M:%S')
                timestamps.append(timestamp)
            timestamps = list(reversed(timestamps)) # ブックマークされた時間を保存しておく
        count = len(timestamps) 
        number = range(count)
        if(count!=0):
            first = timestamps[0]
            plt.plot(timestamps,number,"-o",lw=3,color="#444444")
            # 3時間で3
            plt.axvspan(first,first+datetime.timedelta(hours=3),alpha=0.1,color="blue")
            plt.plot([first,first+datetime.timedelta(days=2)],[3,3],"--",alpha=0.9,color="blue",label="new entry")
            # 12時間で15
            plt.axvspan(first+datetime.timedelta(hours=3),first+datetime.timedelta(hours=12),alpha=0.1,color="green")
            plt.plot([first,first+datetime.timedelta(days=2)],[15,15],"--",alpha=0.9, color="green",label="popular entry")
            # ホッテントリ
            plt.plot([first,first+datetime.timedelta(days=2)],[15,15],"--",alpha=0.7, color="red",label="hotentry")        
            plt.xlim(first,first+datetime.timedelta(days=2))
            plt.title(name)
            plt.xlabel("First Hatebu : {}".format(first))
            plt.legend(loc=4)
            plt.savefig(args.directory+"/hatebu/{}.png".format(name))
            plt.close()
    
    def graph_visualize(G, args):
        import networkx as nx
        import numpy as np
        # グラフ形式を選択。ここではスプリングモデルでやってみる
        pos = nx.spring_layout(G)
        # グラフ描画。 オプションでノードのラベル付きにしている
        plt.figure()
        nx.draw(G, pos, with_labels=False, alpha=0.4,font_size=0.0,node_size=10) 
        plt.savefig(args.directory+"/graph/graph.png")
        nx.write_gml(G, args.directory+"/graph/graph.gml")
        # 次数分布描画
        plt.figure() 
        degree_sequence=sorted(nx.degree(G).values(),reverse=True) 
        dmax=max(degree_sequence) 
        dmin =min(degree_sequence)
        kukan=range(0,dmax+2) 
        hist, kukan=np.histogram(degree_sequence,kukan)
        plt.plot(hist,"o-")
        plt.xlabel('degree') 
        plt.ylabel('frequency')
        plt.grid()
        plt.savefig(args.directory+'/graph/degree_hist.png') 
        
    def main():
        parser = ArgumentParser()
        parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
        parser.add_argument("-d", "--directory", type=str, required=True,help="output directory")
        parser.add_argument("-i", "--image", action="store_true", default=False, help="extract image file from articles")
        parser.add_argument("-g", "--graph", action="store_true", default=False, help="visualize internal link network")
        parser.add_argument("-l", "--invalid_url", action="store_true", default=False, help="detect invalid links")
        parser.add_argument("-b", "--hatebu", action="store_true", default=False, help="visualize analyzed hatebu graph")   
        args = parser.parse_args()
    
        urls = extract_urls(args)
        # 保存用ディレクトリ作成
        make_directories(args)
        # 記事リストを作る
        with open (args.directory+"/articles_list.csv", "w") as f:
            writer = csv.writer(f, lineterminator='\n')
            writer.writerow(["Article TITLE", "URL","Hatebu COUNT"])
            if args.invalid_url:
                f = open(args.directory+'/invalid_url_list.csv', 'w')
                writer_invalid = csv.writer(f, lineterminator='\n')
                writer_invalid.writerow(["Article URL", "ERROR", "LINK"])        
            if args.graph:
                import networkx as nx
                G = nx.Graph()
                for i, url in enumerate(urls):
                    name = url.replace(args.url+"/entry/","").replace("/","-")
                    G.add_node(name)
            for i, url in enumerate(urls):
                name = url.replace(args.url+"/entry/","").replace("/","-")
                print("{}/{}".format(i+1,len(urls)), name)
                # 抽出したurlに対して各処理実行
                try:
                    html = request.urlopen(url)
                except error.HTTPError as e: 
                    print(e.reason)
                except error.URLError as e: 
                    print(e.reason)
                soup = BeautifulSoup(html, "html.parser")
                # WordPressならいらない
                data = request.urlopen("http://b.hatena.ne.jp/entry/json/{}".format(url)).read().decode("utf-8")        
                info = json.loads(data.strip('(').rstrip(')'), "r")
                try:
                    count = info["count"]
                except TypeError:
                    count = 0
                # 記事の名前とurl、はてブを出力
                try:
                    writer.writerow([soup.title.text, url, count])
                except UnicodeEncodeError as e:
                    # ふざけた文字が入ってる場合はエラー吐くことも
                    print(e.reason)
                    print("\tArticleWriteWarning この記事のタイトルに良くない文字が入ってます :",url)
                    continue
                if args.image:
                    if "%" in name:
                        name = str(i) #日本語対応
                    articles_to_img(args, url, soup, name)
                if args.graph:
                    make_network(G, args, url, urls, soup)
                if args.invalid_url:
                    check_invalid_link(args, urls, url, soup, writer_invalid)
                if args.hatebu:
                    if "%" in name:
                        name = str(i) #日本語対応
                    get_timestamps(args, url, name)
                time.sleep(3)
            if args.invalid_url:
                f.close()
            if args.graph:
                graph_visualize(G, args)
    
    
    if __name__ == '__main__':
        main()
    

    コマンドプロンプトやターミナルを動かしたことがない人は、デスクトップ上に保存しておくと、後々の説明でハマりづらいと思います。

    または、githubページに飛び、下記画像に従いファイル一覧をDLしてください。

    コマンド一発!実行する

    コードの実行方法です。 コマンドプロンプト*4かターミナルを開き、all_in_one.pyを保存したフォルダまで行きます。デスクトップに保存している方は、下記コマンドを入力ください。

    cd Desktop

    デフォルトだとタイトル/URL/はてブcsvファイルだけですが、オプションの付け方でできることを選べるようにしています。詳しくはgithubページをお読みください。また、ここに使用上の注意点もかいていますので、一度読むと良いと思います。

    全部使いたいときは、下記のように入力してください。

    python all_in_one.py --url http://procrasist.com --directory procrasist --graph --image --hatebu --invalid_link

    または

    python all_in_one.py -u http://procrasist.com -d procrasist -g -i -b -l

    この一発で、あとはきちんと動くはずです。(--url--directoryの後ろは自分が調べたいサイトURLを入れてください)

    中身がどうなっているか

    引数とできることをまとめたのがコチラ。必要ないなと思う情報は上記コマンドのオプションから消してください!

    引数 意味
    -u, –url 調べたいサイトのurl
    -d, –directory 結果を保存するフォルダ名
    -i, –image 記事内画像を抜き出す
    -g, –graph 内部リンクの解析結果を表示する
    -l, –invalid_url リンク切れを調べる
    -b, –hatebu はてブの付き方の初動解析

    なお、各要素技術はすでに過去記事にて説明しているので、気になる方はそちらをどうぞ

    内部ネットワークについては、新たにグラフの次数分布を出せるようにしました(下記画像)。次数というのは、どのくらい他の記事と結びついているか(リンクの数)です。縦軸は記事数です。どの程度浮いた記事があるかを把握するのにお使いください。

    この機能を応用すると、次のような可視化も可能です。yowaiが私です。。。強くしたい。。。

    また、余談ですが、下記記事のsearch consoleの解析もpythonで行っています

    pandasを使えばこういう表情報の抜き出しも簡単にできるので、興味のある方は是非。

    注意

    下記簡単に注意事項です。

    • リンク切れのチェックはまだ動作が不安定でエラーが出ることも多いかもしれません。
      • 楽天のリンクが多い人はチェックに時間がかかると思います。
    • リンクに対する日本語対応はまだ不完全です。リンクに日本語が含まれる場合は、フォルダ名等通し番号になります。

    ※注意事項追記(7/9)

    • 楽天アフィリエイト等はこのリンク切れ検査実行時に、クリック数をカウントしてしまうため、もしかするといつもより多くアクセスするために不正クリックとみなされる恐れがあります楽天等アフィリンクだけ別にアクセスするようコード修正予定ですが、現状使用の際は自己責任でお使いくださいませ。
    • 楽天/もしもからのリンクは取らないようにコードを修正しました。オプション付加は今後対応いたします。

    出力結果

    実行後は下記のフォルダ構成で出力されます。(procrasistで調べた場合)

    ├─all_in_one.py             実行するコード
    └─procrasist                
        ├─articles.csv          記事一覧
        ├─invalid_url_list.csv  リンク切れ
        ├─graph                 内部ネットワーク
        ├─hatebu                はてブ解析
        └─imgs                  画像

    また、出力ログは次のようにしています。

    pythonを勉強したい方に

    正直、スクールに行ったりするよりも、

    • 何をやりたいかを明確にする
    • 自分で手を動かしながら知り合いに聞きまくる

    ほうが伸びると思います。

    今回はブログ最適化という目的があったので、私は色々と勉強することができました。

    正直Qiitaとかネット上に解説が溢れているので、適宜調べながらでOKだとは思います。 ただ、何に使うかという目的ベースから書かれているこの本は少し気になってます。

    この記事はこの本タイトルにinspiredされて書いたものです。 時間があれば読んでみようかなと思っています。

    私が書いてるコードは初心者なので未熟ですが、 単純なものの組み合わせでもこのくらいはできるようになるので、是非チャレンジしてみてください!

    まとめ

    ブログ最適化って、本当に大事なのはネットワークの構成を考えたり、記事内容をリライトしたり ってところですよね。それ以前の大変面倒な単純作業は人間がするようなところではありません。

    頭の使わない退屈なことはpythonにやらせて、余った時間で有意義な最適化作業をしましょう。ではではっ!

    いくつかのブログでチェックはしましたが、まだエラーが出ると思います。 エラー起きたり、こんな機能が欲しい!というのがあれば教えてね!機能は随時追加します!
    準備は整った!!いよいよ最適化作業へとうつr( ˘ω˘ ) スヤァ…

    *1: 7/10 WP版にも対応しました!下記リンクのコードをコピペし、all_in_one.pyをall_in_one_wp.pyとしてお使いください! WP用ツール

    *2:コードが長くなっちゃったので分けようかと思ったのですが、記事にする都合全て一箇所にまとめています

    *3:日曜大工程度なので、pull-requestやissue等お待ちしております!

    *4:windowsボタン+cmd+Enterで開きます。

    自サイトの記事のリンク切れを自動で抽出するアレを作った

    こんにちは!ほけきよです。

    今日は自動化第四弾!! またまたGoogleに媚びを売るSEO対策です(^^)

    前回は内部リンクネットワークの可視化でした。今回は、リンク切れの抽出です!

    悲しきNot FoundGoogleさんがお怒りになる前に片付けちゃいましょう!

    リンク切れとは?

    自サイトで言うと、こういうのです

    悲しいですよね。以前まであったはずのものがなくなるのは…

    というわけで、リンク切れは早めに対処しましょう

    めんどい

    そう思い、いちいちリンククリックして確かめてたんですが、

    めんどい、めんどうがすぎる

    大体のリンクはちゃんと通るのに、通らないリンクを探すなんて、修行ですか? というわけで、3記事で飽きました。ううーん、そうだ、自動化しよう

    環境を作る

    python環境のある人へ : pipで全部入ります。面倒な人は、anacondaに全部入っているはずなのでそれで。

    python環境のないwinユーザへ : python環境をwindowsにいれる方法は、私の過去記事に書いています。anacondaを入れておくと大抵のことができるのではかどります(容量が2GBほど必要ということには注意しておいてください)

    コード

    例の如く下記コードをコピペしてinvalid_links.pyなどで保存してください *1

    どこでもいいのですがあんまりわからない!って人はデスクトップに一旦保存しておきましょう。

    コードをみる
    from bs4 import BeautifulSoup
    import urllib
    from urllib import request
    import time
    from argparse import ArgumentParser
    import csv
    
    
    def extract_url(root_url):
        page = 1
        is_articles = True
        urls = []
        
        while is_articles:
            html = request.urlopen(root_url+"/archive?page={}".format(page))
            soup = BeautifulSoup(html, "html.parser")
            articles = soup.find_all("a",class_="entry-title-link")
            for article in articles:
                urls.append(article.get("href"))
            if len(articles) == 0:
                # articleがなくなったら終了
                is_articles = False
            page += 1
        return urls
    
    def url_checker(url, urls):
        #変なリンクは除去したい
        flag1 = "http" in url[:5]     
        #ハテナのキーワードのリンクはいらない
        flag2 = "d.hatena.ne.jp/keyword/" not in url    
        #amazonリンクはダメ
        flag3 = "http://www.amazon.co.jp" not in url and "http://amzn.to/" not in url
        return flag1 and flag2 and flag3
    
    def check_invalid_link(root_url, urls, output):
        import re
        regex = r'[^\x00-\x7F]' #正規表現    
        entry_url = root_url + "/entry/"
        with open (output, "w") as f:
            writer = csv.writer(f, lineterminator='\n')
            writer.writerow(["URL", "ERROR", "LINK", "STATUS"])
            for i,url in enumerate(urls):
                print(i+1,"/",len(urls),url)
                try:
                    html = request.urlopen(url)
                except urllib.error.HTTPError as e: 
                    print(e.reason)
                except urllib.error.URLError as e: 
                    print(e.reason)
                soup = BeautifulSoup(html, "html.parser")
                entry = soup.select(".entry-content")[0]
                links = entry.find_all("a")
                for link in links:
                    l = link.get("href")
                    #日本語リンクは変換
                    matchedList = re.findall(regex,l)
                    for m in matchedList:
                        l = l.replace(m, urllib.parse.quote_plus(m, encoding="utf-8"))
                    check = url_checker(l, urls)
                    if check:
                        #リンク切れ検証
                        try:
                            html = request.urlopen(l)
                        except urllib.error.HTTPError as e: 
                            writer.writerow([url, "HTTP ERROR", l, e.reason])
                            print("HTTPError:", l, e.reason)
                        except urllib.error.URLError as e: 
                            writer.writerow([url, "URL ERROR", l, e.reason])                        
                            print("URLError:", l, e.reason)
                        except UnicodeEncodeError as e:
                            writer.writerow([url, "UNICODE ENCODE ERROR", l, e.reason])
                            print("UnicodeEncodeError:", l, e.reason)                        
                # time.sleep()
    
    if __name__ == '__main__':
        """
        TODO
        - APIを叩いてなんとかなるやつ実装
            - amazon associateの対応
            - youtubeの削除された動画対応
        """
        parser = ArgumentParser()
        parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
        parser.add_argument("-o", "--output", type=str, default="articles.csv", help="output csv name")
        args = parser.parse_args()
        urls = extract_url(args.url)
        check_invalid_link(args.url, urls, args.output)
    

    なにをやっているかだけ簡単に説明しておきます。

    • 本文中からhttpで始まるリンクだけ抜き出している
    • ハテナのキーワードリンクは邪魔なので排除する
    • amazon associateリンクは特殊なことをしないとアクセスできないので除去する
    • リンクにアクセスして、エラーが出たら出力する

    という流れになります。

    実行する

    ターミナル or コマンドプロンプトを開き*2、さっきのinvalid_links.pyが保存されている場所まで移動します。 デスクトップに保存している方は下記コマンドで実行されるはずです。

    cd Desktop
    python invalid_links.py --url http://www.procrasist.com --output procrasist.csv
    • --url URLで、調べたいブログのURLを入力してください
    • --output hoge.csv で、出力されるcsvの名前を決められます(省略できて、省略した場合はarticles.csvになります)

    結果はこんな感じ。

    最新記事から順番にびゃぁーーっと調べてくれます。

    なお、結果はcsvファイルにも出力されます。 結構リンク切れってあるのね。

    今後の課題/注意

    下記のことはまだできていません

    • amazon商品リンクのリンク切れ
    • youtubeの削除されたものの追従
    • はてなブログのみに対応
    • ブラウザでのみアクセスを許しているものがあり、拒否(Forbidden)されちゃう。
    • 日本語以外のエンコードエラーに非対応(いずれ直します)

    いずれもAPIでうまくすればうまくいくっぽいですが、ちょっと面倒だったので、今回はやめておきます。TODO! はてなブログ以外で試したいという方は、相談していただければ応えられる範囲で答えます!URLをぶっこ抜くところがおそらく一番のポイントです

    あと、これは注意ですが、外部へのアクセスを頻繁に行いすぎると、そのサイト運営者の迷惑になりますので、コード実行は最小限にしておいてください

    ※コードが動かない場合も連絡ください。

    まとめ

    いかがでしたか? コレでいちいち眠たい眼こすりながらリンクポチポチする必要もなくなりましたねっ

    どうぞ試してみてください!

    よし、リンク切れ見つかったし、あとはリンクを直すだけd...( ˘ω˘)スヤァ

    *1:Windowsのメモ帳でコピペする際は、デフォルトでShift-JISとかのふざけた文字コードになっている可能性があるので、UTF-8で保存しましょう。

    *2:windowsユーザーへ。簡単な開き方は、windowsボタン+“cmdと入力”+Enterです

    SEO対策!自分のブログの内部リンクを自動で可視化する

    こんにちは!ほけきよです。

    今日は自動化第三弾!! SEO対策ってしてますか? ブログの検索流入をあげるために、Googleには媚を売っていかなければいけないじゃないですか~。

    そんなわけで、前回は表示スピード向上にむけた画像圧縮のために、全画像をぶっこ抜いてくる方法を紹介しました。

    今日は、SEO対策で大事だと言われる。内部リンクのネットワーク化についてです。

    内部リンクのネットワーク化

    SEO ブログ

    とかで調べるとわんさか出て来るわけですよ。内部リンクの密度、数、アンカーテキスト*1などが大事って。

    なので、記事をほったらかしにするわけじゃなくて、過去記事にもリンクをはりながら、蜘蛛の巣のような構造にしていかなければいけないってわけです。

    めんどい

    というわけで、最適化しようと思うじゃないですか~。

    めんどい、めんどうがすぎる。

    まず、どの記事にリンクが貼られているかわからない。忘れた。 ううーん、面倒なものは自動化しましょう

    環境を作る

    • python(3系)

      • bs4
      • urllib
      • networkx
      • numpy
      • matplotlib
    • python環境のある人へ : pipで全部入ります。面倒な人は、anacondaに全部入っているはずなので

    • python環境のないwinユーザへ : python環境をwindowsにいれる方法は、私の過去記事に書いています。anacondaを入れておくと大抵のことができるのではかどります(容量が2GBほど必要ということには注意しておいてください)

    コード

    下記コードをコピペしてlink_network.pyなどで保存してください。*2 *3

    どこでもいいのですがあんまりわからない!って人はデスクトップに一旦保存しておきましょう。

    コードをみる
    from bs4 import BeautifulSoup
    import urllib
    from urllib import request
    import time
    import csv
    import os
    import numpy as np
    import time
    from argparse import ArgumentParser
    import networkx as nx
    import matplotlib.pyplot as plt
    
    def extract_url(root_url):
        page = 1
        is_articles = True
        urls = []
        entry_url = root_url + "/entry/"
        
        while is_articles:
            html = request.urlopen(root_url+"/archive?page={}".format(page))
            soup = BeautifulSoup(html, "html.parser")
            articles = soup.find_all("a",class_="entry-title-link")
            for article in articles:
                urls.append(article.get("href"))
            if len(articles) == 0:
                # articleがなくなったら終了
                is_articles = False
            page += 1
        return urls
    
    def make_network(root_url, urls):
        entry_url = root_url + "/entry/"
        G = nx.Graph()
        for url in urls:
            article_name= url.replace(entry_url,"").replace("/","-")
            G.add_node(article_name)
        for i,url in enumerate(urls):
            print(i+1,"/",len(urls))
            try:
                html = request.urlopen(url)
            except urllib.error.HTTPError as e: 
                print(e.reason)
            except urllib.error.URLError as e: 
                print(e.reason)
            soup = BeautifulSoup(html, "html.parser")
            entry = soup.select(".entry-content")[0]
            links = entry.find_all("a")
            for link in links:
                l = link.get("href")
                if l in urls:
                    linking_article_name = url.replace(entry_url,"").replace("/","-")
                    linked_article_name = l.replace(entry_url,"").replace("/","-")
                    print("被リンク!{} -> {}".format(linking_article_name, linked_article_name))
                    j = urls.index(l)
                    G.add_edge(linking_article_name, linked_article_name)
                else: 
                    continue
        return G
    
    def visualize(G, savename, savegml):
        pos = nx.spring_layout(G) # グラフ形式を選択。ここではスプリングモデルでやってみる
        nx.draw(G, pos, with_labels=True,alpha=0.3,font_size=0.0,node_size=10) # グラフ描画。 オプションでノードのラベル付きにしている
        plt.savefig(savename+".png")
        plt.show()
        if savegml:
            nx.write_gml(G,savename+".gml")
    
        
    if __name__ == '__main__':
        parser = ArgumentParser()
        parser.add_argument("-u", "--url", type=str, required=True,help="input your url")
        parser.add_argument("-o", "--savename", type=str, default="network", help="output name")
        parser.add_argument('-g', "--savegml", action="store_true", default=False)
        args = parser.parse_args()
        
        urls = extract_url(args.url)
        G = make_network(args.url, urls)
        visualize(G, args.savename, args.savegml)
    

    何をしているかだけ軽く説明すると

    ■ URLをぶちぬいてリスト作る

    ■ 一記事ずつ本文からhrefついてるリンク抜き出す

    ■ リンクのURLがさっきのリストに含まれるものだったらグラフのエッジを張る

    という手順でネットワークを作っていっています。リンクにはamazonリンクなど必要のないものも入っているので、予めURLリストを作っておくことがポイントですね。

    使い方

    ターミナル or コマンドプロンプトを開き*4、さっきのlink_network.pyが保存されている場所まで移動します。 デスクトップに保存している方は下記コマンドで実行されるはずです。

    cd Desktop
    python link_network.py --url http://www.procrasist.com --savename procrasist -g

    一行目はデスクトップに移動しています。二行目の解説をちらっとしておくと

    • --url URLで、調べたいブログのURLを入力してください
    • --savename hoge で、出力されるグラフの名前を決められます(省略できて、省略した場合はgraph.pngになります)
    • -gは後で説明します。無くてもOKです。

    結果は画面に描画されると同時に、pngファイルで保存されるようになっています!

    出力結果

    グラフとして出力されるのがこんな感じです。 ちなみに、比較用としてレコメンタンクを運営しているみるおか氏のリンク構造も載せておきます。

    どちらが私でしょう?一目瞭然ですね。かなしい…(;_;) レコメンタンクの方は、かなり密なネットワーク構造になっていて、しっかり手入れをしているんだなと言うことがわかります。

    どのようにしてそういう密なリンクを張っていくのか、下記記事でその一端に触れることができるので、最適化に興味がある人は是非みてみてはいかがでしょうか。

    もう少し詳しく

    このグラフ、出力でネットワークの粗密はわかるんですが、どうも、どういうカテゴリがクラスタを成しているかまではわかりづらいです。もう少し詳しくみたい!という方に、グラフ描画ソフトとの外部連携をおすすめします。

    『使い方』で紹介した-gオプションで、savename.gmlというファイルが生成されるはずです。これは、グラフ構造を保ったファイルになっていて、これを使えば外部の描画ソフトと連携し、いい感じのグラフがかけるのです!! 有名所は

    なんてのがあります。Gephiはなぜか私の環境じゃうまく動かなかったので、Cytoscapeを使います。

    結果

    グラフ可視化の結果がコチラ

    よりスカスカグラフであることが判明しました!かなしい…(;_;)

    このグラフは結構良くて、拡大するとどういう記事かまでわかるようになっています。

    これは高校野球クラスタっぽいですね。じゃあどこと繋げられるかな~なんて考えることも可能です!

    まとめ

    いかがでしたか? こうやって見ると、強いブログ、弱いブログの差が一目瞭然ですね!

    きちんとお手入れしなきゃなと痛感しました。皆さんも是非試してみてはいかがでしょうか?

    よし、いよいよ最適化作業にうつr...( ˘ω˘)スヤァ

    *1:リンクを貼る前の文言

    *2:なお、今回は面倒だったのでエラーハンドリングしてません。必要な方は過去記事などを参考にどうぞ

    *3:Windowsのメモ帳でコピペする際は、デフォルトでShift-JISとかのふざけた文字コードになっている可能性があるので、UTF-8で保存しましょう。

    *4:windowsユーザーへ。簡単な開き方は、windowsボタン+“cmdと入力”+Enterです

    PROCRASIST