HTTP通信でHTMLを取得して解析する方法。
APIでJSON取得が出来ると良いが、そういうものが見つからない場合にスクレイピング的にHTMLから取得したい。
環境はWindowsServer2012+Python3.9.7
パース
HTMLのパースには
import bs4 # beautifulsoup4 XMLパーサ
を利用する。
pipで取得して以下のようにしてセレクタを使って取得したりする。
selector = "#items"
soup = bs4.BeautifulSoup(html, "html.parser") # パーサーに渡す
result = soup.find(selector)
request
実際にHTMLを取得する方法だが、2つ存在する。
1つはPythonからhttpリクエストを投げてデータを取得する方法。
もう一つはブラウザ経由で取得、つまりReact等で構成されたページの内容を取得する方法。
requestsは一つ目の方法で、簡単に利用できる。
パースと組み合わせた簡単な処理は以下のような感じ。
公式とか参照。
def htmlSearch(key):
proxies = {
"http":"http://proxy:8080",
"https":"http://proxy:8080"
}
params = {'q': key} # ここの書き方が正しいかは見てない
url = "https://www.target.com" + key # URLがキーになっている場合
selector = "#items"
req = requests.get(url,proxies=proxies,params=params)
print(req.url)
print(req.text)
soup = bs4.BeautifulSoup(req.text, "html.parser") # パーサーに渡す
result = soup.find(selector)
print(result) # findは先頭の1件のみなのでget_textが可能
return result
Selenium
ブラウザコントロールのパターン。今回はこれがメイン。Chromeをコントロールする。
ここがある程度参考になりそうだが、バージョン違いなのか、一部上手く行かない事もあった。
まずはpip install selenium
でインストール。
pipでプロキシ使う場合はpip install --proxy="http://proxy:8080" selenium
とする。
Chromeを操作するにはChromeDriverが必要なのでダウンロードしておく。
Python内ではdriver = webdriver.Chrome(executable_path='G:/apps/chromedriver_win32/chromedriver.exe')
のように設定。
設定が間違っているとchromedriver_win32 executable needs to be in PATH.が出る。
これでdriver.get(url)
でChromeにURLを与える事が出来る。
今回入力操作は不要なので省略。
検索結果から値を取得するが、SeleniumにXPathを利用した取得機能が付いているのでそれを利用する。
実際にはReactでの読み込みに少し時間がかかるので待機が必要。
time.sleepやdriver.implicitly_waitを使う。
基本形はこのような形
from selenium import webdriver
from time import sleep
from selenium.webdriver.chrome.options import Options
# key sample: /p/6891/0196/
def chromeSearch(key):
try:
driver = webdriver.Chrome(executable_path='G:/apps/chromedriver_win32/chromedriver.exe')
options = Options()
# options.add_argument('--headless') # ヘッドレスモード
url = "https://www.target.com" + key
selector = '//*[@id="items"]/div[1]/div[3]/div[1]/div[1]/span' # 実際にはXPath検索
sleep(1) # 念のため待つ
driver.get(url)
driver.implicitly_wait(10) # findで要素が見つかるまで最大10秒待機
result = driver.find_element_by_xpath(selector)
print(result.text)
# 使い終わったら閉じる
finally:
driver.close()
driver.quit()
return result.text
実際にはExcelから読み込んで連続検索したりしたいので、外部からdriverを渡すようなメソッドを作る方が良さそう。
上記ソースでブラウザからの取得は上手くいったが、headlessオプションを付けると上手くいかない、といった挙動が発生した。
いくつか情報があって、ブラウザサイズの問題や、サーバ側でヘッドレスモードを弾こうとしている、といったもの。
ヘッドレスが弾かれる場合はヘッダーを偽装する手法は取れそう。
from fake_headers import Headers
header = Headers(
browser="chrome", # Generate only Chrome UA
os="win", # Generate only Windows platform
headers=False # generate misc headers
)
customUserAgent = header.generate()['User-Agent']
options.add_argument("user-agent={customUserAgent}")
とりあえず画面サイズとヘッダ偽装を両方実装する事で対応出来た。