Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
7
Help us understand the problem. What is going on with this article?
@akihiro199630

Pythonクローリング&スクレイピング 第2章まとめ

More than 1 year has passed since last update.

前回まで

前回は環境構築やunixコマンドでのクローリング・スクレイピングを行った
見出しは本文に準ずる

第2章Pythonではじめるクローリング・スクレイピング

Pythonを使うメリット

標準ライブラリもサードパーティライブラリも充実していて、データ分析に役立つライブラリもいっぱい揃っているよ!という話

Python3.7のインストール

Ubuntu 18.04ではPython3.6.8がインストールされているが、本書では3.7の新機能を使うとのことでインストール
$ sudo apt install -y python3.7 python3.7-venv libpython3.7-dev python3-pip build-essential
apt install のあとの -y は 「問い合わせがあった場合はすべて「y」と答える」のオプション
【 apt-get 】 パッケージを取得してインストール/アップデートする

仮想環境(venv)の使用

Pythonでは同じライブラリの別バージョンを同一環境でインストール出来ないため、ライブラリの依存関係による誤作動を防ぐために仮想環境を利用する。Pythonでは標準の仮想環境としてvenvがある。

$ python3.7 -m venv 仮想環境名 でカレントディレクトリに仮想環境名と同じ名前のフォルダが作成される。-mオプションは指定したモジュールをスクリプトとして実行する

注釈としてVirtualBoxの仮想マシン上のubuntuで上記コマンドを実行した場合の不具合が書かれてあったが、WSL上のubuntuでの場合は問題なく動いた

$ . 仮想環境名/bin/activate で仮想環境を有効化
.コマンドは引数で指定したファイルを読み込み、現在のシェルで実行する
仮想環境が有効化されるとプロンプトの先頭に
(仮想環境名)ユーザー名$と表示される
(仮想環境名) $ deactivateで仮想環境を無効化

Pythonの基礎知識

この辺は自分では理解出来ていると思うので流し読み
でもfを先頭に付けた文字列で
f"{変数名} hogehoge" みたいに変数内の値を文字列に置き換えられるというのは知らなかった(3.6から導入された模様)

Webページを取得する

Requests

サードパーティライブラリのRequestsを使ってWebページを取得する
(仮想環境名) $ pip install requests仮想環境内にインストール

import requestsしてr = requests.get('<URL>')でWebページを取得
get()関数はHTTPメソッドのGETに対応している
get()関数はキーワード引数を指定することで、HTTPヘッダーをリクエストに追加,Basic認証を指定,URLパラメータを指定といったことができる

その他post(),put(),patch(),delete(),head(),options()関数がそれぞれ関数名大文字のHTTPメソッドに対応している。

  • r.status_code →ステータスコードを取得
  • r.headers['content-type']→HTTPヘッダーの辞書を取得
  • r.encoding→エンコーディングを取得
  • r.text→str型でレスポンスボディ(HTMLのソース)を表示
  • r.content→ ↑のbytes型版
  • r.json()→JSON形式で取得

複数のページを連続してクローリングするときはSessionオブジェクトを利用すると良い
設定を複数のリクエストで使いまわしたり、HTTP Keep-Aliveという接続方式を使うことで相手側のサーバーの負担を軽減することができる
get()、post()などのメソッドはrequets.get()と同じように使える
s = requests.Session()
r = s.get('<URL>')

文字コードの扱い

Webページのエンコーディングを取得・推定する3つの方法

1.HTTPレスポンスのContent-Typeヘッダーからそのページのエンコーディングを知ることができる

url = sys.argv[1] # 第1引数からURLを取得する。
r = requests.get(url) # URLで指定したWebページを取得する。
print(f'encoding: {r.encoding}', file=sys.stderr) # エンコーディングを標準エラー出力に出力する。
print(r.text) # デコードしたレスポンスボディを標準出力に出力する。

2.r.encoding = r.apparent_encodingでバイト列の特徴からそのWebページのエンコーディングを推定した結果を得ることができる

3.HTMLのmetaタグで指定されたエンコーディングを取得する
RequestsはHTTP通信のためのライブラリでHTMLなどのコンテンツに依存したエンコーディングを取得することが出来ないため、正規表現を使ってmetaタグのエンコーディングの指定を取り出す

Webページからデータを抜き出す

1.正規表現(reモジュール)を使ったスクレイピング

コードは割愛
re.findall()を使って抜き出したい部分(本文では出版社のサイトの書籍のタイトルと書籍ページのURL)を取得して、タグをスペースに置き換えたり、取り除いたりといったところ

2.lxmlを使ったスクレイピング

lxml → PythonのC拡張モジュール。HTMLのパースにはlxml.htmlを使う。

パースとは、文法に従って分析する、品詞を記述する、構文解析する、などの意味を持つ英単語。
パースとは - IT用語辞典

  • XPathとCSSセレクター
    • XPath →XMLで特定の要素を指定するための言語。多機能で細かな指定ができる。
      • 例://body//h1 body要素の子孫のh1
    • CSSセレクター→CSSで装飾する要素を指定するための表記方法
      • 例:body h1 body要素の子孫のh1
    • Chromeの開発者ツールでは選択した要素のXPathとCSSセレクターをコピーできる
      • このとき、tableタグ直下にtrタグがある場合、ブラウザがこの2つの間にtbodyを補完してしまうのでそのままコピーするとlxmlで思うように要素を取れなくなるので注意
  • lxmlではCSSセレクターをXpathに変換して実行する
lxml.htmlのメソッド色々
  • lxml.html.parse('HTMLファイル')→HTMLファイルをパースする。URLやファイルオブジェクトを指定することもできる。
    • ↑のパース結果は _ElementTreeオブジェクトとして得られる
    • ↑ にgetroot()メソッドでHtmlElementオブジェクトが得られる
  • fromstring()で文字列をパースできる
    • lxml.html.fromstring("""<html> ....... </html>""")
  • HtmlElementのxpath()/cssselect()メソッドでXPath/CSSセレクタにマッチする要素のリストを取得できる
    • リストの各要素(Elementオブジェクト)はtag属性でタグの名前を,textで要素のテキストを、get("属性名(idとかclassとか)")メソッドでHTMLタグの属性の値、attrib属性で全属性を表すdict-likeな要素, getparent()メソッドで親要素, tail属性で要素の直後のテキストを取得できる。text_content()メソッドで要素内のすべてのテキストを結合した文字列を得られる

データをファイルに保存する

CSV形式での保存

hogehoge_csv.py
print('hoge,hugo,hego') #ヘッダー
#各行の要素
print('aaa,bbb,ccc')
print(','.join(['aaaa','bbbb','cccc']))

みたいなファイルを作って
$python hogehoge.py > hogehoge.csv でCSV形式で保存できる
または

hogehoge2.py
import csv

with open('hogehoge_csv2.csv', 'w', newline='') as f:
writer = csv.writer(f) #引数はファイルオブジェクト
writer.writerow(['hoge', 'hugo', 'huga']) #ヘッダーを出力
writer.writerows([
    [1, 'aaa', 111],
    [2, 'bbb', 222],
    [3, 'ccc', 333],
    [4, 'ddd', 444],
    [5, 'eee', 555],
])

辞書形式では

hogehoge_csv3.py
import csv
with open('hogehoge3.csv', 'w', newline='') as f:
    # 第1引数にファイルオブジェクトを、第2引数にフィールド名のリストを指定する。
    writer = csv.DictWriter(f, ['id', 'name', 'age'])
    writer.writeheader() # 1行目のヘッダーを出力する。
    # writerows()で複数の行を一度に出力する。引数は辞書のリスト。
    writer.writerows([
        {'id': 1, 'name': 'aaa', 'age': 11},
        {'id': 2, 'name': 'bbb', 'age': 12},
        {'id': 3, 'name': 'bbb', 'age': 13},
        {'id': 4, 'name': 'bbb', 'age': 14},
    ])

みたいな方法で出力できる

JSON形式での保存

hogehoge_json.py
import json
hogehoge = [
    {'hoge': 1, 'huga': 'あああ', 'huge': 111},
    {'hoge': 2, 'huga': 'いいい', 'huge': 222},
    {'hoge': 3, 'huga': 'ううう', 'huge': 333},
]
print(json.dumps(hogehoge, ensure_ascii=False, indent=2)) #後ろ2つのオプションで読みやすく出力できる

を実行するとJSON形式の文字列が表示される
保存する場合には最後の行を
with open('hogehoge.json', 'w') as f:
json.dump(cities, f)
とする

Pythonによるスクレイピングの流れ

サンプルプログラムではここまでの内容をまとめてWebページの取得、スクレイピング、保存をの処理ごとに3つの関数に分けて行っている。
この節に関しては別の記事で自分で書いたプログラムをアウトプットしたい。
ちなみにサンプルプログラムに関しては取得先のURLがなぜか403エラーになり正常に動作しなかった(´・ω:;.:...

URLの基礎知識

URLの構造や絶対URL、相対URLなどについて
Pythonで相対URLを絶対URLに変換するには標準ライブラリurllib.parseのurljoin()関数を使う

(第3章につづく)

7
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
akihiro199630
研修中のエンジニアの卵です

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
7
Help us understand the problem. What is going on with this article?