python3.doc_70
最終投稿日:2022年6月18日
※Anaconda3 があれば入っています ※ライブラリ詳細はこちらから( urllib )
● 画像や HTML を取込み保存する 例) import os import urllib.request url = 'http://uta.pw/shodou/img/28/214.png' dir = os.path.dirname(__file__) savename = 'hoge.png' #保存先ファイルパス file_path = os.path.join( dir, savename ) #ファイルの取込み・保存(第1:取込み先 URL、第2:保存先) urllib.request.urlretrieve(url, file_path) print('保存したよ') ● 画像を一旦メモリに取込み保存する 例) import os import urllib.request url = 'http://uta.pw/shodou/img/28/214.png' dir = os.path.dirname(__file__) savename = 'hoge.png' file_path = os.path.join( dir, savename ) #url の内容をオープンし、read メソッドでメモリに読込む mem = urllib.request.urlopen(url).read() #保存するファイルを上書きモードで開く with open(file_path, mode='wb') as f: #そのまま保存する f.write(mem) print('保存したよ') ● URL の内容を表示する 単純なページの内容を取り込むサンプルです。 http://api.aoikujira.com/ip/ini 上記 URL はクライアントの接続状況を表示する単純なページです。 直接アクセスすると以下の様な感じ ------------------------------------------------------------------------------------------ [ip] API_URI=http://api.aoikujira.com/ip/get.php REMOTE_ADDR=27.136.34.223 REMOTE_HOST=... ------------------------------------------------------------------------------------------ 例) import urllib.request url = 'http://api.aoikujira.com/ip/ini' data = urllib.request.urlopen(url).read() #read メソッドではバイナリデータで取得するので、デコードする text = data.decode('utf-8') print(text) 結果) [ip] API_URI=http://api.aoikujira.com/ip/get.php REMOTE_ADDR=27.136.34.223 REMOTE_HOST=27-136-34-223.rev.home.ne.jp REMOTE_PORT=53078 HTTP_HOST=api.aoikujira.com HTTP_USER_AGENT=Python-urllib/3.6 HTTP_ACCEPT_LANGUAGE= HTTP_ACCEPT_CHARSET= SERVER_PORT=80 FORMAT=ini ● URL の内容を表示する http://api.aoikujira.com/zip/xml/get.php?fmt=xml&zn=2770841 上記パラメータ付き URL は XML 形式で郵便番号 2770841 の情報を取得する API です 例) import urllib.request import urllib.parse api = 'http://api.aoikujira.com/zip/xml/get.php' values = { 'fmt':'xml', 'zn':'2770841' } params = urllib.parse.urlencode(values) url = api + '?' + params data = urllib.request.urlopen(url).read() text = data.decode('utf-8') print(text) 結果) <?xml version="1.0" encoding="utf-8" ?> <address result="1"> <header> <result>1</result> <api>api.aoikujira.com/zip</api> <version>1.1</version> </header> <value> <zip>2770841</zip> <ken>千葉県</ken> <shi>柏市</shi> <cho>あけぼの</cho> <disp>千葉県柏市あけぼの</disp> <kenkana>チバケン</kenkana> <shikana>カシワシ</shikana> <chokana>アケボノ</chokana> </value> </address>
※Anaconda3 があれば入っています ※ライブラリ詳細はこちらから( BeautifulSoup )
● HTML 構造から取得する 例) from bs4 import BeautifulSoup html = """ <html> <head></head> <body> <h1>タイトルだよ~</h1> <p>最初のPタグだよ</p> <p>次のPタグだよ</p> </body> </html> """ #第1:HTML 文字列、第2:解析をするパーサー名(HTML の場合は html.parser) soup = BeautifulSoup(html, 'html.parser') #タグ名をドットで区切って目的の値を取得する h1 = soup.html.body.h1 p1 = soup.html.body.p #next_sibling で次の要素を取得 # (2回記述する理由は、次の要素は改行文字とかになるため) p2 = p1.next_sibling.next_sibling print(h1.string) print(p1.string) print(p2.string) 結果) タイトルだよ~ 最初のPタグだよ 次のPタグだよ ● find メソッドを使って取得する 例) from bs4 import BeautifulSoup html = """ <html> <head></head> <body> <p id='hoge'>最初のPタグだよ</p> <div id='foo'>hoge</div> </body> </html> """ soup = BeautifulSoup(html, 'html.parser') v1 = soup.find(id='hoge') v2 = soup.find(id='foo') print(v1.string) print(v2.string) 結果) 最初のPタグだよ hoge ● find_all メソッドを使って取得する a タグの様に複数取得できる場合に使えます 例) from bs4 import BeautifulSoup html = """ <html> <head></head> <body> <a href='./hoge/hoge.html'>リンク1</a> <a href='./hoge/foo.html'>リンク2</a> </body> </html> """ soup = BeautifulSoup(html, 'html.parser') links = soup.find_all('a') for a in links: print(a.attrs['href']) print(a.string) 結果) ./hoge/hoge.html リンク1 ./hoge/foo.html リンク2 ● 正規表現を組み合わせる 例)以下の HTML に対して <a href='http://hoge.com'>hoge</a> <a href='https://foo.com'>foo</a> <a href='http://bar.com'>bar</a> <a href='https://fuga.com'>fuga</a> 以下の様に正規表現で取得できる from bs4 import BeautifulSoup import re soup = BeautifulSoup(html, 'html.parser') links = soup.find_all(href=re.compile(r'^https://')) for a in links: print(a.attrs['href']) 結果) https://foo.com https://fuga.com ● urllib.request を使いネット上から HTML を取得し解析する 例) from bs4 import BeautifulSoup import urllib.request url = 'http://api.aoikujira.com/zip/xml/2770841' res = urllib.request.urlopen(url) soup = BeautifulSoup(res, 'html.parser') ken = soup.find('ken').string shi = soup.find('shi').string cho = soup.find('cho').string print(ken + shi + cho) 結果) 千葉県柏市あけぼの ● CSS セレクタを使う 例) from bs4 import BeautifulSoup import urllib.request html = """ <html> <head></head> <body> <div id='meigen'> <h1>トルストイの名言</h1> <ul class='items'> <li>ほげです~</li> <li>ふぅです~</li> <li>ばぁです~</li> </ul> </div> </body> </html> """ soup = BeautifulSoup(html, 'html.parser') h1 = soup.select_one('div#meigen > h1').string print(h1) li_list = soup.select('div#meigen > ul.items > li') for li in li_list: print(li.string) 結果) トルストイの名言 ほげです~ ふぅです~ ばぁです~
JOSN を返却する API を解析するサンプル
API は以下を使います
https://api.gnavi.co.jp/RestSearchAPI/20150630/?
keyid=be7d309da17016076a5065cff12a362f&format=json
※ぐるなびの店舗検索用のテストAPIで、
常にkeyidが変わりますので URL はサイトから都度取得する
(https://api.gnavi.co.jp/api/tools/?apitype=ver1_RestSearchAPI)
まずは、以下のコードを使って返却された JSON を展開してみる
例)
import json
import requests
# リスト型、辞書型を再帰的に展開するメソッド
def pars_obj(obj):
if type(obj) == list:
for v in obj:
if type(v) in (str, int):
print(' ' + v)
elif type(v) in (dict, list):
pars_obj(v)
else:
print('あれ。。')
exit()
else:
for k, v in obj.items():
print(' [' + k + ']')
if type(v) in (str, int):
print(' ' + v)
elif type(v) in (dict, list):
pars_obj(v)
else:
print('あれ。。')
exit()
return
# メイン処理
api = ('https://api.gnavi.co.jp/RestSearchAPI/20150630/?'
+ 'keyid=e7ed74287d98fc5d395bc178742d6b0b&format=json')
res = requests.get(api)
data = json.loads(res.text)
for k, v in data.items():
print('【' + k + '】')
if type(v) in (str, int):
print(' ' + v)
elif type(v) in (dict, list):
#再帰関数で文字列になるまで頑張る
pars_obj(v)
else:
print('あれ。。')
exit()
結果)
【@attributes】
【api_version】
20150630
【total_hit_count】
535750
【hit_per_page】
10
【page_offset】
1
【rest】
【@attributes】
【order】
0
【id】
a127635
【update_date】
2018-05-29 01:42:42
【name】
くいもの屋わん 新橋店
【name_kana】
クイモノヤワン シンバシテン
................
上記結果を見るとヒットした件数は 535750 件、表示するのは 10 件ぽい事がわかり、
【rest】以降に店舗データが出力されると予想できます。
※現に【name】の数は 10 件でした。
また、上記を踏まえてダンプした JSON を見ると以下の構造で取得できそうです。
[ JSON の構造 ]
{'hoge':{...},'foo':{...},'rest':[{'bar':'ほげ','name':'店舗名'},...]}
店舗名を取得するには以下のルールで取得できそうです。
【1】辞書型変数をループ処理し、Key に rest を見つける
【2】rest を見つけたらリスト型のループ処理する
【3】各要素は辞書型なので、更にループ処理し、Key に name を見つける
【4】見つかった Key = name の値が店舗名です。
以下ソース)
import requests
import json
api = ('https://api.gnavi.co.jp/RestSearchAPI/20150630/?'
+ 'keyid=be7d309da17016076a5065cff12a362f&format=json')
res = requests.get(api)
data = json.loads(res.text)
for k, v in data.items(): ※【1】
if k == 'rest':
for obj in v: ※【2】
for key, val in obj.items(): ※【3】
if key == 'name': ※【4】
print(val) ※ 店舗名
break
以下は横浜市総務省の防災関連データです
このデータを XML で受け取り解析します。
http://www.city.yokohama.lg.jp/somu/org/kikikanri/data/shelter.xml
取得データ)
<ShelterList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<shelter>
<Name>生麦小学校</Name>
<Ward>鶴見区</Ward>
<Address>生麦四丁目15番1号</Address>
<Notes>被災した住民の避難生活の場所、情報受伝達、備蓄機能を備えた拠点です。</Notes>
</shelter>
<shelter>
<Name>豊岡小学校</Name>
<Ward>鶴見区</Ward>
<Address>豊岡町27番地1</Address>
<Notes>被災した住民の避難生活の場所、情報受伝達、備蓄機能を備えた拠点です。</Notes>
</shelter>
.....
データの構造を確認すると <shelter> が1つのデータの塊だとわかります。
これを踏まえ解析します。
例)
from bs4 import BeautifulSoup
import urllib.request
url = 'http://www.city.yokohama.lg.jp/somu/org/kikikanri/data/shelter.xml'
data = urllib.request.urlopen(url).read()
xml = data.decode('utf-8')
soup = BeautifulSoup(xml, 'html.parser')
soup.prettify()
for l in soup.find_all('shelter'):
name = l.find('name').string
print(name)
print('おわり~')
【メモ】
ブラウザからXMLを表示した時には <Shelter> と表示されていたが、
BeautifulSoup で解析するとタグが <shelter> となっていたため少し嵌まりました。
頭の S を s にしないと解析に失敗します。。
ライブラリ openpyxl を利用します。 ※Anaconda3 があれば既に入っています 【注意】拡張子[xlsx/xlsm/xltx/xltm]のみ対応です 例) import openpyxl import os dir = os.path.dirname(__file__) file_path = os.path.join( dir, 'hoge.xlsx' ) #ファイルを読み込む #名前付き引数keep_vbaをTrueにするとxlsm形式が開けます(xlsxで指定しちゃ駄目) book = openpyxl.load_workbook(file_path, keep_vba=True) #最初のシートを設定 sheet = book.worksheets[0] #内部的に開いているシートを設定したい場合は以下を使う #sheet = book.active data = [] #sheet.rows でデータのある最終行までを対象とする for row in sheet.rows: #値を参照するには 配列.value で取得できる(空の値は None で判別) if row[0].value != None: data.append([row[0].value, row[1].value, row[2].value]) #先頭行が不要な場合は配列を削除 del data[0] print(data) #新たにセルに値を記入する場合は sheet オブジェクトに対しセル番地を指定するだけ sheet['A5'] = '群馬' #フォントのサイズや色も指定できる sheet['A5'].font = openpyxl.styles.Font(size=20, color='FF0000') sheet['B5'] = 1200 #フォーマットも指定できる(セルB4 と同じにしている) sheet['B5'].number_format = sheet['B4'].number_format sheet['C5'] = 4 #保存する場合 book.save(file_path) print('おわり~')
PHP にて動作している自サイト内検索用のテキストファイルを生成します。
要件)
サイト内の全ページのリンクを絶対パスにて取得し、
以下のキー文字列を付ける(外部リンク等一部対象外URLあり)
【^^^site_log_link^^^】絶対パス
上記リンク以下をそのページ内のコンテンツとし、タグを抜いたテキストを列挙する
1ページが終了したら繰り返し処理し、全ページを1ファイルで完成させる。
イメージ)
【^^^site_log_link^^^】URL1
ページ1のコンテンツ内容
ページ1のコンテンツ内容
ページ1のコンテンツ内容
【^^^site_log_link^^^】URL2
ページ2のコンテンツ内容
ページ2のコンテンツ内容
【^^^site_log_link^^^】URL3
ソース)
from bs4 import BeautifulSoup
import requests
import urllib.parse as urlps
import os, time, re
"""
解析の起点
第1:ベースとなるURL
第2:解析するURL
"""
def analize_start(base_url, target_url):
#グローバル変数なので SES にアクセスできる
res = SES.get(target_url)
soup = BeautifulSoup(res.text, 'html.parser')
soup.prettify()
body = soup.find('body')
if body == None:
return # 内容が取得できない
print('■ ' + target_url + ' を解析...')
# 取得したページの内容を取得
get_html(body, target_url)
# ページ内のリンクを更に解析
get_links(base_url, body)
"""
取得したページの内容を取得し、ファイルへ保存
第1:analize_startで解析されたbodyタグの内容
第2:保存対象のURL
"""
def get_html(body, url):
mem = '【^^^site_log_link^^^】' + url + '\n'
html = str(body.prettify())
# 不要なタグ内の文字列を削除
for ng_tag in NG_TAGS:
#re.DOTALL を引数に含める事で複数行に渡り正規表現の対象とする
p = re.compile(r'<' + ng_tag + '.*?/' + ng_tag + '>',re.DOTALL)
html = p.sub('', html)
# タグを削除
html = re.sub(r'<[^>]*?>','',html)
# ページを1行単位で評価して空と1文字の行は不要とする
for line in html.split('\n'):
if len(line.strip()) > 1:
mem += line + '\n'
with open(SAVE_FILE, mode='a', encoding='utf-8') as f:
f.write(mem)
time.sleep(1) # マナーとして1秒待機
"""
リンクを取得して再帰的に analize_start を実行する
第1:ベースとなるURL
第2:analize_startで解析されたbodyタグの内容
"""
def get_links(base_url, body):
for a in body.select('*'):
if a.name.lower() == 'a': # タグ名を小文字にしてaタグを見つける
link = urlps.urljoin(base_url, a.attrs['href'])
if link not in IS_LINKS:
IS_LINKS.add(link)
if is_analize(base_url, link) == True:
# 新たなURLを解析
analize_start(base_url, link)
"""
解析不要なURLを評価する
第1:ベースとなるURL
第2:解析するURL
"""
def is_analize(base_url, url):
url = url.lower()
# 外部リンクは解析しない
if url.find(base_url) == -1:
return False
for ng_list in NG_LINKS:
# NGリストにある文字列は解析しない
if url.find(ng_list) > -1:
return False
return True
if __name__ != '__main__':
exit()
# 解析結果保存情報
dir = os.path.dirname(__file__)
SAVE_FILE = os.path.join( dir, 'site_log.txt' )
if os.path.isfile(SAVE_FILE) == True:
#既にファイルが存在している場合は削除する
os.remove(SAVE_FILE)
#ベースとなる URL ※この直下全てをスクレイピングする
base_url = 'http://localhost/cake3x/'
#ログインページの URL
rogin_url = 'http://localhost/cake3x/users/login'
# ログイン情報
user = '******'
pwd = '******'
login_info = {
'username':user,
'password':pwd
}
# セッション開始
SES = requests.session()
# ログイン
res = SES.post(rogin_url, login_info)
res.raise_for_status()
# リンクフラグ
#処理済みのリンクを追加するための変数
IS_LINKS = {base_url}
# 無視リンク
#リンクの URL に文字列が含まれていたら処理しない
NG_LINKS = ['#', 'javascript', 'items/index/detail', 'items/index/item']
# 無視タグ
#タグの開始 ~ 終了に含まれる全ての文字列を削除する
NG_TAGS = ['script', 'footer', 'myheader']
# 解析開始
analize_start(base_url, base_url)
print('終わり~')