pythonでopenweathermapのAPIを叩こう

まず、jsonのapi request
http://api.openweathermap.org/data/2.5/weather?q=Tokyo,JP&APPID={apikey}

レスポンス

{"coord":{"lon":139.76,"lat":35.68},"weather":[{"id":701,"main":"Mist","description":"mist","icon":"50n"}],"base":"stations","main":{"temp":301.81,"pressure":1009,"humidity":66,"temp_min":301.15,"temp_max":302.15},"visibility":16093,"wind":{"speed":1.5,"deg":120},"clouds":{"all":1},"dt":1533250560,"sys":{"type":1,"id":7622,"message":0.0074,"country":"JP","sunrise":1533153003,"sunset":1533203030},"id":1850147,"name":"Tokyo","cod":200}

pythonで書きます。

import requests
import json

apikey = "hoge"

cities = ["Tokyo,JP", "London,UK", "New York, US"]
api = "http://api.openweathermap.org/data/2.5/weather?q={city}&APPID={key}"

k2c = lambda k: k - 273.15

for name in cities:
	url = api.format(city=name, key=apikey)
	r = requests.get(url)
	data = json.loads(r.text)
	print("+ 都市=", data["name"])
	print("| 天気=", data["weather"][0]["description"])
	print("| 最低気温=", k2c(data["main"]["temp_min"]))
	print("| 最高気温=", k2c(data["main"]["temp_max"]))
	print("| 湿度=", data["main"]["humidity"])
	print("| 気圧=", data["main"]["pressure"])
	print("| 風向き=", data["wind"]["deg"])
	print("| 風速度=", data["wind"]["speed"])
	print("")

コマンドライン
[vagrant@localhost python]$ python3 app.py
+ 都市= Tokyo
| 天気= mist
| 最低気温= 28.0
| 最高気温= 29.0
| 湿度= 66
| 気圧= 1009
| 風向き= 120
| 風速度= 1.5

+ 都市= London
| 天気= clear sky
| 最低気温= 20.0
| 最高気温= 24.0
| 湿度= 64
| 気圧= 1023
| 風向き= 230
| 風速度= 2.6

+ 都市= New York
| 天気= light rain
| 最低気温= 27.0
| 最高気温= 29.0
| 湿度= 69
| 気圧= 1019
Traceback (most recent call last):
File “app.py”, line 21, in
print(“| 風向き=”, data[“wind”][“deg”])
KeyError: ‘deg’

New Yorkの風向きがエラーになりますね。
new yorkのjsonを叩きます。
http://api.openweathermap.org/data/2.5/weather?q=New%20York,US&APPID={key}

{“coord”:{“lon”:-73.99,”lat”:40.73},”weather”:[{“id”:500,”main”:”Rain”,”description”:”light rain”,”icon”:”10d”}],”base”:”stations”,”main”:{“temp”:300.55,”pressure”:1019,”humidity”:69,”temp_min”:300.15,”temp_max”:302.15},”visibility”:16093,”wind”:{“speed”:2.6},”clouds”:{“all”:75},”dt”:1533250560,”sys”:{“type”:1,”id”:2121,”message”:0.0072,”country”:”US”,”sunrise”:1533203668,”sunset”:1533254951},”id”:5128581,”name”:”New York”,”cod”:200}

NewYorkのwindはspeedだけのようです。何故だ?wall streetは風向きなんて関係ないのかな。

execute_script()メソッド

from selenium import webdriver

browser = webdriver.PhantomJS()
browser.implicitly_wait(3)

browser.get("https://google.com")

r = browser.execute_script("return 100 + 50")
print(r)

[vagrant@localhost python]$ python3 app.py
/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/site-packages/selenium/webdriver/phantomjs/webdriver.py:49: UserWarning: Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead
warnings.warn(‘Selenium support for PhantomJS has been deprecated, please use headless ‘
150

r = browser.execute_script("var date = new Date(), console.log(date)")

windows10でIIS

コントロールパネル → プログラム → Windowsの機能の有効化または無効化 → インターネットインフォメーションサービス で有効にする。

seleniumをインストール

[vagrant@localhost python]$ pip3 install selenium
Collecting selenium
Downloading https://files.pythonhosted.org/packages/41/c6/78a9a0d0150dbf43095c6f422fdf6f948e18453c5ebbf92384175b372ca2/selenium-3.13.0-py2.py3-none-any.whl (946kB)
100% |████████████████████████████████| 952kB 552kB/s
Installing collected packages: selenium
Successfully installed selenium-3.13.0

R社にpythonからログイン

まさか、できるわけないよね。

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

USER = "hoge"
PASS = "hogehoge"

session = requests.session()

login_info = {
	"u" : USER,
	"p": PASS,
}
url_login = "https://grp01.id.rakuten.co.jp/rms/nid/vc?__event=login&service_id=top"
res = session.post(url_login, data=login_info)
res.raise_for_status()
print("success")

おい、ちょっと待て、ちょっと待て。マジこれ?これ、やばくないか。。。
[vagrant@localhost python]$ python3 app.py
success

bs4でログイン後の情報をスクレイピングする

まず、login.phpで、sessionのusernameとpasswordがokなら、メールトップ画面にリダイレクトする処理を書いています。

<?php elseif($status == "ok"): header('Location: mail.php?path=u0'); ?> 

こちらがログイン後の画面。outlookのUIを参考にしています。

「yumeさんのメールボックス」がh2です。

続いて、python。 sessionのusernameをyumeでpostして、beautifulsoupでh2をselect_oneします。

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

USER = "yume"
PASS = "hogehoge"

session = requests.session()

login_info = {
	"username" : USER,
	"password": PASS,
}
url_login = "http://open-box.co/login.php"
res = session.post(url_login, data=login_info)
res.raise_for_status()
print("success")

soup = BeautifulSoup(res.text, "html.parser")
a = soup.select_one("h2").string
if a is None:
	print("取得できませんでした")
	quit()
print(a)

[vagrant@localhost python]$ python3 app.py
success
yumeさんのメールボックス

おいおいおい。まてまてまて、頭が追い付かない。
すげー、python!

え、それなら、もしかして、twitterもいける?

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

USER = "hoge"
PASS = "hogehoge"

session = requests.session()

login_info = {
	"signin-email" : USER,
	"signin-password": PASS,
}
url_login = "https://twitter.com/login"
res = session.post(url_login, data=login_info)
res.raise_for_status()
print("success")

[vagrant@localhost python]$ python3 app.py
Traceback (most recent call last):
File “app.py”, line 16, in
res.raise_for_status()
File “/home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/site-packages/requests/models.py”, line 939, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://twitter.com/login

Forbiddenだ。さすがにあかんか。
twitterに限らず、requests.session()では、ログインできないようにできるらしいですね。
なるほどね。

pythonからusernameとpasswordのsessionをpostしてログインする

よく、不正ログインの防ぐために、画像認証などありますが、どうやってプログラムからログインしてるのか、やっとわかりました。
まず、以前自作したメールボックス
http://open-box.co/login.php

phpで、username, passwordをmysqlと照合してcheckしています。

public function login($username, $password){
		$stmt = $this->mysqli->prepare(
			"SELECT password FROM users WHERE username = ?");
		$stmt->bind_param('s', $_POST["username"]);
		$stmt->execute();

		$stmt->store_result();
		if($stmt->num_rows == 1){
			$stmt->bind_result($hash);
			while ($stmt->fetch()){
				if(password_verify($_POST['password'], $hash)){
					$_SESSION["username"] = $_POST["username"];
					return true;
				}
			}
		}
		return false;
	}

pythonでusernameとpasswordのsessionをpostします。

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

USER = "hoge"
PASS = "hogehoge"

session = requests.session()

login_info = {
	"username" : USER,
	"password": PASS,
}
url_login = "http://open-box.co/login.php"
res = session.post(url_login, data=login_info)
res.raise_for_status()
print("success")

エラーだと、res.raise_for_status()でエラーが表示される筈ですが、、、
来た!!!!!
[vagrant@localhost python]$ python3 app.py
success

bs4も使いたい。

SyntaxError: invalid syntax

しょっぱな躓いた。

from bs4 import BeautifulSoup
from urllib.request import
from urllib.parse import
from os import makedirs
import os.path, time, re

proc_files = {}

def enum_links(html, base):
	soup = BeautifulSoup(html, "html.parser")
	links = soup.select("link[rel='stylesheet']")
	links += soup.select("a[href]")
	result = []
	for a in links:
		href = a.attrs['href']
		url = urljoin(base, href)
		result.append(url)
	return result

def download_file(url):
	o = urlparse(url)
	savepath = "./" + o.netloc + o.path
	if re.search(r"/$", savepath):
		savepath += "index.html"
	savedir = os.path.dirname(savepath)
	if os.path.exists(savepath): return savepath
	if not os.path.exists(savedir):
		print("mkdir=", savedir)
		makedirs(savedir)
	try:
		print("download=", url)
		urlretrive(url, savepath)
		time.sleep(1)
		return savepath
	except:
		print("ダウンロード失敗:", url)
		return None

def analize_html(url, root_url):
	savepath = download_file(url)
	if savepath is None: return
	if savepath in proc_files: return
	proc_files[savepath] = True
	print("analize_html=", url)
	html = open(savepath, "r", encoding="utf-8").read()
	links = enum_links(html, url)
	for link_url in links:
		if link_url.find(root_url) != 0:
			if not re.search(r".css$", link_url): continue
		if re.search(r".(html|html)$", link_url):
			analize_html(link_url, root_url)
			continue
		download_file(link_url)

if __name__ == "__main__":
	url = "http://docs.python.jp/3.5/library"
	analize=html(url, url)

はい?
[vagrant@localhost python]$ python3 app.py
File “app.py”, line 2
from urllib.request import
^
SyntaxError: invalid syntax

≧urllib.request モジュールは基本的な認証、暗号化認証、リダイレクション、Cookie、その他の介在する複雑なアクセス環境において (大抵は HTTP で) URL を開くための関数とクラスを定義します。

from urllib.request importって書き方がおかしい気がするが。。

urllib.parse.urljoin()

base urlの相対パスをurljoinで結合していく。

from urllib.parse import urljoin

base = "http://hoge.com/html/crm.html"

print( urljoin(base, "erp.html"))
print( urljoin(base, "accounting/ifrs.html"))
print( urljoin(base, "../scm.html"))
print( urljoin(base, "../img/bi.png"))
print( urljoin(base, "../css/style.css"))

[vagrant@localhost python]$ python3 app.py
http://hoge.com/html/erp.html
http://hoge.com/html/accounting/ifrs.html
http://hoge.com/scm.html
http://hoge.com/img/bi.png
http://hoge.com/css/style.css

もし、urljoinがhttpの場合は、そのまま絶対パスを返す。

from urllib.parse import urljoin

base = "http://hoge.com/html/crm.html"

print( urljoin(base, "erp.html"))
print( urljoin(base, "http://www.nttdata.com/jp/ja/services/oss/index.html"))
print( urljoin(base, "../scm.html"))

[vagrant@localhost python]$ python3 app.py
http://hoge.com/html/erp.html
http://www.nttdata.com/jp/ja/services/oss/index.html
http://hoge.com/scm.html

なるほど。