1000ページあっても取得可能!】複数ページに記載があるリンク先URLを取得する方法|PythonでWebスクレイピング第02回

【1000ページあっても取得可能!】複数ページに記載があるリンク先URLを取得する方法|PythonでWebスクレイピング第02回

ライブラリインポート

from bs4 import BeautifulSoup
import requests
import pandas as pd
import time

それでは、ライブラリをインポートする記述をします。
bs4のパッケージの中にあるBeautifulSoupを読み込みます。
BeautifulSoupは、複雑なHTMLの構造を解析し、必要な部分を取り出すことができるWebスクレイピング用のライブラリです。
次に、requestsをインポートします。これはPythonを使ってHTTP接続するために必要なライブラリです。
前回のWebスクレピングのレッスンで紹介したPythonの標準のurllibモジュールよりもコード量が少なく記述することができ、シンプルに実装できるライブラリです。
次に、取得したデータをCSVに書き出すために、pandasをインポートします。
asを記述するとライブラリ名を好きな名前で使うことができます。
pandasについては、「Pandas入門講座」にて詳しくご紹介していますので、ご興味があればご覧ください。
また、HTTPに接続した後、すぐに次の処理が実行されないようにするためtimeをインポートします。
timeモジュールを使うことで、Webページのデータを取得する前に次の処理にいかないように、3秒間であったり5秒間、次の処理を待機させることができます。
それでは、実行します。
インポートが完了しました。

url = 'https://kino-code.work/python-super-basic-course/'
r = requests.get(url)
time.sleep(3)

では、kinocodeのWebサイトの「Python超入門コース」のコース一覧にあるタイトルとリンク先のURLのデータを取得してみましょう。
また、そのタイトルとリンク先URLをセットにして、CSVにデータを書き込む処理を記述します。

まず、変数urlにPython超入門コースのコース一覧ページのURLを代入します。
そのurlをrequestsに渡してgetメソッドを使ってWebページのデータを取得して、変数rに代入をします。
次に、Webページへ移動する前に、次の処理に行かないように、time.sleep(3)で、3秒待機する記述をします。

<html>
    <head>
        <meta charset="utf-8">
        <title>キノコード
        </title>
    </head>
    <body>
        <h1>こんにちは</h1>
    </body>
</html>

例えば、このようなシンプルなHTMLであっても、HTMLの構造はこのようになっています。

soup = BeautifulSoup(r.text, &#039;html.parser&#039;)

では早速、BeautifulSoupを使って、HTMLのデータを取得するする記述をします。
BeautifulSoup()と書いてカッコの中の、第1引数に、先ほど、取得したWebページが格納されている変数rに.textを追加したものを記述します。
第二引数には、'html.parser'と記述します。
これで、Python超入門コースのコース一覧のHTMLのデータが取得できました。
変数soupを表示させてみましょう。実行します。ずらーーーといろいろデータが取得できているようです。

soup
contents = soup.find(class_="entry-content")

これだとHTMLの構造がわかりにくいので、Google Chromeを使ってHTMLの構造をみてみましょう。
Google Chrome画面を右クリックするとメニュー画面が表示されます。
一番下にある「検証」を選択してみましょう。
HTMLのソースコードの中身が確認できます。

タイトルがリンク先が記述されているのはクラス"entry-content"内のテキストと、pタグ内のhrefに記載されているリンクです。
こちらが、entry-content部分のHTMLです。
aタグ内にあるhrefのリンクです。
これらを全て取得していきます。

続いて、BeautifulSoupのfindメソッドで取得したい部分のクラスを指定し、HTMLを取得します。
classアンダースコア、イコール、entry-contentを記述します。
classはPythonの予約語のため、アンダースコアをclassの後に追加します。
ちなみに、予約語とは、プログラムミングであらかじめ決められていて、変数名などに使用できない文字列のことです。
例えば、Pythonの予約語には、if, while, with, True, Falseなどがあります。
実行します。
これで「entry-content」のクラスの情報が変数contentsに代入されたはずです。

contents

表示させてみましょう。
実行します。
entry-contentの情報が取得できています。

get_a = contents.find_all("a")

次に、find_allメソッドで、entry-contentsクラス内にあるaタグを全て取得します。
実行します。

get_a

表示させてみましょう。aタグが取得できています。

len(get_a)

今回は、aタグをカウントしてその個数分の処理をするようにします。
len関数で、変数get_aの個数を確認します。

15と出力されます。
15個のaタグが存在しています。
次は、forループでその個数分の処理をするようにします。

title_links = []
for i in range(len(get_a)):
    try:
        link_ = get_a[i].get("href")
        title_links.append(link_)
    except:
        pass

forループで抜き取った複数のリンクを格納できる変数title_linksを用意します。
リスト型としたいので、角かっこで括り、中には何も代入しないでおきます。

forループで数値をカウントする場合、range関数を使用します。
例えば、for i in range(5)と記述すれば5回ループがまわります。
今回は、リンクの個数分だけループを回したいので、range関数の引数にlen(get_a)を記述します。

もし、このURL取得する際にエラーが起きた場合は、処理が止まってしまいます。
それを避けるために、例外処理を記述しましょう。
例外処理とは、プログラム内で予期せぬエラーが発生した際に、実行を止めたり、エラーを飛ばして実行を続けたりする処理のことです。
プログラミングで一般的に使われる、try-exceptで記述してみましょう。

try-exceptの構文はこのようになっています。

try:
エラーが起きない場合に実行する処理
except:
エラーが起きた場合に実行する処理

エラーが起きない場合に実行する処理を記述します。
この場合、try:の下に記述します。

リストが代入された変数の「get_a」からaタグ内にあるhrefを、getメソッドで、最初から順番に取得していきます。
iには0から14までの数字が順番に代入されていきます。
つまり、リストget_aの1つめのhrefを取得してlink_に代入、次にリストget_aの2つめのhrefをlink_に代入といった処理になります。
そして、リストのtitle_linksに対して、appendメソッドで、取得したhref、つまりリンク先URLをリストのtitle_linksに次々と追加していきます。

続いて、もしエラーが起きた場合にはexcept:の下に処理を実行します。
今回は、エラーが起きた場合は、エラーを無視するのでpassと記述します。
ちなみに、passは、特に何も処理を実行しない時に使用します。
実行します。
リンク先のURLが取得できるはずです。

title_links

取得できているか確認をしてみましょう。
実行します。
取得できています。

title_link = title_links[0]
print(title_link)

ここからは、コースの一覧ページから取得した先程のURLを利用して、各ページに移動。
そして、Youtubeのタイトルとリンクを取得する処理を行います。
for文を使って各ページに移動をするのですが、いきなりそれをやるとわかりにくいので分解して説明をします。
まず、リストの「title_links」の1番目、つまり、添字の0では、どのようなURLが取得されているかprintで表示させてみましょう。
このように添え字を使うと、リストの1番目の値を取得できますよね。

for i in range(15):
    title_link = title_links[i]
    print(str(i)+"回目のループ→",title_link)

したがって、このように書くと、すべてのURLが順番にtitle_linkに代入されるはずです。
わかりやすく何回目のループかも表示させてみます。
実行してみましょう。
順番にURLを取得できたようです。

for i in range(len(title_links)):
    title_link = title_links[i]
    print(str(i)+"回目のループ→",title_link)

先ほどはrange関数の中に直接15と記述しました。
しかし、このようにtitle_linksの数をlen関数で数えて、その個数分、for文まわせばtitle_linksの個数がいくらであっても対応可能です。
このような記述に変えてみましょう。
実行します。
問題ないですね。

youtube_titles = []
youtube_links = []

for i in range(len(title_links)):
    title_link = title_links[i]
#     print(str(i)+"回目のループ→",title_link)

    r = requests.get(title_link)
    time.sleep(3)
    soup = BeautifulSoup(r.text, &#039;html.parser&#039;)
    youtube_title = soup.find(class_="entry-title").text
    print(youtube_title)

では、先ほどに記述に色々加えていき、リンク先URLのタイトルを取得しましょう。

まず、「youtube_titles」と「youtube_links」というリストを用意します。
次に、先ほど説明をしたfor文を記述します。
先程も説明した通り、リストのtitle_linksに格納されているリンクを1つずつ取り出し、変数title_linkに代入していきます。
その取得したtitle_linkをrequestsに渡して、getメソッドを使ってWebページのデータを取得し、変数rに代入をします。
Webページが読み込まれる前に、次の処理に行かないようにtime.sleep(3)で3秒待機させます。
そして、BeautifulSoupで、読み込まれたWebページのHTMLを解析をします。
ちなみに、タイトルが記載されているHTMLとなります。

<h1 class="entry-title" itemprop="headline">【5分レッスン】Python超入門コース#01 Pythonのコース紹介</h1> 

したがって、find(class_="entry-title")を記述するとタイトルが取得できます。
そして、ドットtextを追加すると、タイトルのテキストを抜き取ることができます。
printでタイトルも取得できているか確認してみましょう。
実行します。
リンク先のURLとタイトルが取得できています。

youtube_titles = []
youtube_links = []
for i in range(len(title_links)):
    title_link = title_links[i]
    print(title_link)
    r = requests.get(title_link)
    time.sleep(3)
    soup = BeautifulSoup(r.text, &#039;html.parser&#039;)

    youtube_title = soup.find(class_="entry-title").text
    if youtube_title == &#039;404 NOT FOUND&#039;:
        continue
    else:
        youtube_titles.append(youtube_title)

        youtube_link = soup.find(&#039;iframe&#039;)[&#039;src&#039;].replace("embed/","watch?v=")
        youtube_links.append(youtube_link)

ちなみに、「【5分レッスン】Python超入門コース#03 環境構築 for Windows」のタイトルのリンク先のページは削除されていて、
クラスentry-titleのテキストが「404 NOT FOUND」になっていますよね。
こちらのページの処理が実行されてしまうと、エラーが起きてしまいます。
それを避けるために、変数youtube_titleが、「404 NOT FOUND」の場合は、処理がおこなわれないようにしましょう。
if文で、youtube_titleが、「404 NOT FOUND」であるか、そうでないかの判定を行います。
左辺と右辺が等しいかどうか判定する場合は、左辺イコールイコール右辺とイコールを2つ記述します。
エラーが起きた場合、処理は行わないので、その後にpassを記述します。
そして、youtube_titleが「404 NOT FOUND」ではない場合は、その下のelse:の処理に進みます。
リストのyoutube_titlesにappendを追加し、テキストを代入します。

次に、Youtubeのリンク先URLを取得します。

<iframe width="560" height="315" src="https://www.youtube.com/embed/3jymAyMJjR8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="allowfullscreen"></iframe>  

iframeタグの中のsrc、つまり、ソースに記載されているリンクを取得したいと思います。
findメソッドでiframeを渡すとiframeのタグが取得できます。
それに、丸括弧の中にsrcを加えて、srcのリンクが取得できます。
srcのリンクのアドレスですが、このままだと、Youtubeのチャンネル画面ではなく、動画のみが出てくるリンクになってしまいます。
"https://www.youtube.com/embed/3jymAyMJjR8"の「embed/」の部分を「/watch?v=」に変更すると、youtubeのチャンネル画面のリンクとなります
したがって、replace関数を使用して、文字列を置換します。
replaceの丸括弧中の第一引数に"置換前の文字列"、第二引数に"置換後の文字列"を渡すことで、文字列の置き換えができます。
youtube_linksリストにappendを追加し、取得したリンクを格納します。
実行します。
これで、タイトルとリンク先URLが取得できているはずです。
表示させてみましょう。

youtube_titles
youtube_links

取得できています。

result ={
    &#039;youtube_title&#039;: youtube_titles,
    &#039;youtube_link&#039;: youtube_links
}

辞書型にまとめておくと、PandasのDataFrameにしやすいです。
したがって、取得したデータを辞書型にまとめておきましょう。
波括弧の中にキーと値を設定することで辞書型を作成できます。
ここでは、変数resultに、辞書型を代入する記述をします。
タイトルのキーを'youtube_title'、リンクのキーを'youtube_link'とします。
値は、先ほど取得したリストを設定します。

result

resultの中身を表示させてみましょう。
実行します。
リストを辞書型として表示できました。

df = pd.DataFrame(result)

それではデータフレームにしてみましょう。
データフレームの作り方はPandas超入門コースのレッスン4でも、詳しく解説をしています。
そちらもご覧ください。
dfイコール、pd.DataFrame丸括弧で、データフレームを用意します。
辞書型のresultで取得したデータを格納します。

df

データフレームを確認してみましょう。
綺麗にデータを取得できているようです。
またリンクエラーの環境構築のWindowsは取得せずにエラーになっておりません。

df.to_csv(&#039;result.csv&#039;, index=False, encoding=&#039;utf-8&#039;)

データフレームを、CSVへ書き出しましょう。
pandasのto_csvメソッドを使ってCSVへ書き出す記述をします。
第一引数には、任意のCSVファイル名を記述し、シングルコーテーションでくくります。
ここでは、result.csvとします。
indexイコールFalseとすることで、インデックスをつけない設定にします。
インデックスとは、データフレームにした際に自動的に出力される行番号のことです。
最後に、文字化けを避けるために、endodingで文字コードを指定します。
ここではutf-8とします。
実行します。
CSVファイルを作成できました。

レッスンで使ったファイルはこちら

■保存方法

Mac:右クリック⇒「リンク先を別名で保存」

Windows:右クリック⇒「名前を付けてリンク先を保存」

Jupyter Labのファイルはこちら