Pythonでデータを収集してみます。
今回は、自分の別ブログ「愚公は引越しました」の文書を保存するスクレイピングを行っていきます!
得られた文書は、形態素解析などの自然言語処理に使用する予定です。
スクレイピングの実装
requests, Beautiful Soupなどのライブラリもありますが、今回はSeleniumを使用し、ブラウザ経由でスクレイピングします。
たとえ禁止されていなくても、処理によってサーバーに負荷をかけ過ぎるとと、業務妨害罪に問われる可能性もあります。
十分に注意して運営してください。
サイトの構造を把握する
コードを書く前に、「愚公は引越しました」の構造を確認します。
タイトル,見出し,本文から構成されており、ページ下部に「次ページに移動」リンクがあります。これは全ページ共通です。
サイトを全ページを巡ってスクレイピングするために、「次ページに移動」のリンクを使用することにします。
サイトを巡る処理
最新の記事から、投稿日時がもっとも古い記事までページを巡るコードを書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
from selenium import webdriver import time # 設定 #------------------------------------------------------------------------------------ browser = webdriver.Chrome('chromedriver.exe') ini_url = "https://gukouhikkoshi.com/lifehack20180928/" # 準備 #------------------------------------------------------------------------------------ NEXT_PAGE = '//*[@id="main"]/div[1]/div/div[2]/a' # 実行部分 #------------------------------------------------------------------------------------ browser.get(ini_url) next_page_url = ini_url while True: if next_page_url != "https://gukouhikkoshi.com/": try: print("Start to get info...") print(next_page_url) # 最終ページでの処理 if next_page_url == "https://gukouhikkoshi.com/startblog/": print("finish") browser.close() break # 次ページへの移動 next_page_url = browser.find_element_by_xpath(NEXT_PAGE).get_attribute('href') browser.get(next_page_url) time.sleep(1) print("Moving...") except: print("e") browser.close() break |
セクションごとに説明していきます。
1 2 3 4 |
# 設定 #------------------------------------------------------------------------------------ browser = webdriver.Chrome('chromedriver.exe') ini_url = "https://gukouhikkoshi.com/lifehack20180928/" |
この部分では、使用するブラウザと、スクレイピング開始ページを指定しています。
ブラウザにはChromedriverを使用しています。他にはPhantomJSなどが有名ですね。
実行ディレクトリに置くか、別ディレクトリに置いてPathを通してください。
1 2 3 |
# 準備 #------------------------------------------------------------------------------------ NEXT_PAGE = '//*[@id="main"]/div[1]/div/div[2]/a' |
「次ページに移動」のXPathです。
確認したい箇所で右クリックして、「検証」を押すと 要素を簡単に確認できます。
「次ページに移動」の箇所は 、「next np-post-list」クラス となっています。
Selenium のメソッド、find_element_by_class(name) 使用し、name に「next np-post-list」を選択することで、「次ページに移動」を抽出できそうです。
しかし、find_element_by_class() はクラス名に空白が含まれるとエラーを出すようです。
「next np-post-list」の空白を除き、部分名「np-post-list」でも検索可ですが、「前のページに移動」部分が「prev np-post-list」クラスとなっており、「np-post-list」では名前が重複してしまいます。(要素が複数ある場合は最初に検索されたものが適用されます。
そのため、今回はXPathを使用することにしました。
対象箇所で、右クリック > Copy から CSSセレクタ,XPathがコピーできます。
1 2 3 4 |
# 実行部分 #------------------------------------------------------------------------------------ browser.get(ini_url) next_page_url = ini_url |
いよいよ実行部分です。
最初に、ini_url
をブラウザで開きます。
next_page_url
には ini_url
を入れておきます。
1 2 3 |
while True: if next_page_url != "https://gukouhikkoshi.com/": |
最終ページまで、処理をループさせるwhileループです。
1 2 3 4 5 |
# 最終ページでの処理 if next_page_url == "https://gukouhikkoshi.com/startblog/": print("finish") browser.close() break |
最終ページまで到達したときの処理です。
ブラウザを閉じ、ループを抜けます。
1 2 3 4 5 |
# 次ページへの移動 next_page_url = browser.find_element_by_xpath(NEXT_PAGE).get_attribute('href') browser.get(next_page_url) time.sleep(1) print("Moving...") |
次ページに移動する処理です。
next np-post-listクラス、<a>タグの href 属性が次ページのURLとなっており、 next_page_url
に代入しています。
次ページに移動した後に、サーバー負荷の軽減のため1秒待機しています。
実際に動作させてみます。
ページがどんどん更新されています!
サイト上を移動する処理ができました。
文書の保存
ページごとに、文書を保存していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
from selenium import webdriver import time import pandas as pd # 設定 #------------------------------------------------------------------------------------ browser = webdriver.Chrome('chromedriver.exe') #ini_url = "https://gukouhikkoshi.com/book20180709/" ini_url = "https://gukouhikkoshi.com/lifehack20180928/" df = pd.DataFrame() # 準備 #------------------------------------------------------------------------------------ NEXT_PAGE = '//*[@id="main"]/div[1]/div/div[2]/a' WORDS = 'entry-content' # 実行部分 #------------------------------------------------------------------------------------ browser.get(ini_url) next_page_url = ini_url while True: if next_page_url != "https://gukouhikkoshi.com/": try: print("Start to get info...") print(next_page_url) all_sentence_list = browser.find_element_by_class_name(WORDS).find_elements_by_tag_name('p') for sentence_list in all_sentence_list: sentence = sentence_list.text if sentence != "目次 [非表示]" and len(sentence)> 1: #print(sentence) df_t = pd.Series(sentence) df = df.append(df_t, ignore_index=True) # 最終ページでの処理 if next_page_url == "https://gukouhikkoshi.com/startblog/": print("finish") browser.close() break # 次ページへの移動 next_page_url = browser.find_element_by_xpath(NEXT_PAGE).get_attribute('href') browser.get(next_page_url) time.sleep(1) print("Moving...") except: print("e") print(next_page_url) browser.close() break df.to_csv("output.csv") |
追加した箇所のみ抽出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import pandas as pd # 設定 #------------------------------------------------------------------------------------ df = pd.DataFrame() # 準備 #------------------------------------------------------------------------------------ WORDS = 'entry-content' # 実行部分 #------------------------------------------------------------------------------------ while True: if next_page_url != "https://gukouhikkoshi.com/": try: all_sentence_list = browser.find_element_by_class_name(WORDS).find_elements_by_tag_name('p') for sentence_list in all_sentence_list: sentence = sentence_list.text if sentence != "目次 [非表示]" and len(sentence)> 1: #print(sentence) df_t = pd.Series(sentence) df = df.append(df_t, ignore_index=True) except: break df.to_csv("output.csv") |
ソースを見ると「entry-content cf」クラスの、<p>タグに本文が書かれていることがわかりました。
1 2 3 4 5 |
# 準備 #------------------------------------------------------------------------------------ WORDS = 'entry-content' all_sentence_list = browser.find_element_by_class_name(WORDS).find_elements_by_tag_name('p') |
ページ内の該当する要素をすべて、all_sentence_list[]
に格納しています。
1 2 3 4 5 6 |
for sentence_list in all_sentence_list: sentence = sentence_list.text if sentence != "目次 [非表示]" and len(sentence)> 1: #print(sentence) df_t = pd.Series(sentence) df = df.append(df_t, ignore_index=True) |
all_sentence_list[]
から一文ずつテキストを抽出し、データベースに保存しています。
途中の if文は、どうしても本文に含まれてしまった「目次 [非表示]」と、文字数が1字以下となる文の除外です。
1 |
df.to_csv("output.csv") |
最後に、output.csv という名前のファイルに保存しています。
中身はこんな感じ↓
サイトから本文を保存することができました!
最後に
スクレイピングで、サイト内の文書の保存ができました。
せっかくなので、取得したデータをもとに色々やってみようと思います。
形態素解析,頻出単語順にソート,マルコフ連鎖による文書生成などなど…
次回をお楽しみに!