NewsApiのURLから本文を抽出してSQLite3に挿入する
本文はHTMLでなくプレーンテキスト。だが、スクレイピングに大問題。
成果物
動作確認
以下のような手順でテストデータを用意する。
- NewsApiを取得する(NewsApiでカテゴリ別にニュースを取得する)
- 1で取得したJSONファイルを
news.json
とでもリネームしておく - 2のJSONファイルをコピーする
- 3のうち最新(先頭)の2件くらいを削除して
news_old.json
とでもリネームしておく - SQLite3DBファイル作成する(NewsApiで得たニュースを保存するSQLite3テーブルを考える)
- 3のJSONファイルをDBファイルに取り込む(NewsApiのJSONからSQLite3DBファイルへ挿入する)
- 6の全レコードに対して
body=''
してしまう
この状態で、以下コードを実行し、最新2件だけが追加されたことを確認する。そしてHTMLから本文を抽出されていることを確認する。
コード
insert_newer_news_extract.sh
# $1: JSONテキスト, $2: JSONパス json_extract() { sqlite3 :memory: 'select json_extract(readfile('\'"$1"\''), '\'"$2"\'');'; } make_insert_stmt() { echo 'insert into news(published,url,title,body) values('\'"$1"\'','\'"$2"\'','\'"$3"\'','\'"$4"\'');'; } # $1: DB内最新ニュース一意特定値(published,url,title。デリミタ\n) # $2..3: NewsApi JSON(publishedAt,url) is_new() { local latest_published="`echo "$1" | sed -n 1P`" local latest_url="`echo "$1" | sed -n 2P`" echo "$latest_published $2 `[[ "$2" < "$latest_published" ]]`" [[ "$2" < "$latest_published" ]] && return 0 || [[ "$3" = "$latest_url" ]] && return 0; return 1; } # $1: url extract_content() { local html="index.html" wget -O "$html" "${1}"; python3 extract_content.py "$html" } # $1: NewsApiJSONパス run() { local json_path="$1" local insert_sql="insert.sql" [ 'ok' != "`json_extract "$json_path" '$.status'`" ] && { echo 'エラー。JSONのstatusがokでない。: '"`json_extract "$json_path" '$.status'`" 1>&2; exit 1; } # DB内の最新ニュースを一意特定するデータを取得する(published,url,title) local db="news.db" local latest_news="`sqlite3 "$db" < 'get_latest.sql'`" # SQLファイル内容を空にする(さもなくば連続使用時に前の分と合わせて追記されてしまう) : > "$insert_sql" local totalResults="`json_extract "$json_path" '$.totalResults'`" for idx in $(seq 0 $(expr $totalResults - 1)); do # JSONから項目を抽出する local published="`json_extract "$json_path" '$.articles['"$idx"'].publishedAt'`" local url="`json_extract "$json_path" '$.articles['"$idx"'].url'`" is_new "$latest_news" "$published" "$url"; [ $? -eq 0 ] && break; local title="`json_extract "$json_path" '$.articles['"$idx"'].title'`" # local body="`json_extract "$json_path" '$.articles['"$idx"'].description'`" # とりあえずdescriptionで代用する local body="`extract_content "$url"`" # 本文を抽出してプレーンテキスト化したもの # totalResultsが多すぎたとき各項目はNULL(空文字)になる。このときは終了する。JSONが正しい限り起こり得ない。 [ -z "$title" ] && { echo "JSON不正。titleが空。totalResults:$totalResults,idx:$idx" 1>&2; break; } # insert文を作る make_insert_stmt "$published" "$url" "$title" "$body" >> "$insert_sql" done sqlite3 "$db" < "$insert_sql" } run "$1"
ポイント
url
をwget
でダウンロードする- 1を
index.html
として保存する - 2をpythonで解析して本文を抽出する
問題
- 文字コードが様々でUTF-8にエンコードできない
- 本文はJavaScriptの実行によって取得される
続きを読む
ボタンを押下しないと本文が読めない構造のサイトである
これはひどい。ぜんぜん自動化できない。原因がかなり致命的。特にJavaScriptはもう何でもあり。対応がむずかしい。
対策
自力での対処は諦める。スクレピング用ライブラリを探してみたらあった。
今度はこれを試してみるか。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12(1)-release
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux