環境
Linux Ubuntu Xfce
参考
PythonによるWebスクレイピング
Pythonクローリング&スクレイピング-データ収集・解析のための実践開発ガイド
実践 Selenium WebDriver
道具
- Chrome
- Chrome-Driver
- BS4
- 文字や画像の取得時に用いる
- Selenium
- ブラウザ上の操作をするときに用いる
- pandas
- データの結合やファイル出力に用いる
- liblzma-dev : pandasに必要なパッケージなので入れる
sudo curl -sS -o - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add
sudo echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list
sudo apt-get -y update
sudo apt-get -y install google-chrome-stable
sudo apt install chromium-chromedriver liblzma-dev \
&& pip install bs4 selenium pandas
基本
bs4でできること
bs4は様々なメソッドが用意されており、それらのメソッドや正規表現(re)などを駆使すれば取得が不可能なものは存在しない
パースはlxmlを使う
処理が最も早く、最も多くのCSSセレクタがつかえる
html_doc = '<html>...</html>'
soup = BeautifulSoup(html_doc, 'lxml')
実行後に必ずclose,quit
やらないとプロセスの残骸がたまる。
from selenium import webdriver
driver = webdriver.Chrome()
# ドライバーを終了させる
driver.close()
driver.quit()
seleniumで操作してhtmlソースをBS4に渡す
受け渡しが終わったらあとは、BS4で宝探し
options = ChromeOptions()
options.add_argument('--headless') # ウィンドウレスモード
driver = Chrome(options=options)
url = 'https://www.example.com/'
driver.get(url)
# Seleniumの操作開始
...
...
...
# Selenimuの操作終了
html = driver.page_source.encode('utf-8')
soup = BeautifulSoup(html, "lxml")
# BS4の処理開始
...
...
...
# BS4の処理終了
HTMLタグの数が少ないときはfindメソッドは使わない
直接BeautifulSoup
オブジェクトからタグ名で探索する
from bs4 import BeautifulSoup
html_doc = '''
<html>
<head>
<title>hello soup</title>
</head>
<body>
<p class="my-story">my story</p>
</body>
</html>
'''
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.title)
print(soup.title.text)
print(soup.p)
print(soup.p['class'])
print(soup.p.text)
<title>hello soup</title>
hello soup
<p class="my-story">my story</p>
['my-story']
my story
bs4の4つのオブジェクトを知る
BeautfulSoupは、Tag
, NavigableString
, BeautifulSoup
, Comment
の4種類のオブジェクトがある
この中で、よく使うのが、BeautifulSoup
とTag
BeautifulSoupオブジェクトとTagオブジェクト
BeautifulSoup
: HTMLソースをPythonで扱える形式(ツリー構造)に変換する
Tag
: BeautifulSoupオブジェクトに特定のメソッドを使うとTagオブジェクトが生成される
findとfind_allの違いを理解する
BeautifulSoup
オブジェクトにfind
メソッドとfind_all
メソッド使うと何でも探索できるが、的確に探索をするためには、メソッドによって何が生成されるかを知っておく必要がある
メソッドによって生成されるオブジェクト
find
→ bs4.element.Tag
find_all
→ bs4.element.ResultSet
何もみつからないときの返り値
find
→ None
find_all
→ []空のリスト
bs4.element.Tag
find_all
メソッド、BeautifulSoup
メソッド、select
メソッド、以外のbs4のメソッドを使うと生成されると思っておけば良い
from bs4 import BeautifulSoup
html_doc = '''
<html>
<head>
<title>hello soup</title>
</head>
<body>
<p class="my-story">my story</p>
<a class='brother' href='http://example.com/1' id='link1'>リンク1</a>
<a class='brother' href='http://example.com/2' id='link2'>リンク2</a>
<a class='brother' href='http://example.com/3' id='link3'>リンク3</a>
</body>
</html>
'''
soup = BeautifulSoup(html_doc, 'lxml')
print('tag1')
tag1 = soup.find('a')
print(tag1)
print(type(tag1))
print('tag2')
tag2 = soup.a
print(tag2)
print(type(tag2))
bs4.element.ResultSet
find_all
メソッド、BeautifulSoup
メソッド、select
メソッド、を使うと生成される
bs4.element.Tag
がリストにたくさん入っているイメージ(このイメージけっこう大事)
bs4.element.ResultSet = [bs4.element.Tag, bs4.element.Tag, bs4.element.Tag,...]
なので、そのままでは探索できず、リストから取り出してから使う
取り出せば、あとは上記のbs4.element.tag
と同じメソッドが使える
※ メソッドが使えない!ってときは、ほぼほぼ
bs4.element.ResultSet
にbs4.element.Tag
のメソッドを使おうとしている時です
from bs4 import BeautifulSoup
html_doc = '''
<html>
<head>
<title>hello soup</title>
</head>
<body>
<p class="my-story">my story</p>
<a class='brother' href='http://example.com/1' id='link1'>リンク1</a>
<a class='brother' href='http://example.com/2' id='link2'>リンク2</a>
<a class='brother' href='http://example.com/3' id='link3'>リンク3</a>
</body>
</html>
'''
soup = BeautifulSoup(html_doc, 'lxml')
print('tag3')
tag3 = soup.select('a:nth-of-type(2)') # bodyタグ内のaタグの有無で見つける
print(tag3)
print(type(tag3))
print('tag4')
tag4 = soup.select('.link1') # CSSセレクタのクラ
print(tag4)
print(type(tag4))
print('tag5')
tag5 = soup.select('a[href]') # 属性の有無でタグを見つける
print(tag5)
print(type(tag5))
小技
printの出力制限を緩める
デフォルトのままだとデカくなったファイルをprintしようとするとIOPub data rate exceeded.
というエラーになるので無制限に変更
jupyter notebook --generate-config
# 変更前 1000000 → 変更後 1e10
jupyter notebook --NotebookApp.iopub_data_rate_limit=1e10
pickleで高速に読み書きする
バイナリ形式(コード内の'b'はバイナリの意味)で読み書きをするので高速
同じ機能をもったライブラリにjoblib
がありますが、こちらは速度を犠牲にして、ファイルサイズを小さくしたいときに使うと良い
import pickle
example = 'example'
with open('example.pickle', 'wb') as f:
pickle.dump(example, f)
with open('example.pickle', 'rb') as f:
example = pickle.load(f)
文字列以外を読み書きしようとするとエラーがでる問題に対処
bs4オブジェクト
(bs4.BeautifulSoup等)を書き込みしようとすると maximum recursion depth exceeded while pickling an object
というエラーがでてしまうので、string
等に変換してから保存
import pickle
example = 'example'
with open('example.pickle', 'wb') as f:
pickle.dump(str(example), f)
with open('example.pickle', 'rb') as f:
example = BeatitfulSoup(pickle.load(f), 'lxml')
ただ読み込むだけだとstr型
のためbs4で扱うことができない
したがって、読み込み時にbs4
型に変換
上記のやり方でも上手く行かない場合
dict
をdumpしようとすると駄目な場合
こんなときはjson
でdumpするのが吉
import json
with open('example.json', 'w') as f:
json.dump(example, f)
with open('example.json', 'r') as f:
json.load(f)
Jupyter Notebook
セルの幅を最大にする
pandasのDataFrameを見る時に、セル幅がデフォルトだと文字が見切れてしまうので、セル幅を最大になるように設定する
.container { width:100% !important; }
処理時間を計測する
Jupyter
環境下でのみ使用できる%time
を使う
これはJupyter
の組み込みメソッドなのでimport不要
%time example_function()
正規表現
URLのスラッシュの前後の文字を取得する
https://www.example.com/topics/scraping
のscraping
を取得したい時
split
で/
を指定して、一番うしろの要素を取得する
url = 'https://www.example.com/topics/scraping'
print(url.split('/'))
#['https:', '', 'www.example.com', 'topics', 'scraping']
print(url.split('/')[-1])
#scraping
Pandas
Pandas UserWarning: Could not import the lzma module. Your installed Python is incomplete
pandasに必要なパッケージが不足しているときにでるエラー
sudo apt install liblzma-dev
DataFrameのColumnを取り出してリストにする
- Seriesに取り出したいColumnを入れる
- Seriesのtolistメソッドを使う
import pandas as pd
df = pd.DataFrame(index=[1,2,3], {'Column1':data1, 'Column2':'data2', 'Column3':'data3'})
# Column3を取り出してリストにする
col3 = pd.Series(df['Column3']).tolist()
DataFrameの出力結果を左寄せにする
デフォルトは右寄せなので、URLや英語が読みづらい
df.style.set_properties(**{'text-align': 'left'}) # 左寄せ