(更新)クラスにしたものを置いておきます。 【Python】ニュース記事の更新日をHTMLから取得する
#サイトの更新日を取得するのってむずくね
レスポンスヘッダーを調べることで、静的なサイトであれば最終更新日がわかることがあります。
import requests
res = requests.head('https://www.kantei.go.jp')
print(res.headers['Last-Modified'])
Mon, 17 Feb 2020 08:27:02 GMT
(前回記事)【Python】ウェブサイトの最終更新日を取得
一部ニュースサイトや日本の政府関係の多くのサイトではこれで問題なく取れるのですが、大半のサイトはうまくいきません。
KeyError: 'last-modified'
じゃどうしたらいいかというと、大きく2つの方法がありそうです。
##方針1 URLを見る
URLの中には 2019/05/01 や 2019-05-01 のような文字列が入っていることがあります。これを抜き出すのは有力で確実な手段です。
##方針2 スクレイピング
最終的に頼るのはここなんでしょう。
ということで、これらの合わせ技で、普段読んでいるニュースサイトから自動でサイト更新日を抜き出していきます。
取得したbeautifulsoupオブジェクトをsoupとしています。
取得した更新日はdatetime型に変換します。文字列の抽出や整形は正規表現を使います。
import bs4
import datetime
import re
#調べたニュースサイト
CNN
Bloomberg
BBC
Reuter
Wall Street Journal
Forbes Japan
Newsweek
朝日新聞
日経新聞
産経新聞
読売新聞
毎日新聞
##CNN
print(soup.select('.update-time')[0].getText())
#Updated 2128 GMT (0528 HKT) February 17, 2020
timestamp_temp_hm = re.search(r'Updated (\d{4}) GMT', str(soup.select('.update-time')[0].getText()))
timestamp_temp_bdy = re.search(r'(January|February|March|April|May|June|July|August|September|October|November|December) (\d{1,2}), (\d{4})', str(soup.select('.update-time')[0].getText()))
print(timestamp_temp_hm.groups())
print(timestamp_temp_bdy.groups())
#('2128',)
#('February', '17', '2020')
timestamp_tmp = timestamp_temp_bdy.groups()[2]+timestamp_temp_bdy.groups()[1]+timestamp_temp_bdy.groups()[0]+timestamp_temp_hm.groups()[0]
news_timestamp = datetime.datetime.strptime(timestamp_tmp, "%Y%d%B%H%M")
print(news_timestamp)
#2020-02-17 21:28:00
# 日付だけならURLからも取れる
URL = "https://edition.cnn.com/2020/02/17/tech/jetman-dubai-trnd/index.html"
news_timestamp = re.search(r'\d{4}/\d{1,2}/\d{1,2}', URL)
print(news_timestamp.group())
#2020/02/17
news_timestamp = datetime.datetime.strptime(news_timestamp.group(), "%Y/%m/%d")
print(news_timestamp)
#2020-02-17 00:00:00
コメント:'Updated'の文字列が常に入っているのかは未検証。CNNの記事はまとめページを除くと日付がURLに入っているので、これを取るのが確実に見える
##Bloomberg
print(soup.select('time')[0].string)
# #
# # 2020年2月18日 7:05 JST
# #
timesamp_tmp = re.sub(' ','',str(soup.select('time')[0].string))
timesamp_tmp = re.sub('\n','',timesamp_tmp)
news_timestamp = datetime.datetime.strptime(timesamp_tmp, "%Y年%m月%d日%H:%MJST")
print(news_timestamp)
#2020-02-18 07:05:00
# URLでも日付までは取れる
URL = "https://www.bloomberg.co.jp/news/articles/2020-02-17/Q5V6BO6JIJV101"
timestamp_tmp = re.search(r'\d{4}-\d{1,2}-\d{1,2}', URL)
print(news_timestamp_tmp.group())
#2020-02-17
news_timestamp = datetime.datetime.strptime(timestamp_tmp, "%Y-%m-%d")
print(news_timestamp)
#2020-02-17 00:00:00
コメント:タグ内で改行と空白があり一手間必要。
##BBC
https://www.bbc.com/news/world-asia-china-51540981
print(soup.select("div.date.date--v2")[0].string)
#18 February 2020
news_timestamp = datetime.datetime.strptime(soup.select("div.date.date--v2")[0].string, "%d %B %Y")
print(news_timestamp)
#2020-02-18 00:00:00
コメント:細かい時間はどこ見ればいいのかわかりませんでした。
##Reuter
print(soup.select(".ArticleHeader_date")[0].string)
#February 18, 2020 / 6:11 AM / an hour ago更新
m1 = re.match(r'(January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}',str(soup.select(".ArticleHeader_date")[0].string))
print(m1.group())
#February 18, 2020
m2 = re.search(r'\d{1,2}:\d{1,2}',str(soup.select(".ArticleHeader_date")[0].string))
print(m2.group())
#6:11
news_timestamp = datetime.datetime.strptime(m1.group()+' '+m2.group(), "%B %d, %Y %H:%M")
print(news_timestamp)
#2020-02-18 00:00:00
##Wall Street Journal
https://www.wsj.com/articles/solar-power-is-beginning-to-eclipse-fossil-fuels-11581964338
print(soup.select(".timestamp.article__timestamp")[0].string)
#
# Feb. 17, 2020 1:32 pm ET
#
news_timestamp = re.sub(' ','',str(soup.select(".timestamp.article__timestamp")[0].string))
news_timestamp = re.sub('\n','',m)
print(news_timestamp)
#Feb.17,20201:32pmET
news_timestamp = re.match(r'(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec).(\d{1,2}),(\d{4})(\d{1,2}):(\d{1,2})',str(news_timestamp))
print(news_timestamp.groups())
#('Feb', '17', '2020', '1', '32')
tmp = news_timestamp.groups()
timesamp_tmp = tmp[0]+' '+ tmp[1].zfill(2)+' '+tmp[2]+' '+tmp[3].zfill(2)+' '+tmp[4].zfill(2)
print(timesamp_tmp)
#Feb 17 2020 01 32
news_timestamp = datetime.datetime.strptime(timesamp_tmp, "%b %d %Y %H %M")
print(news_timestamp)
#2020-02-17 01:32:00
##Forbes Japan
https://forbesjapan.com/articles/detail/32418
print(soup.select("time")[0].string)
#2020/02/18 12:00
news_timestamp = datetime.datetime.strptime(soup.select("time")[0].string, "%Y/%m/%d %H:%M")
print(news_timestamp)
#2020-02-18 12:00:00
print(soup.select('time')[0].string)
# On 2/17/20 at 12:11 PM EST
m = re.search(r'(\d{1,2})/(\d{1,2})/(\d{1,2}) at (\d{1,2}:\d{1,2}) ', str(soup.select('time')[0].string))
print(m.groups())
#('2', '17', '20', '12:11')
tmp = m.groups()
timesamp_tmp = tmp[0].zfill(2)+' '+ tmp[1].zfill(2)+' '+'20'+tmp[2].zfill(2)+' '+tmp[3]
print(timesamp_tmp)
news_timestamp = datetime.datetime.strptime(timesamp_tmp, "%m %d %Y %H:%M")
print(news_timestamp)
#2020-02-17 12:11:00
##朝日新聞
https://www.asahi.com/articles/ASN2K7FQKN2KUHNB00R.html
print(soup.select('time')[0].string)
#2020年2月18日 12時25分
news_timestamp = datetime.datetime.strptime(soup.select('time')[0].string, "%Y年%m月%d日 %H時%M分")
print(news_timestamp)
#2020-02-18 12:25:00
コメント:静的でわかりやすい。ざっと見たところカテゴリー別でも揺らぎが無くて助かる。
##日経新聞
print(soup.select('time')[1])
#2020年2月18日 11:00
news_timestamp = datetime.datetime.strptime(soup.select('time')[1].string, "%Y年%m月%d日 %H:%M")
print(news_timestamp)
#2020-02-18 11:00:00
print(soup.select('.cmnc-publish')[0].string)
#2020/2/18 7:37
news_timestamp = datetime.datetime.strptime(soup.select('.cmnc-publish')[0].string, "%Y/%m/%d %H:%M")
print(news_timestamp)
#2020-02-18 07:37:00
print(soup.select('.cmnc-publish')[0].string)
#2020/2/15付
news_timestamp = datetime.datetime.strptime(soup.select('.cmnc-publish')[0].string, "%Y/%m/%d付")
print(news_timestamp)
#2020-02-15 00:00:00
コメント:色々書き方がある。ざっと見で3つあったけれどもっとあるかも。
##産経新聞
print(soup.select('#__r_publish_date__')[0].string)
#2020.2.18 13:10
news_timestamp = datetime.datetime.strptime(soup.select('#__r_publish_date__')[0].string, "%Y.%m.%d %H:%M")
print(news_timestamp)
#2020-02-18 13:10:00
コメント:よくみたらURLに時まで載ってた。
##読売新聞
print(soup.select('time')[0].string)
#2020/02/18 14:16
news_timestamp = datetime.datetime.strptime(soup.select('time')[0].string, "%Y/%m/%d %H:%M")
print(news_timestamp)
#2020-02-18 14:16:00
コメント:日付だけならURLから取れる。
##毎日新聞
print(soup.select('time')[0].string)
#2018年8月3日 東京朝刊
news_timestamp = datetime.datetime.strptime(soup.select('time')[0].string, "%Y年%m月%d日 東京朝刊")
print(news_timestamp)
#2018-08-03 00:00:00
print(soup.select('time')[0].string)
#2020年2月18日 東京夕刊
news_timestamp = datetime.datetime.strptime(soup.select('time')[0].string, "%Y年%m月%d日 東京夕刊")
print(news_timestamp)
#2020-02-18 00:00:00
print(soup.select('time')[0].string)
#2020年2月18日 09時57分
#最終更新はprint(soup.select('time')[1].string)
news_timestamp = datetime.datetime.strptime(soup.select('time')[0].string, "%Y年%m月%d日 %H時%M分")
print(news_timestamp)
#2020-02-18 09:57:00
print(soup.select('time')[0].string)
#2020年2月18日
news_timestamp = datetime.datetime.strptime(soup.select('time')[0].string, "%Y年%m月%d日")
print(news_timestamp)
#2020-02-18 00:00:00
コメント:毎日新聞では、電子版のみの記事は分単位で取得できるぽい。朝刊・夕刊からの記事と毎日プレミアについては、URLで取れるのと同じ、日付までしか取得できない。
#表
ニュースサイト | Rヘッダーから | URLから | HTML中身から |
---|---|---|---|
CNN | 年月日 | 年月日時分 | |
Bloomberg | 年月日 | 年月日時分 | |
BBC | 年月日 | ||
Reuter | 年月日時分 | ||
Wall Street Journal | 年月日時分 | ||
Forbes Japan | 年月日時分 | ||
Newsweek | 年月日時分 | ||
朝日新聞 | 年月日時分 | 年月日時分 | |
日経新聞 | 年月日時分 | ||
産経新聞 | 年月日時分 | 年月日時 | 年月日時分 |
読売新聞 | 年月日 | 年月日時分 | |
毎日新聞 | 年月日 | 年月日時分* |
*毎日新聞は記事によっては日付のみあるいは日付+朝/夕刊の記載
#思ったこと
言語はもちろん、サイトごとに日付の表記はまちまちです。同じニュースサイト内であっても表記揺れがあり、それについては全てを確認できているわけではありません。
HTMLみても取れないけどURLを見るとわかる、というサイトは今のところ見つけられていません。合わせ技と言いましたが、スクレイピングだけで取得しても変わらないですね。
この方法は各サイトごとに地道にタグやクラス名を読んでいかなければならず、全てのサイトはもちろん、ニュースサイトのみですら対応するのはかなり難しそうです。もっと良いやり方があればぜひ教えてください。
(更新)クラスにしたものを置いておきます。 【Python】ニュース記事の更新日をHTMLから取得する