まとめ
- Python + PyQueryでスクレイピングした
- 日本の人気サイト(アクセスが多い)の上位525件を取得してCSVに出力できた
- これで人気サイトのHTML/デザインの特徴を調査できる(かもしれない)
人気のサイトのHTMLとかデザインを分析したら何か面白い結果でないかなーと思って、とりあえず人気サイトのURLを取得するスクリプトを書いた。
Alexaで各国ごとのランキングが見られるので、そこから取得することにする。
Alexa the Web Informationは、1996年からWebサイトのアクセス数や利用状況などの統計を行い、誰でも閲覧できる状態で公開している老舗サービス。1999年からは米Amazonの子
会社。
refs. http://freesoft.tvbok.com/cat94/site10/alexa.html
とりあえず書いたスクリプトはGitHubに置いてある。
https://github.com/saxsir/fjats
ちなみに公式のAPI(有料)を使うと、もっとたくさん取得できます。
注意
- 一般に公開されているサイトなのでスクレイピング自体は違法ではありません(たぶん)が、あんまりやり過ぎると相手サイトに迷惑がかかるので自己責任でお願いします。
refs.
動作環境
- python 3.4.3
- pip 7.0.1
作業手順
- PyQueryのインストール
- 簡単なサンプルを書く
- 日本のランキング525位までを取得するように書き直す
- CSVに出力する
- 毎リクエストごとに1~3秒待つするようにする
PyQueryのインストール
まずはスクレイピングするためのライブラリを入れる。PyQueryというライブラリが人気らしいので、それを使ってみる。
※ 環境を切り分けたいのでpyenvとvirtualenvを使っているが、別に使わなくてもいい。その場合は普通に pip install pyquery
すればOK。
$ cd /path/to/your/workspace
$ pyenv virtualenv 3.4.3 fjats
$ pyenv local fjats
$ pip install --upgrade pip
$ pip install pyquery
あとはスクリプトを書くだけ。
とりあえず完成形を貼っておく。
import csv
from pyquery import PyQuery as pq
from datetime import datetime as dt
from time import sleep
from random import randint
ranks = []
for i in range(21):
# http://www.alexa.com/topsites/countries;0/JP
url = 'http://www.alexa.com/topsites/countries;%s/JP' % i
doc = pq(url, parser='html')
ul = [doc(li) for li in doc('.site-listing')]
ranks += [(li('.count').text(), li('.desc-paragraph')('a').text()) for li in ul]
print('Fetch %s' % url) # Check script is running
sleep(randint(1,3))
with open('topsites-jp_%s.csv' % dt.now().strftime('%y-%m-%d-%H-%M'), 'w') as f:
writer = csv.writer(f, lineterminator='\n')
writer.writerow(('Ranking', 'URL'))
writer.writerows(ranks)
ここから下はコードの解説なので、動けばいいやーという人は上のソースを貼って実行すれば良い。
まずは簡単なサンプルを書く
from pyquery import PyQuery as pq
# とりあえず全世界のトップ25サイトを取得してみる
url = 'http://www.alexa.com/topsites'
doc = pq(url, parser='html')
# 取得したDOMからclassがsite-listingの要素を取得
#(取得したい部分のクラス名とかはChromeのデベロッパーツール等で事前に調査しておく)
ul = [doc(li) for li in doc('.site-listing')]
# とりあえず順位とサイト名を,で区切って出力してみる
ranks = ['%s, %s' % (li('.count').text(), li('.desc-paragraph')('a').text()) for li in ul]
print(ranks)
これをインタプリターで動かしてみる。(起動してベタッとコピペすればOK)
$ python
Python 3.4.3 (default, Mar 27 2015, 14:54:06)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pyquery import PyQuery as pq
>>>
>>> # とりあえず全世界のトップ25サイトを取得してみる
... url = 'http://www.alexa.com/topsites'
>>> doc = pq(url, parser='html')
>>>
>>> # 取得したDOMからclassがsite-listingの要素を取得
... #(取得したい部分のクラス名とかはChromeのデベロッパーツール等で事前に調査 しておく)
... ul = [doc(li) for li in doc('.site-listing')]
>>>
>>> # とりあえず順位とサイト名を,で区切って出力してみる
... ranks = ['%s, %s' % (li('.count').text(), li('.desc-paragraph')('a').text()) for li in ul]
>>>
>>> print(ranks)
['1, Google.com', '2, Facebook.com', '3, Youtube.com', '4, Yahoo.com', '5, Baidu.com', '6, Amazon.com', '7, Wikipedia.org', '8, Taobao.com', '9, Twitter.com', '10, Qq.com', '11, Google.co.in', '12, Live.com', '13, Sina.com.cn', '14, Weibo.com', '15, Linkedin.com', '16, Yahoo.co.jp', '17, Google.co.jp', '18, Ebay.com', '19, Tmall.com', '20, Yandex.ru', '21, Blogspot.com', '22, Vk.com', '23, Google.de', '24, Hao123.com', '25, T.co']
取得できているっぽいので、これを日本のランキングを取得するように書き直す。
日本のランキング525位までを取得するように書き直す
ブラウザで見てみると、http://www.alexa.com/topsites/countries;0/JP こんな感じのURLで1~25位が表示されている。0の部分を増やしていくと20までは取得できるっぽいのでfor文で20回ループを回してデータを取得する。
from pyquery import PyQuery as pq
ranks = []
for i in range(21):
# http://www.alexa.com/topsites/countries;0/JP
url = 'http://www.alexa.com/topsites/countries;%s/JP' % i
doc = pq(url, parser='html')
ul = [doc(li) for li in doc('.site-listing')]
ranks += [(li('.count').text(), li('.desc-paragraph')('a').text()) for li in ul]
この部分
[(li('.count').text(), li('.desc-paragraph')('a').text()) for li in ul]
はちょっと分かりにくいが、
# こんな感じのタプル
('1', 'サイト1') # (li('.count').text(), li('.desc-paragraph')('a').text())
# を、リストの内包表記([... for li in ul])を使ってこんな感じのリストにして返して
[('1', 'サイト1'), ('2', 'サイト2') ...]
# 連結(ranks += ...)している(JavaScriptとかRubyで言うArray.concat)
[('1', 'サイト1'), ('2', 'サイト2') ... ('525', 'サイト525')]
わざわざこんな形にしてるのは、あとでCSVに出力したいため。
CSVに出力する
import csv
from datetime import datetime as dt
with open('topsites-jp_%s.csv' % dt.now().strftime('%y-%m-%d-%H-%M'), 'w') as f:
writer = csv.writer(f, lineterminator='\n')
writer.writerow(('Ranking', 'URL'))
writer.writerows(ranks)
いつのデータか分かりやすいように、CSVのファイル名に時刻を追記している。
最後に、リクエスト毎に1~3秒の間隔をあけるようにする
クローラー(ではないが...)としてのお作法。
from time import sleep
from random import randint
sleep(randint(1,3))
でランダムに1~3秒待つ。