masalibの日記

システム開発、運用と猫の写真ブログです

【python3】はてなブログの投稿画像を圧縮する

ブログ画像は重いとSEO的に評価されない
はてなフォトで圧縮した画像をアップしようとしたら更新処理がなかった
Wordpressにあってはてなブログにない機能の1つ

投稿した画像を圧縮する (はてなブログの場合は切り替えになる)

はてなblogAPIとはてなフォトAPIで作ってみました

私のブログは記事が500を超えています
現在の画像から画像を圧縮して新規で
投稿してその画像にブログを更新するなんて無理ゲーです

ループで全ページを回すことも可能だったのですが怖かったので1ページ単位にしています (アクセスが多いページだけやればいいという考えです)

node.jsで作りたかったが非同期処理の沼にハマったので同期処理のpythonに切り替えた (ファイルIO部分)

続きを読む

フォルダ内のファイルを削除(from~toを複数)

単純なfrom~toでも指定できないファイル削除があった・・・
ファイル数が万を超えてかつ数百のフォルダだった
GUIでは無理だった
Windowsなのでvbsで無理やり削除した
二度とやりたくない・・・ (´;ω;`)

Option Explicit

'// INIT
Dim FSO
Set FSO = WScript.CreateObject("Scripting.FileSystemObject")

FindFolder FSO.GetFolder(".")

'// 後処理
Set FSO = Nothing


Sub FindFolder(ByVal objMainFolder)
    Dim objSubFolder
    Dim objFile

    '// フォルダがあれば再帰
    For Each objSubFolder In objMainFolder.SubFolders
        FindFolder objSubFolder
    Next

    For Each objFile In objMainFolder.files

            if (  "2010/02/02 00:00:00" <= objFile.DateLastModified and objFile.DateLastModified <= "2010/12/30 00:00:00" ) Then
                WScript.Echo  "delete:"  & vbTab & objFile.Name & vbTab & objFile.DateLastModified
                objFile.delete 
            End IF
            if (  "2011/02/02 00:00:00" <= objFile.DateLastModified and objFile.DateLastModified <= "2011/12/30 00:00:00" ) Then
                WScript.Echo  "delete:"  & vbTab & objFile.Name & vbTab & objFile.DateLastModified
                objFile.delete 
            End IF
    Next
End Sub

2019/3月のPV報告

作業

  • 33記事を追加 (673→706記事)
  • 広告の最適化が効かなくなって、H2の前に広告を表示するようにした

masalib.hatenablog.com

ampの場合に広告が表示されない問題があったので表示するように変更した 記事単位でしかできないみたい・・・よくわからないけど

ti-tomo-knowledge.hatenablog.com

結果

PV

f:id:masalib:20190331233325j:plain

アクセスは13070でした
どこからも砲をいただかず・・普通の日々でした

2月に比べると下がっている。これはGoogle検索エンジンアルゴリズムのアップデートにより 3割ほど下がっており、次の一手を探さないといけない

masalib.hatenablog.com

2019/04/01 13:14追記
ノイズがあると順位がさがるそうです。

www.web-planners.net

AdSenseアフィリエイト

AdSenseの広告が表示されないということもありほんの少しだけ下がりました

続きを読む

【python3】はてなBlogApiではてなブログを更新

はてなブログapiを公開しており、そちらを使って更新したいと思います。

参考URL

cartman0.hatenablog.com

参考にしたサイトをほぼ丸パクリしています。 違う点は * 関数にしている所 * 更新用の配列を渡している所

仕様

  1. はてなblogに必要なパラメータをセットする
  2. リプレース用の配列を渡す
  3. 更新用の関数を起動する
  4. urlからXMLをダウンロードする
  5. 必要な箇所を更新してXMLを作成する
  6. はてなブログAPIで更新する

ソース

import requests
import bs4



# blogを更新する(一部replaceする)
def update_blog(hatena_id:str , password:str , blog_id:str, entry_id:str,replace_data):
  
    member_uri = "https://blog.hatena.ne.jp/{hatena_id}/{blog_id}/atom/entry/{entry_id}".format(hatena_id=hatena_id, blog_id=blog_id,entry_id=entry_id)

    print(member_uri)
    
    res_member = requests.get(member_uri, auth=(hatena_id, password))
    if not res_member.ok:
        raise Exception("Failed: status_code: " + str(res_member.status_code))
    soup_response_xml = bs4.BeautifulSoup(res_member.content, features="xml")

    # XMLsoupのクローン
    update_soup_xml = bs4.BeautifulSoup(str(soup_response_xml), features="xml")
    # id 削除
    if update_soup_xml.id: update_soup_xml.id.decompose()
    # link 削除
    for l in update_soup_xml.findAll("link"):
        l.decompose()
    # delete published
    if update_soup_xml.published: update_soup_xml.published.decompose()
    # delete app:edited
    edited = update_soup_xml.find("app:edited")
    if edited:
        edited.decompose()
    # delete summary
    if update_soup_xml.summary: update_soup_xml.summary.decompose()

    # hatena:formatted-content
    hatena_formatted_content = update_soup_xml.find("hatena:formatted-content")
    if hatena_formatted_content:
        hatena_formatted_content.decompose()
      
      
      
    # title(変更するなら)
    #new_title = "test title"
    #if new_title: update_soup_xml.title.string = new_title

    # author
    new_author = ""
    if new_author: update_soup_xml.author.string = new_author

    # updated
    new_updated = ""
    if new_updated: update_soup_xml.updated.string = new_updated

    # new category
    new_categories = []
    for new_c in new_categories:
        cate_tag =  update_soup_xml.new_tag("category")
        cate_tag.attrs = {"term": new_c}
        update_soup_xml.append(cate_tag)
    # new draft
    new_draft = "" # yes, no

    if new_draft: soup_response_xml.find("app:draft").string = new_draft
    # content書き換え
    new_content = update_soup_xml.content.string
    for item in replace_data:
        print(item[0] ,item[1] )
        new_content =new_content.replace(item[0], item[1])

    #new_content = "new content"
    #update_soup_xml.content['type'] = "text/plain"
    
    
    update_soup_xml.content.string = new_content
    print( str(update_soup_xml.prettify()))
    result_xml = requests.put(member_uri , auth=(hatena_id, password) , data=str(update_soup_xml).encode("utf-8"))
    print(result_xml)
    print(result_xml.text)

hatena_id = "masalib"
password = "xxxxxxx"
blog_id = "masalib.hatenablog.jp"      #猫用のblogid
entry_id = "10328537792364688871"   #テスト用の記事id

replace_data = []
replace_data.append(["http://cdn-ak.f.st-hatena.com/images/fotolife/m/masalib/20160218/20160218144707.jpg","replace_url_1"] )
replace_data.append(["https://cdn-ak.f.st-hatena.com/images/fotolife/m/masalib/20190323/20190323161133.jpg","replace_url_2"] )
#print (replace_data)

update_blog(hatena_id , password , blog_id, entry_id,replace_data)

結果

f:id:masalib:20190331224433j:plain

ステータスが200になった。
更新もできるようになった。これで次のステップにいける

はまった事

Blog_idがメインとサブが間違えていた
その場合に、データは取得できるけど、更新ができないという沼にはまった

エラー文言 The entry is not article of the blog" api

GETは成功するけど、PUTは失敗するという状態 GETでも失敗してほしかった

javasciptで表示領域から画像のピクセルを指定する

古いシステムで画像を表示する関数で tableタグのなかにtableを使って表示しているという 超ーーーいけていないのがあり、 ピクセルサイズが大きいサイズがきた時に 画面幅を振り切れるというバグがあった

単純にjsでパーセントを指定すると小さくなりすぎるという問題があった 結局、表示領域から計算して ピクセルを指定といういけてない形になった

<html><body>
<tbody><tr>
    <tr>
        <td class="orangeHdr">写真<br>
        </td>
        <td>
            <table border="0" cellpadding="0" cellspacing="4">
            <tbody><tr>
            <td width="130" align="center">
            <img id="img" src="https://lh3.googleusercontent.com/4yQhH6EqCJJKFr7gOybqoaKRfkDZ5HJsmvBLE4OwdfP7BT3GSIRXFG5JGpgVoT3QFCeqNb51qS6BySmj-jtC9pKsN4CrF1zegxouVf11bkJe24JXoNoe7zys-RgyQTBFYOWLaNTj8nDEtZZtc2YoyDF3QmJzyu5CzCKQiccrP1e3jyLw8RYCwi25q7Sx9eWli_KfPbvJRZe5LLUmlX9kJHHNJMFFIdk4qJMhOdmogJ9_PHIikmPK4bGDqLcnu8kaLoU9l3enylHrCfIV89HL18dOwaLer2J9NnA11blX1ojn4OBv-bNsWobunCrGyFu_O09DTLwE_I-iLjCfv07DBje1Oil3yQ1kAwIr7ZsbyS4fshpxxc-gwB_mRz55aVDopiwSdPxegY-NRWjzfx8SS88KUzBURhxRNLJwY9HLNlFUEeYsp1-PvvwgAWwE8-wl4memdO5egqW8zldF5uP_QlSmXnojl3cYysPK447O-9s8fBMKr_GjaLV3mYxLl-AUkWeMiqmJY_fIY8MwJiVpDr6dF0vduD0IF03AogXrb7vfw96r0YupyJbyEt2rwwZX4xzsvMsMLYjKLrjaMkJv_rDMZdtAOJ2kTa0IK9iLlqkJdZss691GSfrAFTj0y_Jgs6LOCg-5pnjCESfBOFu8heIdtyXyXXyRaVtHCS--zuKrhgqo-cbnD2QFKoUnPQHM9DarTkUqMH48zci9ZxA=w1218-h913-no" >
            <div style="background-color:white;font-weight:bold;font-size:10pt;text-align:center:padding:2px;margin:1px" id="state">現在</div>
            </td>
            <td valign="top">
            <input type="file" name="file" id="file"><br><br>
            <input type="hidden" name="pv" value="">
            </td>
            <input type="hidden" name="pvfile" value="/img/Common/Result/Result/00108216.jpg?dmy=2019%2F03%2F29+18%3A37%3A15">
            </tr>
            </tbody></table>
            </td>
    </tr>

</tbody>
    <script language="JavaScript">
       var element = document.getElementById("img") ;

       var intervalId = setInterval( function () {
           if ( element.complete ) {
               var width = element.naturalWidth ;
               var height = element.naturalHeight ;
               console.log("source image width");
               console.log(width);
               var sw  = window.innerWidth; 
               console.log("window.innerWidth");
               console.log(sw);

               if ((width * 0.8) > sw){
                   console.log("画面幅にあわないのでstyleあわせる");
                   console.log(sw * 0.6);
                   element.style.width = sw * 0.6;
                   console.log("update style");
               }
 

               clearInterval( intervalId ) ;
           }
       }, 1000 ) ;
   </script>
</body>
</html>

関係ないけど・・・

検証用にcodepenを使っていたら落ちた・・・

自分が原因かとあせった

自分の個人情報がもれたらしい

www.taito.co.jp

今回流出した可能性がある情報は、次のとおりです。

  • 氏名
  • メールアドレス
  • 電話番号
  • ハッシュ化されたパスワード
  • クレジットカード情報の一部(カードの下4桁、有効期限)(※)

クレジットがもれていないのが唯一の救いかな
利用したのが2018/11/26なので 正直で・・・どのアドレスで登録したのかすら覚えていたない

気がついた理由

たまたまこのサイトを監視していたから気がついた

www.taito.co.jp

運がいいのやら、悪いのやら

監視と書いてありますが はてなのアンテナで登録すれば更新されたら連絡がもらえます。今回は消すのを忘れていた。たまたま気がついた。

a.hatena.ne.jp

不思議なのは個人情報は以下のサイトに登録していた。

coubic.com

でももれたのは「タイトのサイト」のみだった 個人情報を渡す部分でもれたのかな・・・?

ハッキング方法の発表はない

インフラエンジニアとしては
どうやってもれたのか発表してほしい
他の人が同じ轍を踏まないように ハッキング方法などを教えてほしい。

【python3】pythonのPillowで画像の圧縮して、はてなフォトに投稿する

前回、画像の圧縮ができたので圧縮した画像をはてなフォトに投稿したいと思います

仕様

動き

  1. urlから画像をダウンロードして圧縮する
  2. ダウンロードした画像の縦横のピクセルを取得する
  3. 変換した画像サイズのピクセル数を計算する
  4. 画像圧縮する
  5. 変換画像を保存する
  6. はてなフォトの投稿用のXMLを作成する
  7. はてなAPIの認証用のヘッダーを作成する
  8. はてなフォトAPIにPOST送信する
  9. 結果のXMLを取得する

ソース

from base64 import b64encode
from datetime import datetime
from hashlib import sha1
import random
import requests
import sys
from pathlib import Path
import IPython
from io import StringIO
import io
import urllib.request
from PIL import Image

now = datetime.now()
dtime = str(now.year)+"""-"""+str(now.month)+"""-"""+str(now.day)+"""T"""+str(now.hour)+""":"""+str(now.minute)+""":"""+str(now.second)
print (dtime)
hatena_id = "masalib"
password = "xxxxxx"

username = hatena_id
api_key  = password 


# はてな送信用のヘッダーに認証データ
def wsse(username: str, api_key: str) ->str:
    created = datetime.now().isoformat() + "Z"
    b_nonce = sha1(str(random.random()).encode()).digest()
    b_digest = sha1(b_nonce + created.encode() + api_key.encode()).digest()
    return f'UsernameToken Username="{username}", PasswordDigest="{b64encode(b_digest).decode()}", Nonce="{b64encode(b_nonce).decode()}", Created="{created}"'

# はてなフォトに送るためのXMLデータ作成
def create_photo_data(file_name: str,title : str) ->str:
    uploadData = b64encode(Path(file_name).read_bytes())
    return """
    <entry pxmlns="http://purl.org/atom/ns#">
    <title>""" + title + """</title>
    <content mode="base64" type="image/jpeg">""" + uploadData.decode() + """</content>
    </entry>
    """


def post_hatena_photo_api(data):
    headers = {'X-WSSE': wsse(username, api_key)}
    url = 'http://f.hatena.ne.jp/atom/post/'
    r = requests.post(url, data=data, headers=headers)
    print(r.status_code)
    xml_data =r.text
    return xml_data

# 画像のURLからデータを取得してリサイズする
def get_imagedata_resize(url :str):
    f = io.BytesIO(urllib.request.urlopen(url).read())
    img = Image.open(f)
    before_x, before_y = img.size[0], img.size[1]
    # 横基軸の場合
    width = 480
    x = width
    y = int(round(float(width / float(before_x) * float(before_y))))

    img = img.resize((x,y), Image.LANCZOS)
    return img

savefilepath="resized" + dtime + ".jpg"
title="resizetitle"

imagesrc ="https://cdn-ak.f.st-hatena.com/images/fotolife/m/masalib/20190311/20190311221952.jpg"
imgdata = get_imagedata_resize(imagesrc)
imgdata.save(savefilepath)

print("data作成")

data = create_photo_data(savefilepath,title)

#print("はてなフォトに送信")
hatena_photo_post_xml = post_hatena_photo_api(data)
print(hatena_photo_post_xml)

#IPython.display.Image(savefilepath)

結果

XMLが表示されているだけですがよかったwww

感想

先人の知恵に感謝です。自分の力のみでは作れなかった

teratail.com