pikesaku’s blog

個人的なプログラム勉強メモです。記載内容について一切の責任は持ちません。

Webスクリピングサンプルコード

大変だった。。。待機が重要。

# -*- coding: utf-8 -*-

from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import time

RETRY = 5

def select_by_xpath(wd, xpath, text):
    # nameで指定
    em = wd.find_element_by_xpath(xpath)
    # selectタグの値をテキストで指定
    em = Select(em)
    # optionsでselect選択肢を取得可能
    #for i in em.options:
    #    print(i.text)
    em.select_by_visible_text(text)


def click_by_xpath(wd, xpath):
    # elementがクリック可能になるまで待つ必要あり。そうしないと以下エラーが不定期に出る。
    # selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
    # http://www.seleniumqref.com/api/python/conditions/Python_presence_of_element_located.html
    # 結局waitとループで対処
    cnt = 1
    wait = WebDriverWait(wd, 30)
    while True:
        if cnt >= RETRY:
            print('Error: click_by_xpath failed: ' + wpath)
            exit()
        try:
            em = wait.until(expected_conditions.visibility_of_element_located((By.XPATH, xpath)))
            em = wait.until(expected_conditions.element_to_be_clickable((By.XPATH, xpath)))
            em.click()
            break
        except:
            print('Warn: click_by_xpath retryed: ' + xpath)
            cnt += 1
            time.sleep(0.5)


def click_by_xpath_with_scroll(wd, xpath):
    # 読み込み失敗したらスクロールダウン
    cnt = 1
    wait = WebDriverWait(wd, 30)
    while True:
        if cnt >= RETRY:
            print('Error: click_by_xpath_with_scroll failed: ' + wpath)
            exit()
        try:
            em = wait.until(expected_conditions.visibility_of_element_located((By.XPATH, xpath)))
            em = wait.until(expected_conditions.element_to_be_clickable((By.XPATH, xpath)))
            em.click()
            break
        except:
            print('Warn: click_by_xpath_with_scroll retryed: ' + xpath)
            wd.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            cnt += 1
            time.sleep(0.5)


def get_last_page(wd):
    time.sleep(5)
    xpath = '//a[@ng-click="setCurrent(pageNumber)" and @class="ng-binding"]'
    em = wd.find_elements_by_xpath(xpath)
    return em[-1].text


def scraping():
    options = webdriver.ChromeOptions()
#    options.add_argument('--headless')
#    options.add_argument('--window-size=1280,1280')
    wd = webdriver.Chrome(options=options)
    wd.implicitly_wait(20)
    wd.get('https://access.redhat.com/errata')

    # プロダクト選択
    # ボタンを押してからでないと、選択肢を選べない
    click_by_xpath(wd, '//div[@class="col-sm-3"]/div[@class="dropdown more-wrapper"]/button[@id="dropdown-other-filters-portal-product"]')
    click_by_xpath(wd, '//li[text()="Red Hat Enterprise Linux"]')

    # OS選択
    click_by_xpath(wd, '//div[@class="col-sm-3 chosen-wrapper" and contains(@ng-hide, "portal_product_variant")]/div[@class="dropdown more-wrapper"]/button[@id="dropdown-other-filters-portal-product"]')
    click_by_xpath(wd, '//li[text()="Red Hat Enterprise Linux Server"]')

    # バージョン選択
    click_by_xpath(wd, '//div[@class="col-sm-3 chosen-wrapper" and contains(@ng-hide, "portal_product_version")]/div[@class="dropdown more-wrapper"]/button[@id="dropdown-other-filters-portal-product"]')
    click_by_xpath(wd, '//li[text()="6"]')

    # アーキテクチャ選択
    click_by_xpath(wd, '//div[@class="col-sm-3 chosen-wrapper" and contains(@ng-hide, "portal_architecture")]/div[@class="dropdown more-wrapper"]/button[@id="dropdown-other-filters-portal-product"]')
    click_by_xpath(wd, '//li[text()="x86_64"]')

    # 表示行を最大に
    select_by_xpath(wd, '//select[@ng-model="pageSize"]', '100')

    # 最後のページ数を取得
    last_page = get_last_page(wd)

    # テーブル情報取得
    for i in range(int(last_page)):
        i = i + 1
        xpath = '//a[@ng-click="setCurrent(pageNumber)" and @class="ng-binding" and text()="' + str(i) + '"]'
        click_by_xpath_with_scroll(wd, xpath)
        html = wd.page_source
        bs = BeautifulSoup(html, 'html.parser')
        table = bs.findAll('table', {'id': 'DataTables_Table_0'})[0]
        rows = table.findAll("tr")
        for row in rows:
            for cell in row.findAll('span', {"class": "cell-content"}):
                print(cell.get_text())
        time.sleep(1)
    wd.quit()


if __name__ == '__main__':
    scraping()

Xpathのパス指定メモ

パス省略(//)は複数使ってもOK?

サンプルコード

# -*- coding: utf-8 -*-
from lxml import html

a = """
<html>
    <head>
        <title>Sample</title>
    </head>
    <body>
        <div>
            <h1 class="title1">Sample #01</h1>
        </div>
        <div>
            <h1 class="title2">Sample #02</h1>
        </div>
        <div>
            <h1 class="title3">Sample #03</h1>
        </div>
    </body>
</html>
"""

print('### フルパス指定')
a = html.fromstring(a)
b = a.xpath("/html/body/div/h1")
for i in b:
  print(i.text)
print('')


print('### フルパス指定+条件付き')
b = a.xpath("/html/body/div/h1[@class='title1']")
for i in b:
  print(i.text)
print('')


print('### 省略パス// 指定')
b = a.xpath("//h1[@class='title1']")
for i in b:
  print(i.text)
print('')


print('### 2重省略パス// 指定')
b = a.xpath("//body//h1[@class='title1']")
for i in b:
  print(i.text)
print('')


print('### ワイルドカード指定')
b = a.xpath("/html/*/*/h1[@class='title1']")
for i in b:
  print(i.text)
print('')


print('### 間違った省略パス指定')
b = a.xpath("//body/h1[@class='title1']")
for i in b:
  print(i.text)
print('')


print('### 間違ったワイルドカード指定')
b = a.xpath("/html/*/h1[@class='title1']")
for i in b:
  print(i.text)

実行結果

### フルパス指定
Sample #01
Sample #02
Sample #03

### フルパス指定+条件付き
Sample #01

### 省略パス// 指定
Sample #01

### 2重省略パス// 指定
Sample #01

### ワイルドカード指定
Sample #01

### 間違った省略パス指定

### 間違ったワイルドカード指定

Seleniumメモ

要素特定のポイント

大きく2つ方法があり。

①パブリックメソッド
find_element(s)_by_XXXXX(〜)
②プライベートメソッド
find_element(s)(By.XXXXX, 〜)

参考引用

f:id:pikesaku:20190324002551p:plain

f:id:pikesaku:20190414022741p:plain
上記のパブリックメソッドとは別に、ページオブジェクト内のロケータで便利なプライベートメソッドが2つあります。これらは、find_elementおよびfind_elementsの2つのプライベートメソッドです。

xpathは重要。xpahtはXMLのデータ特定言語。ID等で対象を特定できない場合に便利。

参考引用

<html>
 <body>
  <form id="loginForm">
   <input name="username" type="text" />
   <input name="password" type="password" />
   <input name="continue" type="submit" value="Login" />
   <input name="continue" type="button" value="Clear" />
  </form>
</body>
<html>

上記のid="loginForm"を指定したい時は以下記述が可能。"/"はツリー指定。"//"はツリー省略記述。

login_form = driver.find_element_by_xpath("/html/body/form[1]")
login_form = driver.find_element_by_xpath("//form[1]")
login_form = driver.find_element_by_xpath("//form[@id='loginForm']")

サンプルコード

JVN iPediaから2018年度のレッドハットのCVSS V3で深刻度が9以上の脆弱性出力

# -*- coding: utf-8 -*-

from selenium import webdriver
from selenium.webdriver.support.ui import Select
import time


def check_by_xpath(wd, xpath):
    em = wd.find_element_by_xpath(xpath)
    em.click()


def select_by_text(wd, select_tag, text):
    # nameで指定
    em = wd.find_element_by_name(select_tag)
    # selectタグの値をテキストで指定
    em = Select(em)
    # optionsでselect選択肢を取得可能
    #for i in em.options:
    #    print(i.text)
    em.select_by_visible_text(text)


def click_by_link_text(wd, text):
    # リンクテキストで指定
    em = wd.find_element_by_link_text(text)
    em.click()


def click_by_name(wd, name):
    # リンクテキストで指定
    em = wd.find_element_by_name(name)
    em.click()


def scraping():
    options = webdriver.ChromeOptions()
#    options.add_argument('--headless')
    wd = webdriver.Chrome(options=options)
    wd.implicitly_wait(20)
    wd.get('https://jvndb.jvn.jp/')
    click_by_link_text(wd, '詳細検索')
    select_by_text(wd, 'vendor', 'レッドハット')
    select_by_text(wd, 'product', 'Red Hat Enterprise Linux Server')
    select_by_text(wd, 'datePublicFromYear', '2018')
    select_by_text(wd, 'datePublicFromMonth', '04')
    select_by_text(wd, 'datePublicToYear', '2019')
    select_by_text(wd, 'datePublicToMonth', '03')
    check_by_xpath(wd, '//input[@class=' + '"cvss_v3" and @value="01"]')
    click_by_name(wd, 'search')
    time.sleep(10)
    wd.quit()


if __name__ == '__main__':
    scraping()

Metabase使い方メモ

メモ

SQL書いた場合、ドリルダウンできない?

サンプルグラフにSQLで条件追加したらドリルダウン不可

円グラフはドリグダウンできる時と出来ない時の違いがわからない。

サンプルは円グラフでドリルダウン可能
自分で作った場合はNG(SQL未使用)

英語UIでないと列等の詳細設定が動かない?

ダッシュボード公開可能

metabase.com
管理者画面で有効化→ユーザー画面のダッシュボード設定で"Sharing and Embedded"でリンク入手

Pydnsblを使う

複数のDNSBLに問い合わせしてくれる。
 

ソース

from pydnsbl import DNSBLChecker

chk = DNSBLChecker()
res = chk.check_ip('1.1.1.1')

print("Result")
print(res.blacklisted)
print("")
print("Providers")
print(res.detected_by)

結果

Result
True

Providers
{'dyna.spamrats.com': ['unknown']}

 
タイムアウトDNSBLが多く遅い。解消は以下

ソース

from pydnsbl import DNSBLChecker, providers

BASE_PROVIDERS = [
    'b.barracudacentral.org',
    'zen.spamhaus.org',
]
BASE_PROVIDERS = [providers.Provider(host) for host in BASE_PROVIDERS]
print(BASE_PROVIDERS)

chk = DNSBLChecker(providers = BASE_PROVIDERS)
res = chk.check_ip('8.8.8.8')

print("Result")
print(res.blacklisted)
print("")
print("Providers")
print(res.detected_by)

結果

[<Provider: b.barracudacentral.org>, <Provider: zen.spamhaus.org>]
Result
False

Providers
{}

Pythonのクラスについて

はじめに

Pythonの仕様なので、こういうものだと覚える!
 

参照

Amazon CAPTCHA
 

クラスの定義は以下の形

class クラス名:
    def メソッド名(self, ...):
        ...
    def メソッド名(self, ...):
        ...

 

インスタンス生成

ソース

class myclass:
    def __init__(self):
        pass

a = myclass()
print(type(a))

実行結果

<class '__main__.myclass'>

 

クラスオブジェクト

クラスもオブジェクト
クラスを定義するとクラスオブジェクトが生成される
クラスオブジェクトの代入可能
ソース

class myclass:
    def __init__(self):
        pass

a = myclass
b = a()
print(type(b))

実行結果

<class '__main__.myclass'>

 

属性指定

class myclass:
    def __init__(self):
        self.hoge = "hoge"

a = myclass()
print(a.hoge)

実行結果

hoge

 

属性削除

ソース

class myclass:
    def __init__(self):
        self.hoge = "hoge"

a = myclass()
print(a.hoge)
del a.hoge
print(a.hoge)

実行結果

Traceback (most recent call last):
  File "/Users/pike/PycharmProjects/Study/c.py", line 8, in <module>
    print(a.hoge)
AttributeError: 'myclass' object has no attribute 'hoge'
hoge

 

メソッド

クラスに定義された関数
selfを引数として持つ。selfは生成されたインスタンス自身
ソース

class A:
    def m1(self):
        print("A.m1")
        self.m2("m1 messeage")
    def m2(self, mes):
        print("A.m2")
        print(mes)

a = A()
a.m1()

実行結果

A.m1
A.m2
m1 messeage

 

コンストラク

インスタンス生成時に呼び出されるメソッド。名前は"__init__"にする
ソース

class A:
    def __init__(self):
        print("A.m1")

a = A()

実行結果

A.m1

 

デストラク

インスタンス削除時に呼び出されるメソッド。名前は"__del__"にする
ソース

class A:
    def __del__(self):
        print("del A")

a = A()
del a

実行結果

del A

 

継承

既存のクラスの派生クラスの定義
定義時に既存クラスを指定。これを基底クラスと呼ぶ
基底クラス未指定の場合、objectクラスを継承
super()で基底クラスのメソッド呼び出しが可能
ソース

class A:
    def __init__(self, arg1):
        self.atr1 = arg1
    def m1(self):
        print("m1")

class B(A):
    def __init__(self, arg1, arg2):
        super().__init__(arg1)
        self.atr2 = arg2
    def m2(self):
        print("m2")

i = B("hoge", "fuga")
print(i.atr1)
print(i.atr2)
i.m1()
i.m2()

実行結果

hoge
fuga
m1
m2

 

メソッドのオーバーライド

継承クラス定義と同名のメソッドを定義するとオーバーライドする
ソース

class K():
    def m(self):
        print("m by K")
    def m2(self):
        print("m2 by K")


class A(K):
    def m(self):
        print("m by A")

a = A()
a.m()
a.m2()

実行結果

m by A
m2 by K

 

多重継承

複数クラスの継承
ソース

class A:
    def m1(self):
        print("m1")

class B:
    def m2(self):
        print("m2")

class C(A, B):
    def m3(self):
        print("m3")


a = C()
a.m1()
a.m2()
a.m3()

実行結果

m1
m2
m3

 
メソッドの検索順が確定できない場合エラー
ソース

class A:
    def m1(self):
        print("m1")

class B():
    def m2(self):
        print("m2")

class C(A, B):
    def m3(self):
        print("m3")

class D(B, A):
    def m4(self):
        print("m4")

class E(C, D):
    def m5(self):
        print("m5")

実行結果

Traceback (most recent call last):
  File "/Users/pike/PycharmProjects/Study/a.py", line 17, in <module>
    class E(C, D):
TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B

※CはA→B、DはB→A順で検索。EはC→D順で検索するが、CとDが矛盾する為、エラー(内部実装の話。こういうものだと覚える!)
 
メソッドの検索順はmroメソッドで取得可能
ソース

class A:
    def m1(self):
        print("m1")

class B():
    def m2(self):
        print("m2")

class C(A, B):
    def m3(self):
        print("m3")

a = C()
print(C.mro())

実行結果

[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

 
Cのインスタンスmroメソッドを参照すると未定義エラーとなる。
ソース

class A:
    def m1(self):
        print("m1")

class B():
    def m2(self):
        print("m2")

class C(A, B):
    def m3(self):
        print("m3")

a = C()
print(a.mro())

実行結果

Traceback (most recent call last):
  File "/Users/pike/PycharmProjects/Study/a.py", line 14, in <module>
    print(a.mro())
AttributeError: 'C' object has no attribute 'mro'

 

インスタンスアロケータ

__new__メソッドのこと。__init__と似てるが、こりゃ難しい。以下URLも参考。
__new__と__init__とメタクラスと - Qiita
インスタンスアロケータ - Python学習講座
 
__new__の第一引数はクラスオブジェクト(cls)、__new__はインスタンス(self)
ソース

class A():
    def __new__(cls):
        print("A __new__")
        print(cls)

class B():
    def __init__(self):
        print("B __init__")
        print(self)

a = A()
aa = A
print(aa)

print("")
b = B()
print(b)

実行結果

A __new__
<class '__main__.A'>
<class '__main__.A'>

B __init__
<__main__.B object at 0x107abcc18>
<__main__.B object at 0x107abcc18>

 
__new__も__init__もインスタンス生成時に呼び出される
__new__の方が先に呼び出される。__new__の戻り値がsuper().__new__(cls)の場合のみ、__init__が実行される
ソース

class A():
    def __new__(cls):
        print("A __new__")
        return super().__new__(cls)
    def __init__(self):
        print("A __init__")

class B():
    def __new__(cls):
        print("B __new__")
    def __init__(self):
        print("B __init__")

a = A()
b = B()

実行結果

A __new__
A __init__
B __new__

 
return super().__new__(cls)とは?→クラスAのインスタンスである!
参考書籍にも以下記載あり
__new__はインスタンス生成する為に呼び出される。戻り値がクラスのインスタンスなら、そのインスタンスの__init__メソッドが実行される
やっと理解できた!
ソース

class A():
    print("A super().__new__(cls)")
    def __new__(cls):
        print(super().__new__(cls))
        return super().__new__(cls)
    def __init__(self):
        self.hoge = "A"

a = A()
print(a)

実行結果

A super().__new__(cls)
<__main__.A object at 0x107595cc0>
<__main__.A object at 0x107595cc0>

 
インスタンスアロケータの用途は?→参考書籍やURLを読み、以下と理解した
イミュータブル(変更不可)なクラス(int等)を継承しても、イミュータブルだからメソッドや属性を__init__ではオーバーライドできない(エラーになる)。ただ__new__ならできる。
ソース

class A(int):
    def __init__(self):
        self.hoge = "A"

a = A()
print("test")

実行結果

test

 
しかし以下コードを実行したが。。。エラーにならない。。。理解を誤ってるのだろう
ソース

class A(int):
    def __init__(self):
        self.real = 2

a = A()

実行結果

 
こういうことかも
__init__はself(インスタンス)を引数にする=既にインスタンス生成後の処理→なのでインスタンス生成前の処理をオーバーライドできない。→__new__ならできる!
intを継承したクラスでインスタンス生を成し、その値が常に2になるクラスを実装する場合
__init__ではできない。ダメ元で以下コードで試す
ソース

"""
# intの__init__メソッドのソースコードの内容。passしてるだけ。

def __init__(self, x, base=10):
    pass
"""

class A(int):
    def __init__(self, x, base=10):
        x = "2"
        pass

a = A("1")
print(a)

実行結果

1

 
__new__でインスタンス生成時の引数を2に変更!
ソース

class A(int):
    def __new__(cls, s):
        s = "2"
        return super().__new__(cls, s)

a = A("1")
print(a)

実行結果

2