nyanpyou Note

主な目的は調べたり作ったりしたプログラミング備忘録(予定)

下書きのまま忘れられていたメモ書き

メモ代わりに書きつけたまま公開せずに放置されていたことに気づいたので放り投げます←
最近作りたいものを思いつき、そのためにまた新しいことを学ぼうとしているところなので、それについてまとめるかもしれない。

以下供養のメモ書き

format()関数を使った時の値の表示方法にちょっとこだわりたくなった(6桁固定で左埋めにするとか小数点以下は何桁で固定するとか)ので書式の弄り方について調べた。

docs.python.org

format関数を使っている時に{}を文字列として使いたい時は、{{}}と二重にしておけばエスケープして使える。
文字列中にUnicodeを使いたい時は、\uでエスケープしてやるとそのUnicodeに割り当てられた文字が表示される。例えば

print("\u2642")

とすれば

>>♂

と表示される。


os.system()を使えばPythonからコンソールのコマンドを使えるが、subprocess.call()を使った方がいいらしい。

docs.python.org


numpyのndarrayはtolist()メソッドを使うとリストに変換できる。


ANSIエスケープシーケンスを使えば、コンソール上でカーソルを上下左右自在に動かしたり、文字に色を付けたりできるらしいが、Windowsコマンドプロンプトは悲しいことに対応していない…。UNIXだと使えるという話。
python上からsubprocess.call()でclsコマンドを実行させると遅い(自分の環境だと1秒くらいかかった)ことが分かったので、エスケープシーケンスで何とか改善できないかと思ったものの断念。
以下の記事を見てコマンドプロンプトでアニメーションを高fpsで描画させてみたいと思ったのだが、良い方法知ってる方がいたら教えてください(1日かかってもわからなかったので結局fps落とした←)。

dev.classmethod.jp


Tkinterで使える色のリスト

memopy.hatenadiary.jp


OpenCVのcap.set()を用いて解像度を変更するとき、(録画ソフトなどで確認した)カメラに設定可能な解像度を指定しているのにもかかわらず、設定した解像度にならない事がある。

import cv2
WIDTH = 1280
HEIGHT = 720
FPS = 30

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G'))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
cap.set(cv2.CAP_PROP_FPS, FPS)

width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS)

while(cap.isOpened()):
    ret, frame = cap.read()
    cv2.imshow("",frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

f:id:nyanpyou106:20200707113649j:plain
上記のように16:9の解像度を指定しても、左右に帯が入ってしまう。
この場合はcv2.VideoCapture()で、引数にcv2.CAP_DSHOWを指定してやると解決する。

cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

f:id:nyanpyou106:20200707113743j:plain
黒帯だった部分が埋まった。

下記記事ではcv2.CAP_DSHOWを指定すると、16:9で映せるようになる代わりに処理速度が落ちると書かれているが、自分の環境では確認できなかった。正確に測定したわけではないが、目視上映像にカクつきはなく、それどころか映像が画面に表示されるまでの時間がcv2.CAP_DSHOWを指定した場合には劇的に改善した(指定なしの場合は実行してから映像が表示されるまで30秒くらいかかる)。
記事を書かれた方はC++版のOpenCVを使っているため環境が異なるが、Windows10、Python 3.7.4、OpenCV4.2.0では上記結果となった。

madeinpc.blog.fc2.com

Pythonで論文の情報をスクレイピングする

きっかけ

論文投稿雑誌の一部にはVideo Abstractなるものを一緒に乗せることが可能らしい。
初めて聞いたものなので、一体どんなものなのか他の人達の投稿を確認しようと思ったが、どうやら目次を見てもその論文にVideo Abstractが載っているのかの記載は無く、いちいち該当ページを開いて確認しないとわからない。
あまりにも面倒で手間がかかるので、以前書籍等で読んだPythonを用いたスクレイピングを試してみることにした。
…のだが該当サイトのrobots.txtをよく読んでみると、特定のクローラー以外のアクセスを禁止している事が発覚した。
アクセス毎に数秒待ち、アクセス件数も50にさえ満たないのでサーバーに負荷をかけるようなことは無いと思うが、禁止されている以上従うしかないので、以下は次回別の場所で使うためのメモ書きということにする。

目的

目次のページからどの論文にVideo Abstractが掲載されているかを調べ、該当論文のURLを取得する。

コード

import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse
import time
import random

target_url = input("URL?")
# アクセスの待ち時間(負荷軽減&bot判定回避)1~5秒の間でランダムに待つ
sleeptime = random.uniform(1, 5)

domain = '{uri.scheme}://{uri.netloc}'.format(uri=urlparse(target_url))

# Firefoxからアクセスしているように偽装
# 偽装しないとbotとみなされてCaptchaページに飛ばされる事がある
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0"

# 論文ページへのリンクのリストを取得する

links = []
response = requests.get(target_url, headers={"User-Agent":user_agent})
soup = BeautifulSoup(response.content, "html.parser")
article_title = soup.find_all("a", attrs={"class":"art-list-item-title"})

for i in article_title:
    # iはbs4.element.Tagオブジェクト
    link = i.get("href")
    # hrefタグにドメインを省略してリンクが書かれているので、
    # ドメインをくっつける
    links.append(domain+link)
if len(links) == 0:
    print("Article's link is not found.")
else:
    print("Success")

time.sleep(sleeptime)

# 対象のページにVideo abstractが存在するか調べる
# 存在したらそのページのURLをリストに保存

articles_with_VideoAbstract = []
for url in links:
    response = requests.get(url, headers={"User-Agent":user_agent})
    soup = BeautifulSoup(response.content, "html.parser")
    video_abstract = soup.find_all("div", attrs={"class":"videoPlayer mb-1"})
    if len(video_abstract) == 0:
        pass
    else:
        articles_with_VideoAbstract.append(url)
    time.sleep(sleeptime)

if len(articles_with_VideoAbstract) == 0:
    print("Articles with VideoAbstract is not found.")    
else:
    print("Articles with VideoAbstract")
    for i in articles_with_VideoAbstract:
        print(i)
  • スクレイピングを始める前に、robots.txtにアクセスしてスクレイピングする際の規則をチェックする。
    ディレクトリの最上位に置く決まりになっているようなので、ドメイン/robots.txtとアドレスバーに打ち込んで中身を確認する。
    一番下の方に一般のクローリングについて書かれていることもあるのでしっかり見落とさずに確認すること!(自戒)

  • user_agentは自分の使っているブラウザのものを確認するといいと思うが、各種ブラウザについてまとめてくれている人もいるのでこちらも参考にすると便利かもしれない。 qiita.com

  • urlparse()にURLを渡すと以下の返り値が得られる。

ParseResult(scheme='https', netloc='iopscience.iop.org', path='/issue/0031-9120/55/5', params='', query='', fragment='')
  • 以下はrobots.txtをしっかり読まないままテストしていた時の覚書だが、何度も言うように事前にきっちり理解してから作業しましょう()
    今回はドメインを省略したURLがタグに記載されていたので、事前にこれを利用してドメインを抜き出しておき、結合することにした。
    webサイトへのアクセスを短時間に連発すると、botと判定されてCaptchaページに飛ばされてしまう。実際に待ち時間を0.5秒に設定してみたらbot判定を食らってreCaptcha認証ページ(私はロボットではありません。にチェックを付けるあれ)に飛ばされた。一度このページに飛ばされると手動でブラウザからページにアクセスして認証しないと接続できなくなるので気を付ける。
    このサイトはどうも同じ時間間隔でアクセスし続けてもbot判定を食らうようだったので、こちらの回避のためにアクセス時間も乱数で散らすようにしてみた。

まとめ

欲しい情報を自動で拾ってくるスクレイピングプログラムを作ってみた。やはり知識だけあっても自分で独自に作ってみないと身につかない。今回は作ったものの使えずに終わってしまったが、次回以降はしっかりと運用したい。

参考にしたサイト

URLからドメインを抜き出す方法
http://blog.mwsoft.jp/article/176458287.html

qiita.com