Pythonウェブスクレイピング入門:BeautifulSoupとSeleniumの基本

一、BeautifulSoup4モジュール

beautifulsoup4はHTMLやXMLファイルからデータを抽出するPythonライブラリで、スクレイピングで取得したXMLを解析するために使用されます。

1. インストール
    pip install beautifulsoup4 # bs4モジュールのダウンロード
	pip install lxml  # 解析ライブラリ

2. 使用方法
    '第一引数:解析する文字列'
    '第二引数:使用する解析ライブラリ:html.parser(組み込み、追加インストール不要だが低速)、lxml(追加インストールが必要:pip install lxml)'
    soup=BeautifulSoup('解析する内容(文字列型)','html.parser/lxml')

二、ドキュメントツリーの走査

from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" id='id_xx' xx='zz'>lqz <b>The Dormouse's story <span>彭于晏</span></b>  xx</p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

if __name__ == '__main__':
    # soup = BeautifulSoup(html_doc, 'html.parser')
    soup = BeautifulSoup(html_doc, 'lxml')  # pip install lxml
    # print(soup.find_all(name='html'))

    1. ドキュメントのエラー許容能力
    res = soup.prettify()
    print(res)

    2. ドキュメントツリーの走査:ドキュメントツリー(html開始タグ---->html終了タグ、間に多くのタグが含まれる)
    # .でタグを検索し、最初に見つかった最初のタグのみが見つかります
    print(soup.html)
    print(soup.html.body.p)  # 一層ずつ指定されたタグにたどり着く
    print(soup.p)  # 階層を跨いで直接検索

    3. タグ名の取得
    print(soup.body.name)

    4. タグ属性の取得
    p = soup.html.body.p
    print(p.attrs)  # pタグのすべての属性を取得
    print(p.attrs['class'])  # 指定された一つの属性を取得 HTMLクラス属性は複数設定可能なのでリストに格納されます ['title']
    print(p.attrs.get('xx'))
    print(soup.a.attrs['href'])

    5. タグ内容の取得
    # タグオブジェクト.text
    print(soup.p.b.text) # bタグのすべてのテキストを取得
    # タグオブジェクト.string stringは指定されたタグ内に自分のテキストしかない場合に取得でき、ネストされたタグがある場合はNone
    print(soup.p.b.string)  # None stringは子孫タグを持てない
    print(soup.p.b.span.string)  # 彭于晏
    # タグオブジェクト.strings stringsはジェネレータオブジェクトを取得し、子孫のテキスト内容すべてをジェネレータに格納します
    print(soup.p.b.strings) # textに似ていますが、メモリをより節約します
    print(list(soup.p.b.strings)) #["The Dormouse's story ", '彭于晏']

    6. ネストされた選択
    print(soup.html.head.title)

    '''------参考内容------'''
    7. 子ノード、子孫ノード
    print(soup.p.contents) # pタグ下のすべての子ノードを取得、pタグを一つだけ取得
    print(soup.p.children) # 直接の子ノード、イテレータを取得し、pタグ下のすべての子ノードを含みます
    for i,child in enumerate(soup.p.children):  # list_iterator イテレータ
        print(i,child)
    print(soup.p.descendants) # 孫ノードを取得、pタグ下のすべてのタグが選択されます
    for i,child in enumerate(soup.p.descendants): # generator ジェネレータ
        print(i,child)


    8. 親ノード、祖先ノード
    print(soup.a.parent)  # aタグの親ノードを取得
    print(soup.a.parents) # aタグのすべての祖先ノードを取得 generator
    print(list(soup.a.parents))


    9. 兄弟ノード
    print(soup.a.next_sibling)  # 次の兄弟タグ
    print(soup.a.previous_sibling) # 前の兄弟タグ
    print(list(soup.a.next_siblings))  # 下の兄弟たち=>ジェネレータオブジェクト
    print(soup.a.previous_siblings)  # 上の兄弟たち=>ジェネレータオブジェクト

三、ドキュメントツリーの検索

from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my_p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>
</p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""
soup = BeautifulSoup(html_doc,'lxml')
"""5つのフィルタ: 文字列、正規表現、リスト、True、メソッド """
# find:最初の一つを探す、find_all:すべてを探す
1. 文字列----->検索条件が文字列
res = soup.find(id='my_p')
res=soup.find(class_='boldest')
res=soup.find(href='http://example.com/elsie')
res=soup.find(name='a',href='http://example.com/elsie',id='link1') # 複数のand条件
'以下のように書くこともできますが、nameは書けません'
res = soup.find(attrs={'class':'sister','href':'http://example.com/elsie'})
print(res)


2. 正規表現
import re
res = soup.find_all(href=re.compile('^http'))  # href属性がhttpで始まるすべて
res = soup.find_all(class_=re.compile('^s'))  # classがsで始まるすべて
print(res)

3. リスト
res = soup.find_all(name=['a','b']) # すべてのa/bタグリストを取得
res = soup.find_all(class_=['sister','boldest']) # クラス名がsister、boldestのタグを取得
print(res)


4. ブール値
res = soup.find_all(id=True) # idを持つすべてのタグリストを取得
res = soup.find_all(href=True)  # href属性を持つすべてのタグ
res = soup.find_all(class_=True)  # class_属性を持つすべてのタグ
print(res)


5. メソッド
def has_class_but_no_id(tag):
    # idを持たずclassを持つすべてのタグを検索
    return tag.has_attr('class') and not tag.has_attr('id')
print(soup.find_all(has_class_but_no_id))


6. ドキュメントツリーの検索はドキュメントツリーの走査と組み合わせて使用できます
print(soup.html.body.find_all('p')) # 速度が少し速くなります


7.recursive=True   limit=1 limitパラメータ
print(soup.find_all(name='p',limit=2)) # 最初の2つのpタグのみを取得 取得数を制限
print(soup.find_all(name='p',recursive=False)) # 再帰的に検索するかどうか

四、CSSセレクタ

from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my_p" class="title">asdfasdf<b id="bbb" class="boldest">The Dormouse's story</b>
</p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""
soup = BeautifulSoup(html_doc,'lxml')
'''select内にCSSセレクタを記述'''
res = soup.select('a.sister')
res = soup.select('#link1')
res = soup.select('p#my_p b')
print(res)

'''Webページのコンソールで、対応するタグを右クリックしてCopy selectorを選択できます'''
import requests
header={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
}
res=requests.get('https://www.zdaye.com/free/',headers=header)
# print(res.text)
soup=BeautifulSoup(res.text,'lxml')
res = soup.select('#ipc > tbody > tr:nth-child(2) > td.mtd')
print(res[0].text)

五、Seleniumの紹介とインストール

Seleniumは元々は自動化テストツールでしたが、スクレイパーでは主にrequestsがJavaScriptコードを直接実行できない問題を解決するために使用されます

Seleniumはブラウザを駆動し、ブラウザの操作(ジャンプ、入力、クリック、スクロールなど)を完全にシミュレートして、レンダリング後の結果を取得します。複数のブラウザをサポートします

公式サイト:https://selenium-python.readthedocs.io/

インストールと使用

1.seleniumモジュールのインストール
	pip install selenium
	
	'最新版seleniumモジュールでは、ブラウザドライブのダウンロード手順は不要です(必要な場合はドライブをダウンロード)'
2.ブラウザドライブのダウンロード(ドライブのバージョンは対応するブラウザ(chrome、edgeなど)のバージョンと一致させる必要があります)
	-chromeドライブのダウンロード:https://googlechromelabs.github.io/chrome-for-testing/
	-ダウンロードしたドライブを環境変数に追加するか、プロジェクトのルートディレクトリに直接配置します

3.基本的な使用方法
	from selenium import webdriver
	import time
	bro = webdriver.Chrome()  # ブラウザを手動で開く
	bro.get('https://www.bing.com')  # ブラウザにアクセスするURLを入力してアクセス
	time.sleep(5)
	print(bro.page_source)  # ページ内容を取得
	# これにより、実行後のjsの内容を解析するためにbs4を引き続き使用でき、最も包括的な結果が得られます
	bro.close()  # ブラウザを閉じる

六、Seleniumを使用したログイン(百度)

from selenium import webdriver
from selenium.webdriver.common.by import By
import time
bro = webdriver.Chrome()  # ブラウザ
bro.get('https://www.baidu.com/')
# 待機時間を設定、待機時間を超過すると停止
bro.implicitly_wait(10)  # ページの読み込みを待つ
bro.maximize_window()    # ブラウザを全画面で開く
time.sleep(3)  # 3秒スリープしてから以下の手順を実行
# セレクタを使用してログインタグを見つける
submit_btn = bro.find_element(by=By.ID,value='s-top-loginbtn')
# submit_btn = bro.find_element(by=By.LINK_TEXT,value='登录')  # aタグのテキストで検索
# タグをクリック
submit_btn.click()
time.sleep(5)
'''SMSログインに切り替えてからアカウントログインに切り替える'''
sms_submit = bro.find_element(by=By.ID,value='TANGRAM__PSP_11__changeSmsCodeItem') # キーワード引数渡し
sms_submit.click()
time.sleep(2)
username_submit = bro.find_element(By.ID,'TANGRAM__PSP_11__changePwdCodeItem') # 位置引数渡し
username_submit.click()
time.sleep(2)
'''次にユーザー名とパスワードを入力し、同意にチェックを入れてからログインをクリック'''
# ユーザー名
username = bro.find_element(By.ID,'TANGRAM__PSP_11__userName')
username.send_keys('123456789963') # 入力ボックスに内容を書き込む
time.sleep(1)
# パスワード
password = bro.find_element(By.ID,'TANGRAM__PSP_11__password')
password.send_keys('abcdefjhijkl')
time.sleep(1)
# 同意にチェックを入れる
accept= bro.find_element(By.ID,'TANGRAM__PSP_11__isAgree')
accept.click()
time.sleep(1)
# ログインをクリック
submit = bro.find_element(By.ID,'TANGRAM__PSP_11__submit')
submit.click()
	
bro.close()

七、ヘッドレスブラウザ

スクレイピングでは、ブラウザを開きたくない場合があります。Googleはヘッドレスブラウザをサポートしており、グラフィカルユーザーインターフェース(GUI)のないバックグラウンドで実行されます

import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
	
options = Options()
options.add_argument('blink-settings=imagesEnabled=false')  # 画像をロードしない、速度向上
options.add_argument('--headless')  # ブラウザに可視化ページを提供しない。Linuxシステムが可視化をサポートしない場合、このオプションなしで起動に失敗します
bro = webdriver.Chrome(options=options)
bro.get('https://www.baidu.com') # 対象URL
print(bro.page_source) # ブラウザで表示されるページ内容
time.sleep(2)
bro.close()  # タブ(ラベル)ページを閉じる
bro.quit()  # ブラウザを閉じる

八、タグの検索

'''############1 タグの検索##############
# By.ID  # ID番号でタグを検索
# By.NAME  # name属性でタグを検索
# By.TAG_NAME  # タグ名でタグを検索
# By.CLASS_NAME # クラス名で検索
# By.LINK_TEXT # aタグのテキスト
# By.PARTIAL_LINK_TEXT # aタグのテキスト、あいまい一致
---------以上はselenium独自の--------
	
# By.CSS_SELECTOR # CSSセレクタで検索
# By.XPATH  #xpathで検索
'''
	
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
bro = webdriver.Chrome()
bro.get('https://www.cnblogs.com/liuqingzheng/p/16005896.html')
# bro.implicitly_wait(10)
bro.maximize_window()
	
bro.find_element() # 一つ検索
bro.find_elements() # すべて検索
	
1 IDで検索---いいねボタンを見つけて---クリックする---IDで検索
number=bro.find_element(By.ID,'digg_count')
number.click()
	
2 タグ名で検索   ページ内のすべてのaタグを検索  タグ名で検索
a_list=bro.find_elements(By.TAG_NAME,'a')
print(len(a_list))
	
3 クラス名で検索
dig=bro.find_element(By.CLASS_NAME,'diggit')
dig.click()
	
4 aタグのテキストで検索 By.LINK_TEXT
res=bro.find_element(By.LINK_TEXT,'分布式爬虫')
print(res.text)
print(res.get_attribute('href'))
res.click()
	
5 aタグのテキスト、あいまい一致  By.PARTIAL_LINK_TEXT
res=bro.find_element(By.PARTIAL_LINK_TEXT,'分布式')
print(res.text)
print(res.get_attribute('href'))
res.click()
	
6 cssセレクタで解析
res=bro.find_element(By.CSS_SELECTOR,'a#cb_post_title_url>span')
res=bro.find_element(By.CSS_SELECTOR,'#cb_post_title_url > span')
print(res.get_attribute('role'))
print(res.text)
	
7 xpath解析--->xpath構文を知らない必要なし
res=bro.find_element(By.XPATH,'//*[@id="cb_post_title_url"]/span')
print(res.get_attribute('role'))
print(res.text)
	
time.sleep(5)
bro.close()

九、タグ属性、位置、サイズ、テキスト

'''
############2 タグの属性、テキスト、id(id属性ではない、無関係)、サイズ、位置を取得##############
print(tag.get_attribute('src'))
print(tag.text)
print(tag.id)  # このidはID番号ではなく、気にする必要ありません
print(tag.location)
print(tag.tag_name)
print(tag.size)
# 位置とサイズを使用---》後でキャプチャ画像を取得-->認証コードを解読
'''
import time
	
from selenium import webdriver
from selenium.webdriver.common.by import By
	
bro = webdriver.Chrome()
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
bro.implicitly_wait(10)
bro.maximize_window()
a = bro.find_element(By.LINK_TEXT, '扫码登录')
a.click()
time.sleep(1)
bro.save_screenshot('main.png')  # ページ全体をスクリーンショット
	
# タグの位置と座標を印刷
img=bro.find_element(By.ID,'J-qrImg')
print(img.location)
print(img.size)
	
time.sleep(5)
bro.close()

十、要素の待機

1 暗黙的待機
bro.implicitly_wait(10)  
# 暗黙的待機を設定---》find_elementでタグを検索する際、タグがまだ読み込まれていない可能性がある---》
# コードの実行は非常に速い---》タグが取得できずエラーが発生する
	
'''この一文を追加---》タグを取得する際、タグが読み込まれていない場合---》
最大10秒待機---》タグが読み込まれたら--》見つかれば後続処理を続行'''
	
	
2 明示的待機---》使いにくい
	-各タグを検索するたびに、待機時間を設定する必要がある---》非常に面倒
	-これは無視し、使用しないでください
	
	    
'今後、あるアドレスにアクセスした後、この一文を追加するだけでOK'
bro.implicitly_wait(10)

十一、JavaScriptコードの実行

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
	
bro = webdriver.Chrome()
bro.get('https://www.pearvideo.com/category_1')
bro.implicitly_wait(10)
bro.maximize_window()
	
1 基本的な使用方法
bro.execute_script('alert("美女")')
	
2 変数を出力
bro.execute_script('console.log(urlMap)')
bro.execute_script('alert(JSON.stringify(urlMap))')

3 新しいタブを開く
bro.execute_script('open()')
	
4 画面をスクロール
bro.execute_script('scrollTo(0,document.documentElement.scrollHeight)')
	
5 現在のアクセスアドレスを取得
bro.execute_script('alert(location)')
bro.execute_script('location="http://www.baidu.com"')
	
6 クッキーを出力
bro.execute_script('alert(document.cookie)')
	
	
time.sleep(10)
bro.close()

十二、タブの切り替え

from selenium import webdriver
import time
	
bro = webdriver.Chrome()
bro.get('https://www.pearvideo.com/')
bro.implicitly_wait(10)
print(bro.window_handles)
	# タブを開く
bro.execute_script('window.open()')
	# すべてのタブを取得
	
bro.switch_to.window(bro.window_handles[1]) # 特定のタブに切り替え
bro.get('http://www.taobao.com')
	
time.sleep(2)
bro.switch_to.window(bro.window_handles[0]) # 特定のタブに切り替え
bro.get('http://www.baidu.com')
	
time.sleep(2)
bro.execute_script('window.open()')
bro.execute_script('window.open()')
bro.close() # タブを閉じる
	
time.sleep(2)
bro.quit()  # ページを閉じる

十三、ブラウザの進む・戻るをシミュレート

from selenium import webdriver
import time
bro = webdriver.Chrome()
bro.get('https://www.pearvideo.com/')
bro.implicitly_wait(10)
	
	# すべてのタブを取得
time.sleep(2)
bro.get('http://www.taobao.com')
	
time.sleep(2)
	
bro.get('http://www.baidu.com')
time.sleep(2)
bro.back()
time.sleep(2)
bro.back()
time.sleep(2)
bro.forward()
bro.quit()  # ページを閉じる

十四、Seleniumでcnblogsにログインしてクッキーを取得

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
	# ブラウザの自動制御ソフト検出を回避
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled")  # 自動制御を削除
bro = webdriver.Chrome(options=options)
bro.get('https://www.cnblogs.com/')
bro.implicitly_wait(10)
bro.maximize_window()
	
	
login_btn = bro.find_element(By.LINK_TEXT,'登录')
login_btn.click()
time.sleep(2)
	
	# ユーザー名とパスワード入力ボックスを見つける
username = bro.find_element(By.CSS_SELECTOR, '#mat-input-0')
password = bro.find_element(By.ID, 'mat-input-1')
username.send_keys('@qq.com')  # アカウント
time.sleep(2)
password.send_keys('#')  # パスワード
time.sleep(2)
	
submit_btn = bro.find_element(By.CSS_SELECTOR,'body > app-root > app-sign-in-layout > div > div > app-sign-in > app-content-container > div > div > div > form > div > button')
time.sleep(2)
submit_btn.click()  # 一つの場合は直接ログイン成功 一つの場合は認証コードが表示される
	
	# 認証クリック
code=bro.find_element(By.ID,'rectBottom')
code.click()
time.sleep(2)
time.sleep(5)
	# クッキーを取得
cookies = bro.get_cookies() # すべてのクッキーを取得
# bro.get_cookie() # 特定のクッキーを取得
# クッキーを取得後、ローカルに保存、json形式で後でredisに保存可能
import json
	
with open('cnblogs.json', 'wt', encoding='utf-8') as f:
    json.dump(cookies, f)
	    
bro.close()

十五、取得したクッキーを使用してcnblogsにログイン

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
	# ブラウザの自動制御ソフト検出を回避
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled")  # 自動制御を削除
bro = webdriver.Chrome(options=options)
bro.get('https://www.cnblogs.com/')
bro.implicitly_wait(10)
bro.maximize_window()
	
import json
	# クッキーを読み出し、ブラウザに書き込む
with open('cnblogs.json','rt',encoding='utf-8')as f:
    cookies=json.load(f)
	# []で囲まれた辞書形式なので
for cookie in cookies:
    bro.add_cookie(cookie)
time.sleep(2)  # 2秒スリープ
	
bro.refresh() # ページを更新
time.sleep(5)
bro.close()

タグ: BeautifulSoup Selenium Python ウェブスクレイピング HTML解析

7月31日 23:20 投稿