Webスクレイピング:複数のページから記事のURLを抽出する

前回,トップページから記事の URL を取り出す方法を学びました。とはいえ,トップページから辿ることのできる記事の数はわずかです。そこでコードを改良して,もう少し多くの記事の URL を入手する方法を紹介します。

カレンダーから記事の一覧を取り出す

前回に引き続き VOA Learning English のウェブサイトにアクセスし,As it is のカテゴリーページに進みます。ページの右上にカレンダーがあるので,ここから過去の記事にさかのぼることができます。

今回は,2020年1月の記事を取り出してみます。

まず,日付ごとのページの URL を並べたテキストファイルを作成し,url_by_date.txt という名前で保存します。

https://learningenglish.voanews.com/z/3521/2020/1/1
https://learningenglish.voanews.com/z/3521/2020/1/2
https://learningenglish.voanews.com/z/3521/2020/1/3

・・・・

https://learningenglish.voanews.com/z/3521/2020/1/30
https://learningenglish.voanews.com/z/3521/2020/1/31

全部で31行のテキストになりますが,コピー&ペーストを使えばすぐに用意できるでしょう。

このリストをもとに記事の URL を取り出します。

with io.open('url_by_date.txt', encoding='utf-8') as f:
    url = f.read().splitlines()

上で作ったテキストファイル url_by_date.txt を読み込みます。ファイルをオープンして f という名前を付けたあと,.read() によって,ファイルを読み込みます。.splitlines() はテキストを行ごとに分割する機能です。このように,Python では複数の処理をつなげて書くことができます。

分割されたテキストはurlというリストに格納されます。テキストは31行あるので,url[0]url[30]にそれぞれ URL が格納されています。

url[0] = 'https://learningenglish.voanews.com/z/3521/2020/1/1
'
url[1] = 'https://learningenglish.voanews.com/z/3521/2020/1/2
'
url[2] = 'https://learningenglish.voanews.com/z/3521/2020/1/3
'

・・・・

url[29] = 'https://learningenglish.voanews.com/z/3521/2020/1/30
'
url[30] = 'https://learningenglish.voanews.com/z/3521/2020/1/31'

あとは,前回作ったものとほとんど同じです。urlがリストになっているので,for 文で繰り返し処理をすることで記事の URL の一覧を作ることができます。

for date in range(len(url)):

urlurl[0]url[30]までなので,len(url)=31 となります。つまり,date0 から 30 まで変化していきます。

    res = requests.get(url[date])
    soup = BeautifulSoup(res.text, 'html.parser')
    elems = soup.find_all(href=re.compile("/a/"))

前回との違いは urlurl[date] になっているところです。

requests.get() で順番に html 文字列を取り出します。requests.get() は html を取り出すだけなので,BeutifulSoup に渡して <a href="~">/a/ を含むものだけを取り出し,elem に格納します 。

    for i in range(len(elems)):
        links.append('https://learningenglish.voanews.com'+elems[i].attrs['href'])

elems[i].attrs['href'] でタグを除去し,URL の文字列だけを取り出します。この文字列にドメイン名の部分を付けたし,links に追加していきます。

    print(str(date+1)+' / '+str(len(url))+' finished')

プログラムの進行状況を表示します。この行は無くても構わないのですが,処理時間が長くなるとちゃんと処理が進んでいるのが不安になるので,念のために付け足しておきます。プログラムを実行すると以下のようなメッセージが表示されていきます。

1 / 31 finished
2 / 31 finished
3 / 31 finished

・・・・・・

31 / 31 finished

取り出した URL を一つの文字列にしてファイルに書き出します。

links = np.unique(links)
text='\n'.join(links)
with io.open('article-url.txt', 'w', encoding='utf-8') as f:
    f.write(text)

この辺りも前回と同じです。取り出した URL のデータには重複が発生するので,np.unique()で重複を削除し,.join() で一つの文字列に連結した後,article-url.txt というテキストファイルに書き出します。

コード全体を示しておきます。

import numpy as np
import requests
from bs4 import BeautifulSoup
import io
import re
with io.open('url_by_date.txt', encoding='utf-8') as f:
    url = f.read().splitlines()
links = []
for date in range(len(url)):
    res = requests.get(url[date])
    soup = BeautifulSoup(res.text, 'html.parser')
    elems = soup.find_all(href=re.compile("/a/"))
    for i in range(len(elems)):
        links.append('https://learningenglish.voanews.com'+elems[i].attrs['href'])
    print(str(date+1)+' / '+str(len(url))+' finished')
links = np.unique(links)
text='\n'.join(links)
with io.open('article-url.txt', 'w', encoding='utf-8') as f:
    f.write(text)

article-url.txt はこのようになります。

https://learningenglish.voanews.com/a/5225652.html
https://learningenglish.voanews.com/a/5225655.html
https://learningenglish.voanews.com/a/5226969.html

・・・・・・

199 個の URL が取り出されました。