C# で Worpdress の API を使って記事を投稿する

WordpressAPI が備わっていることはこれまでも記事で紹介していますが、今回は具体的に使う場合のコードのメモになります。

API では JSON でデータをやり取りしますので、DynamicJson を使用することにします。NuGet でインストールするかソースコードを取り込んで、 DynamicJson を使用できるようにします。

コードです。まず、送信するデータのもととなる匿名オブジェクトを作成します。

var postContent = new
{
    title = <記事タイトル>,
    content = <記事本文>,
    status = "publish", // 公開は"publish" 下書きは"draft"
    categories = "1,2,3", // カテゴリのIDをカンマ区切り
    tags = "4,5" // タグのIDをカンマ区切り
}

content には、普通に HTML タグを含む投稿の本文を文字列で指定します。

categories や tags などは不要であれば指定しなくてかまいません。
status も指定しなければデフォルトで公開状態になります。これらの他にもパラメタを指定でき、それは こちら で確認できます。

この匿名オブジェクトを JSON 形式の文字列に変換するのに、DynamicJson を使用します。

var serialized = DynamicJson.Serialize(postContent);

あとは送信するだけです。

var postData = Encoding.UTF8.GetBytes(serialized);

var req = HttpWebRequest.Create("<サイトのURL>/wp-json/wp/v2/posts") as HttpWebRequest;
req.Method = "POST";
req.ContentLength = postData.Length;
req.ContentType = "application/json"; // 送信するコンテントタイプを application/json にするのを忘れないこと

using (var reqs = req.GetRequestStream())
{
    reqs.Write(postData, 0, postData.Length);
}

using (var resp = req.GetResponse() as HttpWebResponse)
using (var reader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
{
    var s = reader.ReadToEnd();
}

最後の変数 s には、API のレスポンスが JSON 形式の文字列で格納されています。

これをまた DynamicJson で扱うなら、

var post = DynamicJson.Parse(s);

こうすればOKです。こうすると、例えば投稿IDは post.id で取得できます。何が取得できるかはこちらで確認できます。




Redirect ディレクティブによるリダイレクト

ある URL にアクセスされた場合、それを別の URL に飛ばしたい場合があります。

それは内部的なものと外部的なものに分けられます。

内部的なものは、ある URL にアクセスされたときに、その URL を処理する機能を別の機能にまかせたいケースです。
Wordpress が自動的に .htaccess に追加するリライトの設定などがそれにあたります。
Wordpress の場合は、/foo/bar などのアクセスがあった場合、基本的には index.php に処理が転送されます。
ブラウザのアドレスバーに変更がないのが特徴です。

外部的なものは、単に別の URL に転送したいケースです。これには HTTP のリダイレクトが使用されます。リダイレクトでは、ブラウザのアドレスバーに表示される URL は転送先のものになります。
リダイレクトでは、同じサイト内で別の URL に飛ばしたい場合と、他のサイトへ飛ばしたい場合があります。


行いたいのは大抵は外部的な URL の転送だと思いますが、リダイレクトする方法を検索すると、リライトによる方法を紹介しているページが多く目に付きます。
しかし、複雑なことをしないのであれば、Apache であればリライトではなく Redirect ディレクティブによるリダイレクトで十分なケースもあるでしょう。

URL階層を引き継いで、別のサイトに飛ばしたい


例えば、http://foo.test/aaa/bbb.htmlhttp://bar.test/aaa/bbb.html に飛ばしたいというケースでは、以下の設定が使えます。

Redirect / http://bar.test/

301リダイレクトにする場合は、以下のようにします。

Redirect permanent / http://bar.test/
#または
RedirectPermanent / http://bar.test/

/aaa の下だけを飛ばしたい場合は、以下のようにすればOKです。

Redirect /aaa http://bar.test/aaa/


URL階層を引き継がないで、別のサイトに飛ばしたい


http://foo.test/aaa/bbb.htmlhttp://bar.test/ に飛ばしたいなど、/aaa/bbb.html であろうが ccc.html であろうが、とにかく http://bar.test/ という1つの URL に飛ばしたいというケースです。

この場合は、RedirectMatch を使用することで実現できます。

RedirectMatch .* http://bar.test/

RedirectMatch は本来、正規表現を使用して Redirect ディレクティブでは実現できない少し複雑な転送を行うものですが、RedirectMatch は Redirect ディレクティブのように URL 階層を引き継ぎませんので、この目的で使用することができます。



certbot-auto で ImportError: No module named cryptography.hazmat.bindings.openssl.binding エラー

AWS の EC2 で運用しているWebサイトがあり、そのサーバでは certbot-auto を使用してサイトのSSL化を行っていました。

先日、certbot-auto を使用してSSL証明書の作成を行おうとしたところ、以下のエラーが発生しました。

Upgrading certbot-auto 0.27.1 to 0.28.0...
Replacing certbot-auto...
Creating virtual environment...
Installing Python packages...
Installation succeeded.
Traceback (most recent call last):
  File "/opt/eff.org/certbot/venv/bin/letsencrypt", line 7, in <module>
    from certbot.main import main
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/certbot/main.py", line 10, in <module>
    import josepy as jose
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/josepy/__init__.py", line 44, in <module>
    from josepy.interfaces import JSONDeSerializable
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/josepy/interfaces.py", line 8, in <module>
    from josepy import errors, util
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/josepy/util.py", line 4, in <module>
    import OpenSSL
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/OpenSSL/__init__.py", line 8, in <module>
    from OpenSSL import rand, crypto, SSL
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/OpenSSL/rand.py", line 12, in <module>
    from OpenSSL._util import (
  File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/OpenSSL/_util.py", line 6, in <module>
    from cryptography.hazmat.bindings.openssl.binding import Binding
ImportError: No module named cryptography.hazmat.bindings.openssl.binding

Pythoncertbot も詳しくないのですが、タイトルのエラーについてはネット上にいくつも情報が転がっていました。

ただ、どれも微妙に異なる内容で、検索結果の上から試していきましたが一発では解決しませんでした。

結論、この環境では以下で復旧させることができました。

unset PYTHON_INSTALL_LAYOUT
/opt/eff.org/certbot/venv/bin/pip install --upgrade certbot

しかしネット上の情報と上記のエラーとを総合すると、原因は certbot-auto がアップグレードをかけるときに上手くいっていないためだろうと考えられました。
であるならば、certbot-auto を実行する前に unset PYTHON_INSTALL_LAYOUT すればよいのではないかと考え、別のサーバで unset PYTHON_INSTALL_LAYOUT してから certbot-auto を実行してみたところ、無事にアップグレードされました。

一度 certbot-auto してこのエラーが出てしまった後では、/opt/eff.org/certbot/venv/bin/pip install --upgrade certbot で明示的にアップグレードしてあげる必要があります。




XPath 基礎 (4)

XPath の基礎 (4) です。


XPath では、CSS ではできないような強力な選択を行うことができます。

これはテキストノードを使用できることがその主な要因であると感じます。

個人的によく使用するのが、「ある文字列が要素に含まれているか」という判断です。
例えば「"マツタケ"という言葉を含む div タグを探す」などです。これは CSS で行うことはできません。

このケースでは、以下のような XPath で選択することができます。

//div[contains(text(),'マツタケ')]


text() は、そのノードの子テキストノードという意味でした。ですから上記で、「すべての div タグのうち、子テキストノードに"マツタケ"を含むもの」を選択することになります。


ところで、上記の XPath では、以下のどちらの div も選択されるでしょうか?

<div>
マツタケの土瓶蒸し
</div>
<div>
  <span>マツタケ</span>の土瓶蒸し
</div>


実は上記の XPath では、2つ目の div は選択されません。text() はあくまで子テキストノードを指すからです。
2つ目の div の子テキストノードは空であり、条件に一致しません。


こういった場合はどうするかというと、以下のようにします。


//div[contains(.,'マツタケ')]


text() を . に変えています。. は、self::node() の省略形です。つまり上記でいえばそれぞれの div を指しています。

これでなぜ2つ目の div も選択されるかというと、

  • 関数呼び出しにおいては、引数が文字列型の場合は文字列値に変換化される
  • 要素の文字列値は、その子孫ノードのすべての子テキストノードを連結したものである

からです。

つまり、contains(.,'マツタケ') に渡される . は div 要素であり、contains の引数は文字列型なので引数は文字列化され、div 要素を文字列化したものはその div が囲っているすべてのテキストノードを連結したものであり、それはどちらの div も "マツタケの土瓶蒸し" なので、"マツタケ" を含むため条件に一致するということになります。

まとめると、タグのすぐ直下にある文字列だけを狙い撃ちしたいときは text() を使い、タグの下全部を対象にしたい場合は . を使うようにすればよいでしょう。



XPath 基礎 (3)

XPath の基礎 (3) です。

//div[@name='hoge']


上記のように書けば、name 属性が "hoge" であるすべての div タグを選択できます。

実は XPath には様々な省略記法があり、実は上記もその省略記法で書かれていて、省略しないで書くと以下のようになります。

/descendant-or-self::node()/child::div[attribute::name='hoge']


省略記法で書かれたものに比べると、省略記法を使用しないものは長ったらしくなっていますが、XPath の構成要素をちゃんと理解することは、XPath を書くときの一助になってくれると思います。

XPath の主要な構成要素には、ノードテスト述部 があります。

軸は、選択するもののベースを決めるもので、child、self、parent、attribute などがあります。

child であれば子ノード、self であれば自ノード、parent であれば親ノード、attribute であれば属性を意味します。

なお軸の後ろには :: をつける決まりです。

ノードテスト

ノードテストは、指定した軸の何を選択するかを決めるもので、要素名や属性名 や text()、 * などがあります。

例えば child::div であれば、子ノードのうちの div 要素を意味し、child::* であれば、子ノードのうちのすべての要素を意味します。

child::text() であれば、子テキストノードを意味します。CSS では意識することはありませんが、タグとタグの間にあるテキスト部分(<div>ここ</div>)がテキストノードです。

述部

述部は、軸とノードテストで指定したものをフィルタリングするものです。なくてもかまいません。

述部は [ と ] で表し、その中に を指定します。ここでは簡単に、真か偽かを判定する式だと考えましょう。

例えば attribute::name='hoge' なら「属性 name が "hoge" である」という条件でフィルタリングすることになります。



ここで最初に戻って、以下の XPath について考えてみます。

/descendant-or-self::node()/child::div[attribute::name='hoge']

まず、最初の / は「文書のルート」です。これはそのように決められています。

descendant-or-self が軸で、その意味は「自分およびその子孫」になります。

node() はノードテストで、すべての子ノードを意味します。

したがって /descendant-or-self::node() は、「全部のノード」を意味することになります。

次に child::div[attribute::name='hoge'] ですが、この child は軸で、/descendant-or-self::node() で選択されたそれぞれの子、ということになります。

child::div の div はノードテストで、要素 div をあらわしています。つまり、/descendant-or-self::node() で選択されたそれぞれの子の div 要素、平たく言えば 全部の div 要素 ということになります。

[attribute::name='hoge'] は述部で、上記にある通り「name 属性が "hoge" である」という条件になります。ですから、すべての div 要素のうち、name 属性が "hoge" であるもの を選択するということになります。




XPath 基礎 (2) 属性値での指定

HTMLタグの属性値の値によって、何らかの要素を指定したい場合があります。

<div class="foo bar">
</div>
<div id="hoge">
</div>
<div name="hoge">
</div>


CSS の場合、上記であれば、 以下のようなセレクタで指定できます。

.foo { ... }
#hoge { ... }
[name="hoge"] { ... }


XPath の場合は、以下のようになります。

//div[contains(@class, 'foo')]
//div[@id='hoge']
//div[@name='hoge']


上記を見てわかる通り、XPath では属性を @~ であらわします。@id なら id 属性ということになります。

CSS セレクタはあくまで HTML タグを選択するものですが、XPath では属性値を取得するために使うこともあります。そういった場合には、例えば //div/@id のようにして属性を指定することになります。

「id 属性が hoge であるもの」を指定するには、@id='hoge' とします。

name 属性でも同じわけですが、class 属性は contains(@class, 'foo') となっています。

これは、class 属性の値はあくまでも "foo bar" であって、"foo" ではないからです。CSS では .foo とすればそのクラス foo が指定されているものを選択できますが、XPath では属性毎に記法が分かれているわけではないため、「class 属性に foo が含まれているもの」という指定をしなければなりません。

それを指定するのが contains(@class, 'foo') というわけです。この contains は関数であり、他にも様々な関数が使用できます。

関数はネストさせることもできます。例えば「id 属性に hoge含まないもの」を選択するなら、not を使って以下のようにできます。

//div[not(contains(@id, 'hoge'))]


関数は他にも様々あり、それらを使用することで複雑な検索を行うことができます。


XPath 基礎 (1)

みなさんもご存知の通り、Web ページは HTML で書かれています。

ですので、スクレイピングしたりクライアントサイドで動的なページを作ったりするときには、自分が処理したいその部分を何らかの方法で指定する必要があります。

jQuery 使いの方に馴染みが深いのは CSS セレクタかと思います。

.block {
  width: 100px;
}

.block がセレクタですね。 「class 属性に block が指定されている要素」を指定するものです。

CSS セレクタでもかなり複雑な指定ができますが、XPath を使用するとさらに複雑な指定ができるようになります。

CSS セレクタでは、

  • タグ名で指定した要素 (div, p)
  • id 属性や class 属性に、指定した文字列が設定されている要素 (#hoge, .hoge)
  • 任意の属性の値が任意の文字列と一致する要素 (div[class="hoge"])
  • 指定した要素の下位、直下、兄弟などの要素 (div p a, div > p)

といった指定の仕方が基本となります。

XPath でも同じように、タグ名、構造、属性値などで要素を指定することができます。

XPath では、ロケーションパスという表現を使って要素を指定します。例えば、

//div

これは全部の div タグを指定することになります。

XPath では、ディレクトリ構造を指定するように、/ (スラッシュ) で要素を区切って指定します。

//div/p/a

上記であれば、「すべての div タグの直下にある p タグの直下にある a タグ」ということになります。

最初の / はルートノードを意味します。ルートノードとはなんぞやということですが、HTML 文書は入れ子の構造であり、つまりツリー構造になっています。
その一番上の頂点がルートノードです。ですので、

/div

としてしまうと、それは頂点の直下の div ということになってしまいますので、これに合致する要素はありません。

// は、「自身および子孫」を表します。なので、//div はすべての div タグを意味するというわけです。

//div//p

上記のようにすれば、「すべての div タグのそれぞれの子孫にある p タグ」ということになります。

<div id="d1">
  <p>...</p>
  <div id="d1-1">
    <p>...</p>
    <section>
      <p>...</p> <!-- この p は div/p ではマッチしない -->
    </section>
  </div>
</div>
</html>