pythonでスクレイピング(beautifulsoup)について、独学を進めます。
前回の pythonのつづき、スクレイピング(requests) からの続きになります。
PC作業環境
OS: windows 8.1
pythonインストールできた状態なら
この記事の .pyファイルとバッチファイルをコピペして、バッチファイルをダブルクリックすれば動作確認できますので、ぜひ試してみて下さい!
目次
- beautifulsoup(find)
- head() が使えそうなので試してみます。
- 取得する html は、↓ 南びわ湖エリア情報の記事(h3タグを使ってる)にします。
- 【演習】ブログタイトルをテキストで取得する
- beautifulsoup(find_all)
引き続き、この動画シリーズをベースに進めます。
beautifulsoup(find)
パース(解析)ライブラリの beautifulsoup を動かして見ていきます。
まず、取得したhtml が、
beautifulsoup での解析が可能かどうか? 「str」型であるか確認します。
取得した html の「型」を確認します。
pythonコード get_html_003.py
# coding: shift_jis
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')url = "https://minamibiwako.hatenablog.jp/"
import requests
html = requests.get(url)
print(html.url)
print()
print(html.text)
print()
print("取得したhtmlのtype判定")
html_type = type(html.text)
print(html_type)
・「南びわ湖エリア情報」のトップページの html を取得
・その html の型を確認
バッチファイル(pythonコードを実行) do_py006.bat
rem do_py006.bat
@echo off
set year=%date:~0,4%
set month=%date:~5,2%
set day=%date:~8,2%
set time2=%time: =0%
set hour=%time2:~0,2%
set minute=%time2:~3,2%
set second=%time2:~6,2%
set logname=%year%-%month%-%day%_%hour%_%minute%_%second%python get_html_003.py >> %logname%_get_html_003.txt
出力結果(1)
https://minamibiwako.hatenablog.jp/
<!DOCTYPE html>
<html
lang="ja"data-admin-domain="//blog.hatena.ne.jp"
data-admin-origin="https://blog.hatena.ne.jp"
data-author="minamibiwako"
data-avail-langs="ja en"
data-blog="minamibiwako.hatenablog.jp"
data-blog-comments-top-is-new="1"
data-blog-host="minamibiwako.hatenablog.jp"
data-blog-is-public="1"
data-blog-name="南びわ湖エリア情報"省略
取得したhtmlのtype判定
<class 'str'>
よって、取得した html の type は「str」型であることが確認できました。
取得した html は、beautifulsoup で解析が可能です。
ここで、取得した html を全て出力していると、ダラダラと長くなって見にくいので先頭の12行だけ(data-blog-name=""まで)を表示させてみます。pandasライブラリの
head() が使えそうなので試してみます。
pythonの良い所は、外部ライブラリがたくさん公開されていて、「こんなことをやりたい」と思ってググったら、いろいろ出てきて、簡単に試せるところです。
今のインストール済みの「ライブラリを確認」します。これもバッチファイルにしておくと、いつでもバッチファイルをダブルクリックするだけで確認できます。
バッチファイル pip_list.bat
rem pip_list.bat
@echo off
set year=%date:~0,4%
set month=%date:~5,2%
set day=%date:~8,2%
set time2=%time: =0%
set hour=%time2:~0,2%
set minute=%time2:~3,2%
set second=%time2:~6,2%
set logname=%year%-%month%-%day%_%hour%_%minute%_%second%python -m pip list >> %logname%_pip_list.txt
出力結果(2)
Package Version
-------------- ----------
beautifulsoup4 4.8.1
certifi 2019.11.28
chardet 3.0.4
idna 2.8
pip 21.0.1
requests 2.22.0
setuptools 41.2.0
soupsieve 1.9.5
urllib3 1.25.7
pandasライブラリが入っていないことが確認できました。
pandasのインストール
「pip install pandas」
pandas のインストールが完了しました。
外部ライブラリのインストール(pip)などについては、ここ↓で復習できます。
ライブラリのリストを確認します。
バッチファイル pip_list.bat
rem pip_list.bat
@echo off
set year=%date:~0,4%
set month=%date:~5,2%
set day=%date:~8,2%
set time2=%time: =0%
set hour=%time2:~0,2%
set minute=%time2:~3,2%
set second=%time2:~6,2%
set logname=%year%-%month%-%day%_%hour%_%minute%_%second%python -m pip list >> %logname%_pip_list.txt
出力結果(3)
Package Version
--------------- ----------
beautifulsoup4 4.8.1
certifi 2019.11.28
chardet 3.0.4
idna 2.8
numpy 1.20.1
pandas 1.2.3
pip 21.0.1
python-dateutil 2.8.1
pytz 2021.1
requests 2.22.0
setuptools 41.2.0
six 1.15.0
soupsieve 1.9.5
urllib3 1.25.7
pandas がインストールできたことが確認できました。
pandas の他にも、いろいろ入ってるw けど、気にせず進みます。
beautifulsoup のインポート
実行時にプロンプトに何かのワーニングが出てるけど、そのまま進めます。
parse_html_001.py:33: UserWarning: No parser was explicitly specified, so I'm us
ing the best available HTML parser for this system ("html.parser"). This usually
isn't a problem, but if you run this code on another system, or in a different
virtual environment, it may use a different parser and behave differently.The code that caused this warning is on line 33 of the file parse_html_001.py. T
o get rid of this warning, pass the additional argument 'features="html.parser"'
to the BeautifulSoup constructor.soup = BeautifulSoup(html.text)
続行するには何かキーを押してください . . .
最適な使い方でないようですが、特に問題ないのでこのままいきます。
UserWarning:パーサーが明示的に指定されていないため、このシステムで利用可能な最良のHTMLパーサー( "html.parser")を使用しています。
これは通常、問題ではありませんが、このコードを別のシステムまたは別のシステムで実行する場合、仮想環境では、異なるパーサーを使用し、動作が異なる場合があります。
h2タグのテキストの取得のやり方まで分かったので、実際に動かしてみます。
取得する html は、↓ 南びわ湖エリア情報の記事(h3タグを使ってる)にします。
pythonコード parse_html_001.py
# coding: shift_jis
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')url = "https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946"
import requests
html = requests.get(url)
print("@@@@@@@@@@_1")
print("htmlを取得したurl")
print(html.url)print("@@@@@@@@@@_2")
print("取得したhtml")
print("長いので先頭の12行のみ表示")
myfile = open("output.txt", "w", encoding="utf-8")
print(html.text, file=myfile)
myfile.close()
import pandas as pd
data = pd.read_table("output.txt")
print(data.head(12))print("@@@@@@@@@@_3")
print("取得したhtmlのtype判定")
html_type = type(html.text)
print(html_type)print("@@@@@@@@@@_4")
from bs4 import BeautifulSoup
soup = BeautifulSoup(html.text)
print(soup.h3.text)
print(soup.find("h3").text)
・1. htmlを取得したurl
・2. 取得したhtml
・3. 取得したhtmlのtype判定
・4. html解析(h3タグ取得)
バッチファイル do_py007.bat
rem do_py007.bat
@echo off
set year=%date:~0,4%
set month=%date:~5,2%
set day=%date:~8,2%
set time2=%time: =0%
set hour=%time2:~0,2%
set minute=%time2:~3,2%
set second=%time2:~6,2%
set logname=%year%-%month%-%day%_%hour%_%minute%_%second%python parse_html_001.py >> %logname%_parse_html_001.txt
pause
出力結果(4)
@@@@@@@@@@_1
htmlを取得したurl
https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946
@@@@@@@@@@_2
取得したhtml
長いので先頭の12行のみ表示
<!DOCTYPE html>
0 <html
1 lang="ja"
2 data-admin-domain="//blog.hatena.ne.jp"
3 data-admin-origin="https://blog.hatena.ne.jp"
4 data-author="minamibiwako"
5 data-avail-langs="ja en"
6 data-blog="minamibiwako.hatenablog.jp"
7 data-blog-comments-top-is-new="1"
8 data-blog-host="minamibiwako.hatenablog.jp"
9 data-blog-is-public="1"
10 data-blog-name="南びわ湖エリア情報"
11 data-blog-owner="minamibiwako"
@@@@@@@@@@_3
取得したhtmlのtype判定
<class 'str'>
@@@@@@@@@@_4
「知識知恵+さんぽ」=聖地巡礼
「知識知恵+さんぽ」=聖地巡礼
head()が正しく動作していることが確認できました。
指定した url の h3タグの1個目が抽出できていることが分かります。
タグを取得するとき:soup.find()
テキストを取得するとき:soup.find().text
【演習】ブログタイトルをテキストで取得する
pythonコード parse_html_002.py
# coding: shift_jis
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')url = "https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946"
import requests
html = requests.get(url)
print("@@@@@@@@@@_1")
print("htmlを取得したurl")
print(html.url)print("@@@@@@@@@@_2")
print("取得したhtml")
print("長いので先頭の12行のみ表示")
myfile = open("output.txt", "w", encoding="utf-8")
print(html.text, file=myfile)
myfile.close()
import pandas as pd
data = pd.read_table("output.txt")
print(data.head(12))print("@@@@@@@@@@_3")
print("取得したhtmlのtype判定")
html_type = type(html.text)
print(html_type)print("@@@@@@@@@@_4")
from bs4 import BeautifulSoup
soup = BeautifulSoup(html.text)
print(soup.find("title").text)
・1. htmlを取得したurl
・2. 取得したhtml
・3. 取得したhtmlのtype判定
・4. html解析(titleタグ取得)
バッチファイル do_py008.bat
rem do_py008.bat
@echo off
set year=%date:~0,4%
set month=%date:~5,2%
set day=%date:~8,2%
set time2=%time: =0%
set hour=%time2:~0,2%
set minute=%time2:~3,2%
set second=%time2:~6,2%
set logname=%year%-%month%-%day%_%hour%_%minute%_%second%python parse_html_002.py >> %logname%_parse_html_002.txt
rem pause
出力結果(5)
@@@@@@@@@@_1
htmlを取得したurl
https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946
@@@@@@@@@@_2
取得したhtml
長いので先頭の12行のみ表示
<!DOCTYPE html>
0 <html
1 lang="ja"
2 data-admin-domain="//blog.hatena.ne.jp"
3 data-admin-origin="https://blog.hatena.ne.jp"
4 data-author="minamibiwako"
5 data-avail-langs="ja en"
6 data-blog="minamibiwako.hatenablog.jp"
7 data-blog-comments-top-is-new="1"
8 data-blog-host="minamibiwako.hatenablog.jp"
9 data-blog-is-public="1"
10 data-blog-name="南びわ湖エリア情報"
11 data-blog-owner="minamibiwako"
@@@@@@@@@@_3
取得したhtmlのtype判定
<class 'str'>
@@@@@@@@@@_4
南びわ湖街道ウォーカー - 南びわ湖エリア情報
ブログタイトル「南びわ湖街道ウォーカー」が取得できていることが分かります。
指定したタグの1個目は取得できたので、複数個の取得について見ていきます。
beautifulsoup(find_all)
find_all は、for文を使って、それぞれのテキストを取得します。
pythonコード parse_html_003.py
# coding: shift_jis
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')url = "https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946"
import requests
html = requests.get(url)
print("@@@@@@@@@@_1")
print("htmlを取得したurl")
print(html.url)print("@@@@@@@@@@_2")
print("取得したhtml")
print("長いので先頭の12行のみ表示")
myfile = open("output.txt", "w", encoding="utf-8")
print(html.text, file=myfile)
myfile.close()
import pandas as pd
data = pd.read_table("output.txt")
print(data.head(12))print("@@@@@@@@@@_3")
print("取得したhtmlのtype判定")
html_type = type(html.text)
print(html_type)print("@@@@@@@@@@_4")
from bs4 import BeautifulSoup
soup = BeautifulSoup(html.text)
print(soup.find("title").text)print("@@@@@@@@@@_5")
for h3_tag in soup.find_all("h3"):
print(h3_tag.text)
・1. htmlを取得したurl
・2. 取得したhtml
・3. 取得したhtmlのtype判定
・4. html解析(titleタグ取得)
・5. html解析(h3タグ取得)
バッチファイル do_py009.bat
rem do_py009.bat
@echo off
set year=%date:~0,4%
set month=%date:~5,2%
set day=%date:~8,2%
set time2=%time: =0%
set hour=%time2:~0,2%
set minute=%time2:~3,2%
set second=%time2:~6,2%
set logname=%year%-%month%-%day%_%hour%_%minute%_%second%python parse_html_003.py >> %logname%_parse_html_003.txt
rem pause
出力結果(6)
@@@@@@@@@@_1
htmlを取得したurl
https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946
@@@@@@@@@@_2
取得したhtml
長いので先頭の12行のみ表示
<!DOCTYPE html>
0 <html
1 lang="ja"
2 data-admin-domain="//blog.hatena.ne.jp"
3 data-admin-origin="https://blog.hatena.ne.jp"
4 data-author="minamibiwako"
5 data-avail-langs="ja en"
6 data-blog="minamibiwako.hatenablog.jp"
7 data-blog-comments-top-is-new="1"
8 data-blog-host="minamibiwako.hatenablog.jp"
9 data-blog-is-public="1"
10 data-blog-name="南びわ湖エリア情報"
11 data-blog-owner="minamibiwako"
@@@@@@@@@@_3
取得したhtmlのtype判定
<class 'str'>
@@@@@@@@@@_4
南びわ湖街道ウォーカー - 南びわ湖エリア情報
@@@@@@@@@@_5
「知識知恵+さんぽ」=聖地巡礼
東海道さんぽ
中山道さんぽ
びわ湖さんぽ
はてなブログをはじめよう!
find_all を使って、h3タグをすべて取得できていることが確認できました。
確かに、上の出力結果(6)には、「はてなブログをはじめよう!」というノイズが含まれていますね。
この記事を書いている時点での見出し(h3タグ)は3つ
東海道さんぽ
中山道さんぽ
びわ湖さんぽ
※2021年2月21日時点の 出力結果(6)ですが、
今後、追記していく記事なので、h3タグが変化している可能性があります。その点、注意願います!
取得する範囲を狭くすることで、ノイズを消すために
articleタグをmybodyに入れます。↓
pythonコード parse_html_004.py
# coding: shift_jis
import io,sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')url = "https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946"
import requests
html = requests.get(url)
print("@@@@@@@@@@_1")
print("htmlを取得したurl")
print(html.url)print("@@@@@@@@@@_2")
print("取得したhtml")
print("長いので先頭の12行のみ表示")
myfile = open("output.txt", "w", encoding="utf-8")
print(html.text, file=myfile)
myfile.close()
import pandas as pd
data = pd.read_table("output.txt")
print(data.head(12))print("@@@@@@@@@@_3")
print("取得したhtmlのtype判定")
html_type = type(html.text)
print(html_type)print("@@@@@@@@@@_4")
from bs4 import BeautifulSoup
soup = BeautifulSoup(html.text)
print(soup.find("title").text)print("@@@@@@@@@@_5")
for h3_tag in soup.find_all("h3"):
print(h3_tag.text)print("@@@@@@@@@@_6_1")
mybody = soup.find("article")
#print(soup.find("article"))print("@@@@@@@@@@_6_2")
for h3_tag in mybody.find_all("h3"):
print(h3_tag.text)
バッチファイル do_py010.bat
rem do_py010.bat
@echo off
set year=%date:~0,4%
set month=%date:~5,2%
set day=%date:~8,2%
set time2=%time: =0%
set hour=%time2:~0,2%
set minute=%time2:~3,2%
set second=%time2:~6,2%
set logname=%year%-%month%-%day%_%hour%_%minute%_%second%python parse_html_004.py >> %logname%_parse_html_004.txt
rem pause
出力結果(7)
@@@@@@@@@@_1
htmlを取得したurl
https://minamibiwako.hatenablog.jp/entry/2021/02/21/084946
@@@@@@@@@@_2
取得したhtml
長いので先頭の12行のみ表示
<!DOCTYPE html>
0 <html
1 lang="ja"
2 data-admin-domain="//blog.hatena.ne.jp"
3 data-admin-origin="https://blog.hatena.ne.jp"
4 data-author="minamibiwako"
5 data-avail-langs="ja en"
6 data-blog="minamibiwako.hatenablog.jp"
7 data-blog-comments-top-is-new="1"
8 data-blog-host="minamibiwako.hatenablog.jp"
9 data-blog-is-public="1"
10 data-blog-name="南びわ湖エリア情報"
11 data-blog-owner="minamibiwako"
@@@@@@@@@@_3
取得したhtmlのtype判定
<class 'str'>
@@@@@@@@@@_4
南びわ湖街道ウォーカー - 南びわ湖エリア情報
@@@@@@@@@@_5
「知識知恵+さんぽ」=聖地巡礼
東海道さんぽ
中山道さんぽ
びわ湖さんぽ
はてなブログをはじめよう!
@@@@@@@@@@_6_1
@@@@@@@@@@_6_2
「知識知恵+さんぽ」=聖地巡礼
東海道さんぽ
中山道さんぽ
びわ湖さんぽ
取得する範囲を狭くすることで、ノイズを消せていることが確認できました。
該当する要素を削除する方法(extractを使う)は「破壊的な」方法になるのでなるべく使わないほうが良いとのことです。
つづきは、pythonでスクレイピング(beautifulsoup 応用編) で
滋賀を盛り上げていく旅は続く
■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■■■■■■■■■■■■■■■■■■■■■■■■■■■■
以下、ネット上の反応
☆
— Scratch & 子どもたち (@scratchhub31) March 10, 2021
Pythonの第一歩は大抵「print('○○○')」なんですが、
ここで「○○○にみんながニッコリするセリフ入れて」って言うと、1ミリも動けなくなっちゃう子がいます。特に学校の勉強ができる子に多い。
構文とかも大事なんだけど、そこんトコが人間のお仕事なんだけどなー、って感じ。#考える力
プログラミング:Pythonが1位、学びたい言語ランキングをオライリーが公表 https://t.co/q4pKPG7WbS
— Webクリエイター ボックス (@webcreatorbox) March 1, 2021