Beautiful Soup

リファレンス

<h2>headline2</h2>
<h3>headline3</h3>

<div class="box">
  <ul>
    <li>list 1</li>
    <li>list 2</li>
    <li><span>list 3</span></li>
  </ul>
</div>

ソース読み込み

import urllib.request
from bs4 import BeautifulSoup

import pandas as pd
import time

ローカルファイル

soup = BeautifulSoup(open('import.html'), 'html.parser')

公開ウェブページ

# ユーザーエージェント編集
ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) \
    AppleWebKit/537.36 (KHTML, like Gecko) \
    Chrome/90.0.4430.212 Safari/537.36'

req = urllib.request.Request(url, headers={'User-Agent': ua})
html = urllib.request.urlopen(req)
soup = BeautifulSoup(html, "html.parser")

要素の取得

find 系と select

  • 各種タグの取得には find 系と select 系の2種がある
  • それぞれ記法が異なる場合があるが、select 系の方が直感的に HTML タグを取得しやすい

Beautiful Soup のfind_all( ) と select( ) の使い方の違い - ガンマソフト株式会社

該当する要素を1つだけ抽出: select_one()

html = '''
<div id="wrapper">
  <div class="box">
    <ul>
      <li><a href="http://localhost:8888/mamp_1">MAMP_1</a></li>
      <li><a href="http://localhost:8888/mamp_2">MAMP_2</a></li>
      <li><a href="http://localhost:8888/mamp_3">MAMP_3</a></li>
    </ul>
  </div>
  <div class="box">
    <ul>
      <li><a href="http://localhost:8888/mamp_4">MAMP_4</a></li>
      <li><a href="http://localhost:8888/mamp_5">MAMP_5</a></li>
      <li><a href="http://localhost:8888/mamp_6">MAMP_6</a></li>
    </ul>
  </div>
</div>
'''

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')

div = soup.select_one('.box')
print((div))
# <div class="box">
# <ul>
# <li><a href="http://localhost:8888/mamp_1">MAMP_1</a></li>
# <li><a href="http://localhost:8888/mamp_2">MAMP_2</a></li>
# <li><a href="http://localhost:8888/mamp_3">MAMP_3</a></li>
# </ul>
# </div>

print(type(div))
# <class 'bs4.element.Tag'>

div = str(soup.select_one('.box'))
print(type(div))
# <class 'str'>

該当する要素をすべて抽出: select()

  • select() で要素を取得すると <class 'bs4.element.ResultSet'> となるが、通常のリストと同じようにアクセスできる。
html = '''
<div id="wrapper">
  <div class="box">
    <ul>
      <li><a href="http://localhost:8888/mamp_1">MAMP_1</a></li>
      <li><a href="http://localhost:8888/mamp_2">MAMP_2</a></li>
      <li><a href="http://localhost:8888/mamp_3">MAMP_3</a></li>
    </ul>
  </div>
  <div class="box">
    <ul>
      <li><a href="http://localhost:8888/mamp_4">MAMP_4</a></li>
      <li><a href="http://localhost:8888/mamp_5">MAMP_5</a></li>
      <li><a href="http://localhost:8888/mamp_6">MAMP_6</a></li>
    </ul>
  </div>
</div>
'''

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')

div = soup.select('.box')
print((div))
# [<div class="box">
# <ul>
# <li><a href="http://localhost:8888/mamp_1">MAMP_1</a></li>
# <li><a href="http://localhost:8888/mamp_2">MAMP_2</a></li>
# <li><a href="http://localhost:8888/mamp_3">MAMP_3</a></li>
# </ul>
# </div>, <div class="box">
# <ul>
# <li><a href="http://localhost:8888/mamp_4">MAMP_4</a></li>
# <li><a href="http://localhost:8888/mamp_5">MAMP_5</a></li>
# <li><a href="http://localhost:8888/mamp_6">MAMP_6</a></li>
# </ul>
# </div>]

print(type(div))
# <class 'bs4.element.ResultSet'>

div = soup.select('.box li')
for d in div:
    print(d)
    # <li><a href="http://localhost:8888/mamp_1">MAMP_1</a></li>
    # <li><a href="http://localhost:8888/mamp_2">MAMP_2</a></li>
    # <li><a href="http://localhost:8888/mamp_3">MAMP_3</a></li>
    # <li><a href="http://localhost:8888/mamp_4">MAMP_4</a></li>
    # <li><a href="http://localhost:8888/mamp_5">MAMP_5</a></li>
    # <li><a href="http://localhost:8888/mamp_6">MAMP_6</a></li>

    print(type(d))
    # <class 'bs4.element.Tag'>

    print(type(str(d)))
    # <class 'str'>

get_text() HTMLタグを除去して文字列のみを取得する

strip() 文字列前後の余計な空白を削除 & 改行を削除

div = soup.select_one('.box').get_text().strip()

None (<class 'NoneType'>) の扱い

  • BeautifulSoup で存在しないタグを取得しようとすると None が返る

None の判定

x = None
print('True') if x is None else print('False')
# True
  • 以下のような関数を定義しておくと、エラーを除外しつつ余計な空白や改行を取り除いた文字列のみを取得できる
def select_one_text(soup, elm):
    res = soup.select_one(elm)
    return res.get_text().strip() if res is not None else None

a タグ

URLを取得

url_list = soup.select('.box a')
print(url_list)
# [<a href="http://localhost:8888/mamp_1">MAMP_1</a>, <a href="http://localhost:8888/mamp_2">MAMP_2</a>, <a href="http://localhost:8888/mamp_3">MAMP_3</a>, <a href="http://localhost:8888/mamp_4">MAMP_4</a>, <a href="http://localhost:8888/mamp_5">MAMP_5</a>, <a href="http://localhost:8888/mamp_6">MAMP_6</a>]

for u in url_list:
    url = u.get('href')
    print(url)
    # http://localhost:8888/mamp_1
    # http://localhost:8888/mamp_2
    # http://localhost:8888/mamp_3
    # http://localhost:8888/mamp_4
    # http://localhost:8888/mamp_5
    # http://localhost:8888/mamp_6

target_url_list = []

for u in url_list:
    url = u.get('href')
    url = url.split('?')[0] # URLパラメーター削除
    target_url_list.append(url)

print(target_url_list)
# ['http://localhost:8888/mamp_1', 'http://localhost:8888/mamp_2', 'http://localhost:8888/mamp_3', 'http://localhost:8888/mamp_4', 'http://localhost:8888/mamp_5', 'http://localhost:8888/mamp_6']

img タグ

html ='\
<img src="https://sample.jpg" alt="data-src" class="image_src">\
<img data-src="https://sample.jpg" alt="data-src" class="image_data_src">'

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')

img_src = soup.select('.image_src')
img_data_src = soup.select('.image_data_src')

for i in img_src:
    i = i.get("src")
    print(i)

# 画像が'data-src'の場合
for i in img_data_src:
    i = i.get("data-src")
    print(i)

画像ファイル名を取得

imgs = ['https://hoge.com/img/common/a.png', 'https://hoge.com/img/common/b.png', 'https://hoge.com/img/common/c.jpg', 'https://hoge.com/img/common/d.png', 'https://hoge.com/img/common/e.gif']

for im in imgs:
    filename = im.split('/')[-1]
    print(filename)
    # a.png
    # b.png
    # c.jpg
    # d.png
    # e.gif

HTMLタグ内の文字列を取得

txt = soup.select_one('.txtElm').string

前後要素の検索

ユーザーエージェント変更

エラー処理

エラーが起こってもループを止めない

URLリクエスト

from bs4 import BeautifulSoup
import urllib.request
from urllib.error import HTTPError
from urllib.error import URLError
from http.client import RemoteDisconnected
import random

url_list = ['hoge']
user_agent = ['hoge']

for u in url_list:
    try:
        ua = user_agent[random.randrange(0, len(user_agent), 1)] #ユーザーエージェントをランダムで生成
        req = urllib.request.Request(u, headers={'User-Agent': ua})
        html = urllib.request.urlopen(req)
        soup = BeautifulSoup(html, "html.parser")

    # ////////////////////////////////////////////////////////////////////////
    except HTTPError as e:
        continue
    except URLError as e:
        continue
    except RemoteDisconnected:
        continue

スクレイピング

for f in files:
    try:
        text = soup.select('.text')
        content = ''
        for t in text:
            t = str(t.get_text().strip()) + '\n\n'
            content += t
    except:
        content = ''

NoneType の条件分岐

found = li.find('a')
if found is not None:
    print(found.text)

画像URLから画像ダウンロード

import urllib.error
import urllib.request

fName = 'IMGfile_name'

def download_file(url, dst_path):
    try:
        with urllib.request.urlopen(url) as web_file:
            data = web_file.read()
            with open(dst_path, mode='wb') as local_file:
                local_file.write(data)
    except urllib.error.URLError as e:
        print(e)

url = 'https://sample.jpg'
dst_path = 'img/' + fName + '.jpg' # 保存先フォルダとファイル名を指定
download_file(url, dst_path)

https://note.nkmk.me/python-download-web-images/

画像URLから画像を検証する

  • 画像ファイルが gif であったり、大きさが小さいかなどを検証する
  • pillowモジュールの Image.open() に直接画像 URL を記述しても値を取得できない

https://qiita.com/tamanobi/items/e135839bb8115792c185

import requests
from PIL import Image
import io

img_url = ['https://sample-1.jpg', 'https://sample-2.jpg']

for i in img_url:
    img = Image.open(io.BytesIO(requests.get(i).content))
    if img.format == 'GIF' or img.size[0] <= 800:
        continue
    else:
        print(img)