プログラミング素人のはてなブログ

技術屋の末端。プログラミングも電気回路も専門外です。 コードに間違いなど見つけられたら、気軽にコメントください。 VC#、python3、ラズパイ始めました。

tweepyでTwitterの画像検索

twitterは情報の宝庫です。
tweepyを使って「ねこ」の画像を集めてみたいと思います。
しかしながら、tweepy(twitter apiに無い)には画像を保存するメソッドが無いようなので、tweepyで画像のurlを取得してpythonのreqestsでアクセスして画像を保存します。

画像の保存の動きは↓を使わせてもらいました。
qiita.com

tweepyで検索を実行して、

for tweet in api.search(q=keyword, count=200):

Tweetの中にmediaがあるときに、mediaのurlを取得して、qiitaからいただいた関数にurlを渡します。

tweet.extended_entities['media'][0]['media_url']

tweepyではmedia(画像や動画)がないときにはこのKeyが無いので、本来ならこのkeyがあることを確認してからアクセスするべきですが、tryでくくることで省略しています。

この、extended_entitiesのkeyが変な構造になっていて、media keyのなかに要素が1のlistがあって、そのなかにmedia_urlのkeyがあります。

extended_entities={
'media':{
[
'media_url' :'http://pbs.twimg.com/media/hogehoge.jpg'
]
}
}

twitterapiのloginは関数を作っておくことで、別のpythonファイルにすることもできます。
importするような形にすることで、間違ってapi keyが流出することを防止できます。

login.pyとかにしておく。

def login(acount):
  if acount=="s51517765":
    auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
    auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
    api = tweepy.API(auth)
    return api
  else:
    exit()

画像を保存するときは適当な連番をつけたりしないと、次々上書きされてしまいます。
数字(整数)を使って1~という方法が簡単ですが、これでは実行するたびに1~始まって上書きされてしまうので、日付と時刻を使うことでこれを防ぐことができます。
ただし、これでも1秒以内は同じ名前になって上書きされるので注意です。
ms(ミリ秒)までつかうという方法もありますが。

main.py

import login
import datetime
import tweepy
from datetime import datetime as dt
import time
import requests
import shutil

def download_img(url, file_name):
    r = requests.get(url, stream=True)
    if r.status_code == 200:
        with open(file_name, 'wb') as f:
            r.raw.decode_content = True
            shutil.copyfileobj(r.raw, f)

if __name__ == '__main__':
        keyword="ねこ"
        api=login.login("s51517765")
        for tweet in api.search(q=keyword, count=200):
            try:
                url=tweet.extended_entities['media'][0]['media_url']
                print(url)
                tdatetime = dt.now()
                filename='img/'+tdatetime.strftime('%Y%m%d%H%M%S')+'.jpg'  #"img"というサブフォルダに保存
                download_img(url,filename)
                time.sleep(1) #上書きされるので1秒待ってファイル名が変わるようにする
            except:
                pass #画像がないときはなにもしない

f:id:s51517765:20180217192131j:plain

ラズパイのGPIOを使う

youtu.be

まずは、ラズパイでGPIOの基本的な使い方を確認していきます。
↓のような回路を作って、CDSで明るさを検出し、LEDを制御します。
f:id:s51517765:20180211152302p:plain

回路図用パーツを作る

ここで、回路図は水魚堂のエディタhttp://www.suigyodo.com/online/schsoft.htmを使っていますが、ラズパイの回路図部品がないので作成しました。

部品の作成はLcoV.exeというエディタで作成します。「回路図エディタBSch3V」 をダウンロードすると一緒に入っているはずです。
f:id:s51517765:20180211125536j:plain
これを立ち上げて、”編集¥部品の新規作成”とクリックします。
ここで名前を「RASPBERRYPI」とつけます。
サイズを設定してOKで枠がつくられます。
"ピン"のアイコンまたは、"ツール¥ピン"を選択し、ピンを配置していきます。
ピンを必要数配置したら、”表示¥ピン編集ウィンドウ”でGPIO名やピン番号を設定して、保存します。
これで部品は完成です。
f:id:s51517765:20180211130749j:plain

次に、回路図エディタBSch3Vで、”設定¥ライブラリ¥add” でさきほど作成したライブラリを読み込みます。
f:id:s51517765:20180211163210j:plain
これで、部品にラズパイが追加され、ラズパイを含む回路図が作成できます。
部品ファイルは↓にアップしておきました。よかったらご自由にご活用ください。
https://dotup.org/uploda/dotup.org1460594.zip.html
pass: raspi

ターミナルでGPIOの状態を確認する

現在のGPIOの状態は、ターミナルからGPIO Readallコマンドで確認することができます。
ちょっと、最初はわかりにくいですが以下のように一覧で、ピン配置と同じように、段組みで表示されます。
前回調べた通り、10番Pin(physical)はGPIOの15(BCM)でINPUT MODEになっていることが確認できます。
WiringPiでは16番になるようです。

pi@raspberrypi:~ $ gpio readall
+-----+-----+---------+------+---+-Pi ZeroW-+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 |   IN | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 |   IN | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   IN | 1 |  7 || 8  | 0 | IN   | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 0 | IN   | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 | IN   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI |   IN | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO |   IN | 0 | 21 || 22 | 0 | IN   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK |   IN | 0 | 23 || 24 | 1 | IN   | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | IN   | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   IN | 1 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   IN | 1 | 31 || 32 | 0 | IN   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   IN | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   IN | 0 | 35 || 36 | 0 | IN   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   IN | 0 | 37 || 38 | 0 | IN   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 | IN   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+-Pi ZeroW-+---+------+---------+-----+-----+

トランジスタの状態

ここで、CDSを手で覆う用にして「暗い」状態にすると、10番ピンがHIGHになります。
事前にCDSの抵抗を調べてあって、暗いときは10kΩ、明るいときは1kΩぐらいになっています。
3.3VをCDS、抵抗(ここでは1kΩ)に接続し、CDSと抵抗の分圧された位置からNPNトランジスタのBaseに接続しています。
明るいとき、CDSの抵抗が低くなり、BaseはHighになり、E-Cはつながり、10番がLowになります(GNDと10番がつながるため)。
暗いとき、CDSの抵抗が高くなりBaseはLowになり、E-Cは切れて、10番がHigh(プルアップされているため)になります。

CDSと抵抗の間からラズパイのGPIOに直接入力しても、電位水準は変わりますが読み取ることができます。
しかし、GPIOには中途半端な電位を入力するのはあまり良くないらしいので、トランジスタでHigh/Lowを明確にします。

いつも迷う、トランジスタのECB。刻印のある面を見て左から「エクボ」
f:id:s51517765:20180211133319p:plain

http://www.op316.com/tubes/tips/image/2sc1815.pdf

これで、ラズパイでHigh/Lowが読み取れることが確認できました。

WiringPiのインストール

WiringPiはもともとC言語でGPIOを制御するライブラリで、いろんな言語のラッパーが提供されています。
ここではpyhton3用のラッパーをインストールします。
Windowsには標準ではインストールされていないので、anaconda pronptからpipインストールします。

$pip install wiringpi

て、やろうとしたが、インストール失敗。
っていうか、Windows PCではどちらにしろGPIOの動作はできないのか…
だが、インストール出来ている例がggrと出てくるので、これはこれで別途調査する必要がある。

ラズパイにはインストール済みのようです。
インストール済みのライブラリは↓で確認できます。

$dpkg -l

入っていなければ、同様にpip3でインストールします。

GPIOの基本のプログラミング

CDSで明るさを検出してLEDを制御するプログラムをpython3で作成しました。

# -*- coding: utf-8 -*-
import wiringpi
import time
print("Start WiringPi!")

# プルアップ INPUT
collector_pin = 15 # 10番pin(GPIO15)
# OUTPUT
LED_pin =23 #16番pin(GPIO23)

# GPIO初期化
wiringpi.wiringPiSetupGpio()
# GPIOを入力モード(0)/出力モード(1)に設定
wiringpi.pinMode( collector_pin,0)
wiringpi.pinMode( LED_pin,1)

wiringpi.digitalWrite(LED_pin,0)

while(True):
    if (wiringpi.digitalRead(collector_pin))==1:
        print("High")
        wiringpi.digitalWrite(LED_pin, 1)
    else:
        print("Low")
        wiringpi.digitalWrite(LED_pin, 0)
    time.sleep(1)

ラズパイのGPIOを操作するライブラリは数種類ありますが、私にとっては"WiringPi"がArduinoと似ていて使いやすそうなのでこれを選びました。

参考資料

日経Linux付属のラズパイマガジンビギナーズ
日経Linux(リナックス) 2017年 9月号 [雑誌]

日経Linux(リナックス) 2017年 9月号 [雑誌]

WiringPiの基本的な使い方の例が載っていて解りやすいです。

ラズパイゼロのプルアップ抵抗を調べる

ラズパイでいよいよIOTを始めようとGPIOの資料を読み漁っていたら、ラズパイのGPIOは内部プルアップまたはプルダウンされているという情報を見つけた。

d.hatena.ne.jp

プルアップ・ダウンは、使われていないGPIOをHIGH(3.3Vや5V) or Low(0V = GND)に高抵抗で接続して、入力を安定させるためのものである。
f:id:s51517765:20180128180703p:plain
GPIOの内部は上左図のように電源(3.3V or 5V)が直接ヘッダピンに来ていると思いがちだが、上右図のように、10kΩ程度の抵抗が入っていることがある。これがプルアップである。

プルアップがあるかないかで、どのように影響があるかはたとえばGPIOの1番と3番にLEDをつけてみるとわかる。
GPIO1はいわゆる3.3Vピンで、GPIO3は出力をプログラムで制御できるピンである。
開放状態でテスターをあてるとどちらも3.32V(個体差があるかも)であるが、LEDの明るさが全く違う。
f:id:s51517765:20180128183857j:plainf:id:s51517765:20180128183850j:plain
このように見かけ上同じ3.3Vでもプルアップであるかそうでないかは出力におおきな違いをもたらす。
そこで、実際のところどれくらいの抵抗が入っているのか?設定はプルアップなのかプルダウンなのかを実際に調べてみた。

プルアップ抵抗は↓のように適当な抵抗(ここでは680Ω)を接続し、その抵抗の両端の電圧を測定するとわかる。
f:id:s51517765:20180129213617p:plain

ここでは、ラズパイゼロは電源を入れただけの状態(Raspbianが起動している状態)である。
(また別途試す予定だが、プルアップ・ダウンの有効無効はpythonなどで切り替えられる。)

次の表は、GPIO開放状態の電圧と、680Ωの抵抗を接続したときの電圧をすべてのピンについて実測したものである。
f:id:s51517765:20180128184725p:plain

この結果から内部プルアップ抵抗をR0として、たとえばGPIO7では680Ωの抵抗の両端は0.04V、開放の時の出力が3.15Vであることから、
680Ω+内部抵抗R0で3.15Vとなり、680Ωで0.04Vなので
f:id:s51517765:20180128185102p:plain
たすき掛け計算より、
680*3.15=0.04*R+0.04*680
R0=52,870(Ω)≒53.8k(Ω)と算出できる。
同様にGPIO3,GPIO5は約1.7kΩ。

次に、プルダウン設定になっているピンについては、反対に3.3Vに引っ張り上げて測定すれば算出できる。
f:id:s51517765:20180129213710p:plain

f:id:s51517765:20180128185709p:plain
ここでも、測定電圧は同様に抵抗の両端の電圧である。

こちらは、GPIO8、GPIO15が0で、それ以外が約70.7kΩと算出できた。

しかし、プルアップとプルダウンで抵抗値がちがうのは不自然である。
電圧の読み取り値が0.04Vと0.03Vなので、テスターの分解能+誤差の問題と推定される。
つまり、どちらの場合も本来は0.03~0.04V程度で±0.02V程度の誤差がある可能性がある。
つまり0.02Vが真値であれば約106kΩとなり、0.06Vであれば約35.4kΩとなってしまう。

そこで、基準抵抗を19.9kΩにして再度測定してみると、基準抵抗両端の電圧が大きくなるのでより正確に測定できる。結果、約46.4kΩと算出された。

まとめ

プルアップ・ダウン抵抗値は、GPIO3、GPIO5が1.7kΩ、その他は約46kΩである。
最初に引用したページでの推定結果ともほぼ一致している。
また、測定結果が基準抵抗(最初に使った既知の680Ωのこと)と大きく離れていたら近いサイズのもので再測定するとより正確に算出できる。

ちなみにプルアップを使うべきかプルダウンを使うべきかでは、論理を反転すれば大抵はつかえるが、間違って配線や出力設定をしてしまったときの安全を考えると、電源に抵抗がついているプルアップのほうが安心である。

ラズパイゼロのピンヘッダ装着

Raspberry Pi Zero (W)にはピンヘッダが装着されていません。
ピンヘッダは同梱されているのですが、いままで使っていませんでした。

いよいよラズパイによるIOTを行うために、まずピンヘッダをはんだ付けしました。

ピンヘッダをはんだ付けするときは、まずピンヘッダの位置が正しく決まるように、両端に2つはんだ付けします。
f:id:s51517765:20180127180027j:plain

基板に対してヘッダの黒い部分が隙間無く接しているか確認します。
それから、すべてのはんだ付けをします。
隣のピンと触れていないことはもちろん、所謂富士山型で360°はんだが載っているようにします。
はんだが少ないと、自分の視点の反対側にはんだが回っていないことがあります。
載せすぎるのを怖がるよりは、少し多めに載せて、吸着線で吸い取るようにすると綺麗にできます。
f:id:s51517765:20180127180412j:plain

ところで。いつもはんだ付けをするときに上から覗き込みながら反対側を確認しながら行っていると、はんだの煙がわずらわしいです。
ここで、こんなものを作りました。
f:id:s51517765:20180127180643j:plain

↓のファンにスイッチと電源をつけて吸い込みます。

※こちらの商品でなくても、似たような数百円のものでよいと思います。

電源は余っているACアダプターを転用しました。

Before
f:id:s51517765:20180127181220j:plain

After
f:id:s51517765:20180127181247j:plain

写真では分かり難いですけど、全然違います。
風は吹き付けるのではなく、引き込むようにするのがポイントです。

C#とPythonの基本文法比較

C言語から初めて、C#(VC#)、Python3と学習を進めてきたが、ここでPythonのすばらしさを基本文法を整理することで見ていこうと思う。
以下ではC#とPyhtonの比較として整理するが、ここで例示するC#の文法はほとんどCと同じである。
また、C#Windowsでしか動作しないがPythonWindowsLinuxMacなどで動作するクロスプラットフォームな言語である。


※表記上、"インデント"は"____"で表す。

変数の定義と代入

C# Python3
int a=1; a=1
int b;
b=1;
定義のみは不可
定義した型と異なる値は代入できない c=1
c="1"

C#では変数は型定義と代入が必要だが、Pythonでは代入によって型が決定される

数値から文字列への変換

C# Python3
int a=1;
string b;
b=a.ToString();
a=1
b=str(a)

文字列から数値への変換

C# Python3
string b="1";
int a;
a=(int)b;
b="1"
a=int(b)

演算子

C# Python3
c=a+b; c=a+b
c=a-b; c=a-b
c=a/b; c=a/b
c=a*b; c=a*b
c=a%b; c=a%b
c=Math.Pow(a, b); c=a**b

C#ではべき乗はMathクラスのインポートが必要

文字列の置換

C# Python3
line = line.Replace("before", "after"); line=line.replace("before","after")

置換後の文字列を””(blank)にすると削除できる。

for文

C# Python3
for (int n = 1; n < 40; n++)
____ {xxxx
________}
for i in range(40):
____xxxxx

C#はfor文を”()”でくくり、中身は”{}”でくくる
Pythonはfor文は”:”で終わり改行とインデントが必須

if文

C# Python3
if (value == 1)
____{xxxx
____}
if value == 1:
____xxxxx

forと考え方は大体同じ、whileも同様

論理演算子

C# Python3
if (a==b &&c==d) if a==b and c==d:
if (a==b || c==d) if a==b or c==d:

Python の"and" , "or" のような書き方はVBに似ている

インクリメント(デクリメント)

C# Python3
n=n+1; n=n+1
n++; なし
n+=1; n+=1

デクリメントも同様

コンソール出力

C# Python3
Console.WriteLine("Hello C#"); print("Hello Python3")

C#ではsystemクラスのインポートが必要

配列(リスト)

C# Python3
string[] array = new string[4];
array[0]="a";
array[1]="b";
array[2]="c";
array[3]="d";
array=["a","b","c","d"]
異なる型は一つの配列に代入できない array=["aa",1,"cc",3]

Pythonでは型の混在したリストが可能
C#ではサイズを変更できる”リスト”とサイズを変更できない”配列”は明確に区別されるが、Pythonではサイズを変更できない”配列”は無い

スライス

C# Python3
string sample = text.Substring(start,length); sample=text[start:length]

※スライスとは要素の部分的取得をすること

例外処理

C# Python3
try
____{ xxxx }
catch
____{ MessageBox.Show("Error!!") }
finally
____{ MessageBox.Show("Either Error or Not!") }
try:
____xxxxx
except:
____print("Error!")
finally:
____print("Either Error or Not!")

ファイル読み込み

C# Python3
using System.IO;
string[] line = System.IO.File.ReadAllLines("xxxx.txt");
//一行ごとに配列に格納
f = open('xxxx.txt', 'r', encoding='utf')
list = f.readlines()
using System.IO;
string line;
StreamReader sr = new StreamReader("xxxx.txt");
while ((line=sr.ReadLine()) != null)
____{
// ファイルの末尾まで繰り返す
________}
file = open(’xxxx.txt', 'r', encoding='utf')
line = file.readline()
while line != "":
____line = file.readline()

Pythonでは読み込みは”r”を指定

ファイル書き込み

C# Python3
using System.IO;
using (StreamWriter writer = new StreamWriter("xxxx.txt", true, System.Text.Encoding.GetEncoding("utf-8")))
____{
________writer.Write("yyyy");
____}
file = open(’xxxx.txt', 'a', encoding='utf')
file.write("yyyy")

追記指定はC#では"true"、Pythonでは”a”。新規(上書)はC#では"false"、Pythonでは"w"とする。

参考資料

例外と戦う

f:id:s51517765:20180107184345j:plain
ラズパイでTwitterbotを運用していますが、稀に例外(exception)が発生しています。
とりあえずは、↓のようにまるごとMainの中身にTry、exceptをつけて止まってしまうのを防止していました。

if __name__ == '__main__':
    while(True):
        tdatetime = dt.now()
        HH = int(tdatetime.strftime('%H'))  # 時刻
        MM = int(tdatetime.strftime('%M'))

        try:
       if ○○○○○○
           #時刻をもとにタイマー実行するメインの処理

        except :
            print("----------------------------------")
            print("Parent Error!",str(HH)+":"+str(MM))
            print("----------------------------------")

しかし、これだと何が起こって例外が起きているのかわかりません。
そこで、例外の中身を表示するようにしました。

        except Exception as e:
            print("----------------------------------")
            print("Parent Error!",str(HH)+":"+str(MM))
            print(e)
            print("----------------------------------")

例外が発生したら、例外にException as eという名前を付けてprint()します。
これで例外の原因がわかるはずです。

またラズパイでは、”nohup”を指定してプログラムを起動しておくと、
nohup.outというテキストファイルが作成され、このなかにprint()の内容が記録されていきます。

$ nohup python3 xxx.py bg

これであとからnohup.outを解析して、例外の原因を調べることができます。

これを仕込んで2週間。結果がわかりました。
発生していた例外は↓のようなものでした。

local variable 'word3' referenced before assignment

word3マルコフ連鎖を作成するときに、連続して4つとった4番目の単語ですが、これが代入される前に参照されたというものです。
一つ目に選択された文が4個未満の単語から生成されていると、word3は未代入になってしまうための例外でした。

とりあえず、短すぎるTweetは削除することで対応。

Seleniumでブラウザの制御

seleniumを使う目的は、beautifulsoupがページの情報を取得するだけであるので<a>のようなLinkが設定されていないとLink先の情報が取得できないのに対し、ボタンクリックやフォーム入力によって次のページに遷移するような形のページに対して利用できるという点になるのだと思います。

ここではWindows10での設定と簡易動作までを説明します。

まずはPhantomJSのインストールとパスの設定をします。
パスの設定とはpythonとPhantomJSを紐づけることです。

PhantomJS | PhantomJS
からPhantomJSをダウンロードします。特に"ダマシ"はないので画面に従ってDownloadできると思います。
記事執筆時点ではv2.1.1です。 (phantomjs-2.1.1-windows.zip)
f:id:s51517765:20180107134758j:plain

ダウンロードしたzipを解凍すると、
\Desktop\phantomjs-2.1.1-windows.zip\phantomjs-2.1.1-windows\bin
の中にPhantomjs.exeがあります。

ぐぐるWindowsでのインストール(主に、pythonからのpathを通す方法について)が出てきますが、うまくいきません。
gist.github.com
pathを通すというのは、要するにpythonからモジュールとしphantomJS(以下PJS)をインポートするにあたって、このPJS本体がどこにあるかを設定することなのですが、なにがどう違うのか?よくわかりません。

そこで、逆転の発想をしました。
python(私の環境ではanaconda)にPJSの場所を設定するのではなく、anacondaが知っている場所にPJSを入れたらいいのではないか?
結果、うまくいきました。

anacondaのrootはanaconda promptを起動したときDefoultで立ち上がる場所です。ここにPJSを入れようと思います。
f:id:s51517765:20180107135658j:plain
ここでは、

C:\ProgramData\Anaconda3

です。
これをwindowsエクスプローラで開き、実行ファイルをコピーします。
(windows10ではこのあたりのフォルダは隠しフォルダになっています。)

コピーできたら、anaconda promptで↓のように実行してphantomjsのversion情報が取得できればこれで設定はOKです。

$phantomjs -v

次に実際にPJSをつかってブラウザの操作をしてみます。
twitterのホームを開き、ログインボタンをクリックしてみます。
PJSはヘッドレスブラウザといって、コマンドで操作するために特化されたブラウザで、よって画面描画しないものです。
これでは、うまく動いたのかどうかが判断できないので、スクリーンショットをとることで動作確認します。
ログインボタンは、browser.find_elements_by_link_text("###")で指定します。

※<code></code>でかこむとグレーの四角でハイライトされる。「code記法」と呼ばれるもの。
本来は<>を半角で書くが、半角で書くとタグとみなされるので説明できない。

おもなCSSセレクタについては↓に例が載っている。
qiita.com
qiita.com

from selenium import webdriver
browser = webdriver.PhantomJS()

url = "https://twitter.com/"
browser.get(url)
browser.save_screenshot("twitter.png") #スクリーンショット
login = browser.find_elements_by_link_text('ログイン') #Linkのテキストを探す
login[0].click() #Linkを踏む
browser.save_screenshot("login.png") #スクリーンショット

↓で例題にもなっているのでfirefoxも試してみた。

firefox用のselenium driverは↓
github.com
これも同様に、実行ファイルを

C:\ProgramData\Anaconda3

にコピーすればよい。(firefox https://www.mozilla.org/ja/firefox/new/ がインストールされている前提)

firefoxを使う場合は上のコードでブラウザ指定を書き換える。

browser = webdriver.Firefox()

firefoxの場合は画面がでるブラウザなので、画面が立ち上がってくる。