webスクレイピングを知識0から始めて習得するまで⑤【Scrapyの基本】

2022/1/9 【2日目】

データ分析コンペである程度pythonのコードとかpandasとか使うのに慣れたので、ずっとやりたかったwebスクレイピングを1から勉強していきます。

webスクレイピングができるようになったらやりたいことは、webからTiborの履歴をとってきて、チャートを表示できるサイトを作りたい。データ分析で勉強したpandasとかmatplotも使うことになると思うから丁度いいとおもってる。

Scrapyのコマンド

scrape bench

画像1

benchコマンドは、簡単なベンチマークテストを実行するのに利用します。
2020年M1モデルのMacbook Airでこのくらい。1分間に4000ページぐらいをクロールできる。

clear

これでターミナルの内容をリセットできる。

基本的にはVS Codeではなく、ターミナルを使っていくらしい。

プロジェクトの作成

# option+command+Mで最前面のウインドウを最小化できる。最小化するとDockに入る。

mkdir projects
ls

画像2

これでprojectsディレクトリを作成して、カレントディレクトリの中身を確認できる。

cd projects

projectsでディレクトリに移動して

scrapy startproject qiita

画像3

画像4

画像5

qiitaプロジェクトに必要なディレクトリ、ファイルが自動的に作成される。次のSpiderの作成に必要なコマンドの説明も表示された。

scrape.cfg(コンフィグファイル)はSpiderの作成やディプロイに必要な設定ファイル。

画像6

items.pyをVS Codeで開く。スクレイピングで取得したデータを格納する入れ物のようなもの。アイテムと呼ばれている。フィールドはこのように提示していて、あらかじめ提示していないと格納ができない。

2022/1/13【3日目】

Spiderの作成

ScrapyでのSpiderの作成

cd qiita

qiitaディレクトリに移動

scrapy genspider -l

画像7

scrapyで利用できるコマンドを確認する。
basic:ベーシックのテンプレート
crawl:通常のWEBサイトをクロールする。ルールを指定してリンクを辿っていくテンプレート
csvfeedとxmlfeedは滅多に使わない。

scrapy genspider qiita_trend_1d qiita.com

画像8

spiderファイルを作成する。サイトURLのhttps://と最後の/は不要。

画像9

VScodeで開く。ベーシックなテンプレートとして、Spiderクラスの多くの機能を引き継いだQiitaTrend1dSpiderができている。中身は上書きするコードを書くだけで多くのことを実現できる。
allowed_domains:指定しなくても良い属性だけど、思わぬドメインをスクレイピングしないように指定しておく。リスト型だから複数指定できる。
start_urls:スクレイピングを始めるページ
メソッドとしてparseメソッドが設定されている。ブラウザとwebサーバーとの間でHTTP通信をするが、ブラウザの役割をするのがScrapy。サーバーへリクエストを送ってサーバーからのresponseをparseメソッドでキャッチする。parseの中にXpathやcssセレクタのコードをオーバーライド(上書き)を用いて必要な情報だけを抽出する。

Safari開発者ツールの使い方

画像10

Safari→環境設定→詳細 から「メニューバーに開発メニューを表示」にチェックを入れる必要がある。

画像11

option+command+Uで、ページの要素を表示できる。

やっぱり講座でChrome使ってるからChrome でやることにする。

画像12

option+command+Iで開発者ツールを開ける。
option+command+Cもしくは↑をクリックで検査用モードに切り替わって、カーソルの位置のhtmlがハイライトされる。

画像16

画像13

htmlを見たいところにカーソルを合わせて検証からでも開ける。サイト上部タブのOrganizationsを取得していく。

画像16

href属性にorganizationsaという値が格納されているa要素の配下のテキストにこのOrganizationsという文字が格納されている。

画像15

command+fで検索ボックスを表示。

画像17

//a[@href="/organizations"]//text()

画像18

このコードでXPathでOrganizationsを取得できた。

画像19

a[href="/organizations"]::text

画像20

このコードでCSSセレクタで取得できる。配下の要素を取得するには擬似要素として::textを入力するが、開発ツールでは取得できない。コーディングの時に使える。

画像21

次に、記事のタイトルを取得するために、//section/h2まで記述すると30件ヒットし、全てがタイトルであることを確認する。

//section/h2//text()

これで記事名をXPathで取得できる。

section > h2::text

これでは開発ツールでは記事名をCSSセレクタで取得できるけど、ターミナルでは取得できない。>は子要素を表す。

h2 > a::text

画像34

これでターミナルでも記事名をCSSセレクタで取得できる。

//h2/a/@href

画像22

これでURLをXPathで取得できる。

h2 > a::attr(href)

これでURLをCSSセレクタで取得できる。a要素のhref属性を取得するには::attr(属性名)で取得できる。これらの擬似要素は非標準だから開発者ツールでは確認できずコーディングの際に追記する。

これで目的の要素を取得できるようになった。

2022/1/15【4日目】

Scrapy Shellの使い方

ShellはXPathやCSSセレクタを試すのに使う。Shellで確認する方法。慣れてくれば不要な作業。

画像23

ipythonが必要。

scrapy shell https://qiita.com/

画像24

これでシェルを起動できる。

fetch('https://qiita.com/')

画像25

scrape shell でURLを指定せずに後からfetchコマンドを使ってURLを指定してもデータを取得することもできる。GETは送信したリクエストの種類を示す。qiitaのURLにリクエストを送信している。

画像26

画像27

サーバーから情報を取得したいのでGETを使ってる。200なのでリクエストに成功している。

category=response.xpath('//a[@href="/organizations"]//text()')

画像28

取得してきたデータを格納する変数をcategoryとする。xpathの引数にOrganizationsのXPathを入れる。引数は” ”か’ ’で囲う必要があるが、XPathに””が使われているなら’’じゃないといけない。

selectorオブジェクトという形で表示された。ここに取得したデータが格納されている。htmlの特定の部分を選択するためselectorと呼ばれている。xpathのプロパティには先ほどのXPathが、dataプロパティには取得した要素がそれぞれ格納されている。

category=response.xpath('//a[@href="/organizations"]//text()').get()

画像29

.get()メソッドを使うと、webサイトよりOrganizationの文字をテキストで所得できる。

category=response.css('a[href="/organizations"]::text')

画像30

cssを入力しても、Scrapyの中でxpathに変換され実行される。

category=response.css('a[href="/organizations"]::text').get()

画像31

.get()も同様に使える。

title=response.xpath('//section/h2//text()')

画像32

title=response.xpath('//section/h2//text()').getall()

画像33

複数の要素を取得する場合は、.getall()を使う。リスト型で取得できる。CSSも同様。

title=response.xpath('//h2/a//text()').getall()

画像37

こっちのXPathでも同じデータが得られる。

urls=response.xpath('//h2/a/@href').getall()

画像35

urlも同様に、XPathで取得できる。

urls=response.css('h2 > a::attr(href)').getall()

画像36

exit()

これで、シェルから抜けられる。

慣れてくれば、このScrapy Shellでの確認作業を省略してSpiderで直接コーディングしても良い。

2022/1/16【5日目】

Spiderのコーディングと実行

画像38

アナコンダナビゲーターで仮想環境を選び、VS Codeを実行。

画像39

画像40

settings.pyを開く。

FEED_EXPORT_ENCODING='utf-8'

空いているスペース15行目に上記コードを挿入。出力ファイルの文字コードを指定する。指定しないと文字化けすることがある。

画像41

command+K+UでDOWNLOAD_DELAY=3のコメントアウトを解除する。1つのページをダウンロードしてから次のページをダウンロードするまでの間隔を秒で指定する。サイトに負荷をかけないように。

画像42

画像43

禁止されているページにアクセスしないよう、ROBOTSTXT_OBEYはTRUEのままにしておく。

画像44

WEBサイトから取得するデータを日本語にするためScrapyから送信するリクエストのヘッダーの言語設定をenからjaに変更する。DEFAULT_REQUEST_HEADERSのコメントアウトを解除する。command+K+Cで解除。真ん中の行はコメントアウトする。

全ての変更が終わったのでファイルをcommand+Sで保存する。

画像45

qiita_trend_1dayのspiderを開く。spiderのコーディングをしていく。

   def parse(self, response):
       category=response.xpath('//a[@href="/organizations"]//text()').get()
       titles=response.xpath('//section/h2//text()').getall()
       urls=response.xpath('//h2/a/@href').getall()

       # category=response.css('a[href="/organizations"]::text').get()
       # titles=response.css('section>h2::text').getall()
       # urls=response.css('h2 > a::attr(href)').getall()

parseメソッドにScrapy Shellで確認したxpathを記入していく。今回はcssはコメントアウト。

        yield{
           'category':category,
           'titles':titles,
           'urls':urls
       }

画像46

戻り値はyieldを使って辞書で記述して出力する。キーを設定して値をxpathで定義した変数とする。returnではそこで処理が完全にストップしてしまうが、yieldなら一旦停止するだけなので値を返した後に処理は継続する。

Scrapyではこのようにほんの数行でコーディンができる。


画像47

ターミナルで、qiitaディレクトリに移動。lsコマンドで中身を見て、scrapy.cfg(コンフィグファイル)があることを確認する。spiderの実行にあたってはscrapy.cfgと同じレベルにいる必要がある。

scrapy crawl qiita_trend_1d

画像48

spiderはqiitaのrobot.txtとqiitaのサイト自身にリクエストを送ってレスポンスのステータスとして200成功が返ってきた。categoryにはOrganizationと、titlesには記事名が取得できている。

scrapy crawl qiita_trend_1d -o data.json

画像49

ターミナルで確認するだけでなく、josonファイルで出力もできる。Shift+option+Fでjsonファイルのフォーマットで表示できる。jsonファイルでは、キーと値の組み合わせでデータが保存されている。

画像50

command+JでVScode上でターミナルを開ける。qiitaディレクトリに移動。同様にSpiderを実行できる。Scrapy Shellも起動できる。

(参考)PythonでのJSONファイルの読み込み

画像51

data.jsonと同じレベルにpythonファイルを作成する。

import json

with open('data.json',encoding='utf-8') as f:
   qiita_data=json.load(f)
   print(qiita_data)

with openとすることで自動的に閉じてくれる。オープンしたファイルオブジェクトにfと名付けることで、今後fと打つだけで扱えるようになる。json.load(f)で帆見込んだものをqiita_dataに格納。

python read_json.py

画像52

ターミナルでpythonファイルを実行することで、格納されているデータがリスト型で表示される。

print(qiita_data[0])

画像53

辞書型で表示するには、インデックスに[0]を指定してprintする。

with open('data.json',encoding='utf-8') as f:
   qiita_data=json.load(f)
   # print(qiita_data[0])
   titles=qiita_data[0]['titles']
   print(titles)
   urls=qiita_data[0]['urls']
   print(urls)

画像54

これで、titlesとurlsだけを取得できる。

for title,url in zip(titles,urls):
   print(title,url)

画像55

zipを使うことで、タイトルとURLをペアで表示できた。


「Codes&Co.」「コーズコー」


この記事が気に入ったらサポートをしてみませんか?