popoponnponpopo’s blog

popoponnponpopo’s blog

第二の誕生

python3でスクレイピングをやってみる

■長い謝辞

  • スクレイピングを学んでいるのでここに学習結果を書き足していくページ
  • 金の有無によって人の学習効率が変わるという不条理が嫌いなので、有料の本を一切買わずに無料のネットだけで知識をいかに効率的に得ていけるかが自分の今の課題
  • よってネットから全て引っ張ってくる。そのため知識の得方が断片的になるためこのページも「基礎から積み上げて徐々にレベルアップ」でなく「断片的にコードを書く」という感じになる
  • また、面倒なためAPIの説明は無いので事例集みたいになる
  • 例えば「find()」という関数を説明も無しに使ってるがこれは「最初に出てきたやつだけに反応するやつ」であるから紛らわしい。「find_all()」は全てに反応してくれるが、そこらへんの説明を省く
  • 飽きたら更新を辞める


スクレイピングの前提知識

  • まずスクレイピングする時は基本的に「このhtmlタグのこのテキスト!」というのを特定して、そこを取ってくる形になるが、それはクロームではそのテキストを右クリックして「検証」を押せば表示されてくれる。
  • その「このhtmlタグのこのテキスト!」と指定する書法は大きく分けて2つ見つけた。それはCSSセレクタの書法と、XPathという書法。
  • セレクタの書法は知ってたけどXPathは初めて知った
  • 一気にアクセスしまくると「スパム」と見なされブロックされる
  • スクレイピングしたデータを販売するのは禁止になっていたりする場合があるので規約を読む
  • サイトによっては「public api」という形としてデータを整形された形で公開している場合がある(特に仮想通貨の取引所等はそうである)ので、それがある場合はそちらを拾ってきたほうが早いが、そういうの公開されてないサイトは自力で細かくスクレイピングしないといけない
  • 一気に複数サイトを回る場合は、マルチスレッドとか非同期とかのプログラムにしたほうが速いらしい


■各ライブラリのイメージ

  • 初心者のイメージです。もちろん間違ってるかもしれないし、誤解は普通にあると思う
  • (フェッチとパースという言葉を使うが一般的に合ってるのかわからない。スクレイピングは「フェッチ」⇒「パース」⇒「保存または表示」という流れだと今脳内ではなってる)
  • requests(フェッチ&パース。httpリクエストとhttpレスポンスを直接扱う感じ)
  • urllib.request(フェッチのみ?)
  • Scrapy(フェッチ&パース。少し規模大きめのに良いらしい)
  • BeautifulSoup(パースのみ?小規模なスクレイピングに適してるらしい)
  • lxml(パースのみ?わりと高速でパースしてくれるっぽい)
  • selenium(ブラウザ動かすやつ)
  • splinter(これもたぶんブラウザ動かすやつ)

(ブラウザのドライバとして「chromedriver」と「phantomJS」というのがあった。これらはブラウザそのものだけど、前者はGUIありで後者は無しなので、前者はテストで実際に動くか試す時に使って後者は実践時に使えるかな?そしてどちらもブラウザなので、javascriptを実行もできるので、「jsでデータベースから本文を取り出して表示」 とかjsでコンテンツを出してくるサイトをスクレイピングする場合、ちゃんとそれもスクレイピングできる)
(jsとかDOMとか読み込みたいならたぶんseleniumかsplinterかその他何かを使ってなんらかのブラウザを起動してそっから自動ブラウジングすればいいっぽい。seleniumかsplinterかはどっちでもよくて、一番違うのはそれらで起動するブラウザにchromedriverかphantomJSのどちらを選ぶかの方が問題っぽい。GUIありかなしかという(二回目)。わからないけど)

■悲しいことに

  • 以下にコード例を書いていこうとしてるけどコメントの位置が後ろになっていくと悲しいことに変な所で改行されて見づらいことになってしまったため、コメントはセクションごとに1つという寂しい形にした
  • 本当はもっと入れたい


■コード例


 
#インポート
import urllib.request	
from bs4 import BeautifulSoup
import csv
from datetime import datetime

#変数
targetUrl = "http://www.bloomberg.com/quote/SPX:IND"

#処理
#フェッチ
res = urllib.request.urlopen(targetUrl)	
#パース
parsedHtml = BeautifulSoup(res, "html.parser")
h1_name = parsedHtml.find("h1", attrs={"class": "name"})
h1_name_text = h1_name.text.strip(" ")		
div_price = parsedHtml.find("div", attrs={"class":"price"})
div_price_text = div_price.text	

#出力(表示)
print(h1_name_text)
print(div_price_text)

#出力(ファイル)
with open("out.csv","a") as csv_file:
	#変数
	writer=csv.writer(csv_file)
	
	#出力(追加書き込み)
	writer.writerow([h1_name_text, div_price_text, datetime.now()])
 



 
#インポート
import urllib.request		
from bs4 import BeautifulSoup	
import csv			
from datetime import datetime	
	
#変数
targetUrls=["http://www.bloomberg.com/quote/SPX:IND", "http://www.bloomberg.com/quote/CCMP:IND"]
fetchedData = []

#処理
for targetUrl in targetUrls:
	#フェッチ
	res = urllib.request.urlopen(targetUrl)	
	#パース
	parsedHtml = BeautifulSoup(res, "html.parser")
	h1_name = parsedHtml.find("h1", attrs={"class": "name"})
	h1_name_text = h1_name.text.strip(" ")			
	div_price = parsedHtml.find("div", attrs={"class":"price"})
	div_price_text = div_price.text	

	#追加
	fetchedData.append((h1_name_text , div_price_text ))

#出力(ファイル)
with open("out2.csv","a") as csv_file:
	#変数
	writer=csv.writer(csv_file)
	
	#出力(追加書き込み)
	for name, price in fetchedData:	
		writer.writerow([name, price, datetime.now()])
	



 
from lxml import html
import requests

#変数
targetUrl = "http://econpy.pythonanywhere.com/ex/001.html"

#処理
#フェッチ
res = requests.get(targetUrl)	
htmlTree = html.fromstring(res.content)	
buyers = htmlTree.xpath('//div[@title="buyer-name"]/text()')
prices = htmlTree.xpath('//span[@class="item-price"]/text()')

#出力(ファイル)
with open("out3.csv","a") as csv_file:
	#変数
	writer=csv.writer(csv_file)

	#出力(追加書き込み)
	writer.writerow([",".join(buyers), datetime.now()])
	writer.writerow([",".join(prices), datetime.now()])
 



  • 以下「ブザウザを自動操作」する例。XPathも使う。
  • 参考URLはここhttps://hackernoon.com/mastering-python-web-scraping-get-your-data-back-e9a5cc653d88
  • なお実行するには「chromedriver.exe」をpythonのインストールフォルダの「Scripts」フォルダの中にダウンロードして持ってくる必要がある。あと「pip install splinter」と「pip install pandas」でライブラリインストールも。
  • (グーグルの検索ボタンをクリックする前にいまいちなことしてる。本当はjsでボタンを押す方式にしたほうがいいけど眠いからまた今度)
 
#インポート
from splinter import Browser
import pandas				

#変数
xpath_searchBar='//*[@id="lst-ib"]'		
xpath_searchButton='//*[@id="tsf"]/div[2]/div[3]/center/input[1]'
xpath_null='//*[@id="lga"]'					
xpath_searchResults = '//h3[@class="r"]/a' 		

#処理(アクセス)
#起動
browser = Browser('chrome')	#これをphantomjsにしたらgui無しにできる			
browser.driver.set_window_size(840, 780)			
browser.visit('https://www.google.com')		
searchBar = browser.find_by_xpath(xpath_searchBar)[0] 	
searchBar.fill("ランランルー")						
temp_null = browser.find_by_xpath(xpath_null)		
temp_null.click()								
search_button = browser.find_by_xpath(xpath_searchButton)[0]	
search_button.click()							
search_results = browser.find_by_xpath(xpath_searchResults)	
#スクレイプ
scraped_data = []					
for search_result in search_results:
	#変数
	title = search_result.text  		
	link = search_result["href"]		
	scraped_data.append((title, link)) 	

#出力(csv)
df = pandas.DataFrame(data=scraped_data,columns=["Title", "Url"]) 
df.to_csv("out4.csv", sep='\t')								
 



  • 以下「複数ページに行う例」の「マルチプロセスバージョン」
  • よくわからないままコピペしてるからたぶん雑になってるけどたぶん一応マルチプロセスになってる
  • 参考URLはここ(https://qiita.com/kokumura/items/2e3afc1034d5aa7c6012
 
#インポート
import urllib.request		
from bs4 import BeautifulSoup	
import csv			
from datetime import datetime	
	
def scrape(targetUrl):
	#処理
	#フェッチ
	res = urllib.request.urlopen(targetUrl)	
	#パース
	parsedHtml = BeautifulSoup(res, "html.parser")
	h1_name = parsedHtml.find("h1", attrs={"class": "name"})
	h1_name_text = h1_name.text.strip(" ")			
	div_price = parsedHtml.find("div", attrs={"class":"price"})
	div_price_text = div_price.text	
	
	return [h1_name_text,div_price_text]


def writecsv(results):
	#出力(ファイル)
	with open("outfile.csv","a") as csv_file:
		#変数
		writer=csv.writer(csv_file)
		
		#出力(追加書き込み)
		for result in results:
			writer.writerow([result[0], result[1], datetime.now()])
		
import concurrent.futures

if __name__=='__main__':
	#変数
	targetUrls=["http://www.bloomberg.com/quote/SPX:IND", "http://www.bloomberg.com/quote/CCMP:IND"]
	executor = concurrent.futures.ProcessPoolExecutor(max_workers=4)	
	results = []
	
	#処理
	futures = [executor.submit(scrape,targetUrl) for targetUrl in targetUrls]
	for future in concurrent.futures.as_completed(futures):	
		results.append(future .result()) 	
	writecsv(results)
	
	#終了
	executor.shutdown()
 



  • 以下「ブザウザをGUI無しで開いて自動操作」する例の「dom表示されるまで俺は特に待たない」という俺様版(GUI有りにするには単にphantomjsのところをchromeにするだけ。ドライバー入れてたらそれだけ)
  • 参考URLはここhttps://qiita.com/beatinaniwa/items/72b777e23ef2390e13f8
  • 日本人のコンテンツはほぼ写しみたいなことするのが気が憚れるのでURL紹介のみ
  • 感想は「そういえばGUI出す必要無いやん!」です
  • でもこれはdomを表示するまでの待ち時間を作ってないから、表示に時間かかるサイトだとちゃんとデータを持ってこれない(↓で「time.sleep(秒)」をやってそれを回避してる)



  • 次は「ブラウザをGUI無しで開いて自動操作」する例の「dom表示されるまで3秒待つわよ」版
  • 参考URLはここhttp://www.yoheim.net/blog.php?q=20170302
  • 日本人のコンテンツだけどちょっとコピペ・・・少し変えて・・・
  • time.sleep(秒)をやってるおかげでdomがちゃんと表示されてからのhtmlをゲットできてる(↑の例でも別にこの工程かませば同じことになるはず)
 
 # encoding : utf-8
import sys
import os
import requests
from selenium import webdriver
from bs4 import BeautifulSoup
import time

#phantomJS設定
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
Language = "ja,en-US";
log_path = 'ghostdriver.log'
service_args = ["--webdriver-loglevel=INFO" , '--ignore-ssl-errors=yes',]

def scraping(url):
	#事務
	driver = webdriver.PhantomJS(      #ドライバ設定
		desired_capabilities={
			   'phantomjs.page.settings.userAgent': USER_AGENT,
			   'phantomjs.page.customHeaders.Accept-Language' : Language,
			},
		service_log_path=log_path,
		service_args=service_args
	)
	
	#処理
	#アクセス
	driver.get(url)                                   #アクセス
	time.sleep(3)                                    #JS実行待ち
	raw_html = driver.page_source       #生htmlゲット
	driver.save_screenshot("ss.png")     #スクリーンショット
	#パース
	parsed_html = BeautifulSoup(raw_html, "lxml")#パース後html
	header = parsed_html.find("head")	 #ヘッダ
	title = header.find("title").text          #タイトル
	print(header)
	print(title)
	
	#終了処理
	driver.quit()

if __name__ == '__main__':
	#エラー処理
	if len(sys.argv) != 2:
		print("Usage: python c.py [url]")
		exit()
	#スクレイピング
	scraping(sys.argv[1])
 



  • 以下「ブラウザで検索してみる例」の一つ
  • chrome」のところを「phantomjs」にしたらGUIが出ないようになる(ただしどちらもドライバをインストール済みであること)
  • splinterでやるとクリックとフォーム入力というブラウジングの基本機能が簡単にできる気がする
  • クリックとかフォーム入力とかの例になる
  • 本当は画像保存までできたらと思ったけどそれはできなかった
 
from splinter import Browser	
import time

#処理
with Browser('chrome') as browser: 	#chromedriverを開く
	browser.visit('https://www.google.co.jp')	#アクセス
	browser.find_by_id('lst-ib').click()		#クリック(検索窓)
	browser.find_by_id('lst-ib').fill(u"俺やで!")	#入力
	time.sleep(1)
	browser.find_by_value("Google 検索").click()	#クリック(検索ボタン)
	time.sleep(1)
	browser.find_by_css("a.iu-card-header").first.click()		
	time.sleep(10)