Google Suggest APIでPythonの勉強をしてみました

Java
スポンサーリンク

こんにちは、DSKです。

ワタクシ、社会人になって以降ずっと主にJavaの開発を続けてきて、Java以外あまり勉強していない適当プログラマなのですが、最近Pythonに興味が出てきました。

Python自体は何年か前に社内イベントで触った事があって、その時は簡単ダナーと思ったのですが、時間と共に記憶から完全に抹消されてしまったので、ほぼ1からの勉強になります。

開発環境

Image by Boskampi from Pixabay

統合開発環境は使い慣れているのでEclipse Pleiadesにしました。

Pleiades All in One Eclipseから最新版EclipseのUltimateをダウンロードします。

言語のバージョン
  • Java:Java17
  • Python:Python 3.10.6

今のところ使うか分かりませんが、勢いで自宅のマシンにMySQLもインストール。MySQL 8.0.30。

DBに関してはPostgreSQLにしてみようかな?とも思ったのですが、MySQLの方が使い慣れているし、XServerはMySQLに対応しているというのもあって、結局MySQLに。

バージョン管理は仕事であまり使う機会が無かったGithubを勉強を兼ねて導入。

自分一人でのバージョン管理なので、改修→やっぱリバートくらいしか使う事無い様な気もしますけど、流行りには乗っておこうかなーと。

それ以外だとDocker、Ruby、Kotlinも興味はあるんですが、一度に色々手を出してもわけが分からなくなるので、まずはPythonでの開発が無難に出来る辺りを目指そうかなーと思っています。

練習にGoogle Suggest APIで遊んでみた

検索ワードの候補一覧出せたら便利そうじゃない?って思ったので、とりあえずGoogle Suggest APIを使って遊んでみることにしました。

無料で利用出来るサービスはいくらでもありますが、勉強だから自分で作る事に意味があるんじゃあないかなーと。

Google Suggest APIの仕様を理解する

Google Suggest APIの仕様書が見当たらなかったので、以下の記事を参考にしました。

以前はJavascript形式とJSON形式もサポートしていたみたいですが、現在はXMLのみのサポートになっているそうです。

とりあえずJavaで作ってみる

まずはJavaで、標準ライブラリにあるものだけで作ってみます。

ソースコード

package com.goingourway.test;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * Google Suggest APIで二階層までサジェストを取ってくるだけ
 * 
 * @author DSK
 *
 */
public class GoogleApiTest {
	/** 検索対象ワード */
	private static final String SEARCH_WORD = "ブログ";
	/** APIのURL */
	private static final String API_URL = "http://www.google.com/complete/search";
	/** 言語 */
	private static final String LANGUAGE = "ja"; // English = en
	/** 連続でリクエスト投げて怒られるのが嫌だからスリープ */
	private static final long THREAD_SLEEP = 100L;

	/**
	 * メイン関数
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		new GoogleApiTest().run();
	}

	/**
	 * 実行関数
	 */
	public void run() {
		Set<String> resultSet = new TreeSet<String>();
		parseXML(executeGet(SEARCH_WORD)).stream().forEach(
				s -> resultSet.addAll(parseXML(executeGet(s))));
		resultSet.stream().forEach(s -> System.out.println(s));
	}

	/**
	 * KeyWordを基にSuggestionをGETする
	 * 
	 * @param keyword キーワード
	 * @return 応答XML
	 */
	private String executeGet(String keyword) {
		String apiUrl = API_URL + "?hl=" + LANGUAGE + 
				"&output=toolbar&q=" + keyword.replace(" ", "%20");
		URL url;
		StringBuilder response = new StringBuilder();
		try {
			// 送信前にTHREAD_SLEEPミリ秒スリープする
			Thread.sleep(THREAD_SLEEP);
			url = new URL(apiUrl);
			HttpURLConnection con = (HttpURLConnection)url.openConnection();

			BufferedReader in = new BufferedReader(
					new InputStreamReader(con.getInputStream(), "Shift-JIS"));
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				response.append(inputLine);
			}
			in.close();
		} catch (Exception e) {
			System.out.println("An Exception occured. " + e.toString());
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		return response.toString();
	}

	/**
	 * XMLをParseしてサジェストのリストを作成する
	 * 
	 * @param xml XML
	 * @return サジェスト文言
	 */
	private List<String> parseXML(String xml) {
		System.out.println(xml);
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		Document doc;
		try {
			doc = factory.newDocumentBuilder().parse(
					new InputSource(new ByteArrayInputStream(xml.getBytes("UTF-8"))));
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}

		List<String> ret = new ArrayList<String>();
		Node root = doc.getFirstChild();
		NodeList nodeList = root.getChildNodes();
		int maxNodeLength = nodeList.getLength();
		for (int i = 0; i < maxNodeLength; i++) {
			Element element = getElement(nodeList.item(i));
			if (element == null) {
				continue;
			}
			NodeList childNodeList = element.getChildNodes();
			int maxChildNode = childNodeList.getLength();
			for (int j = 0; j < maxChildNode; j++) {
				Element childElement = getElement(childNodeList.item(j));
				if (childElement == null) {
					continue;
				}
				if (childElement.hasAttribute("data")) {
					ret.add(childElement.getAttribute("data"));
				}
			}
		}
		return ret;
	}
	
	/**
	 * NodeがElementだったらElementにキャストして返すだけの処理
	 * 
	 * @param node
	 * @return
	 */
	private Element getElement(Node node) {
		if (node.getNodeType() != Node.ELEMENT_NODE) {
			return null;
		}
		return (Element) node;
	}
}

実行結果

実行結果はこんな感じです。

もう1階層取りに行こうかなーとも思ったのですが、検索結果が増えすぎるのでこんなとこにしときます。

ブログ
ブログ 一般人
ブログ 一般人 おすすめ
ブログ 一般人 収入
ブログ 一般人 収益
ブログ 一般人 始め方
ブログ 一般人 日記
ブログ 一般人 検索
ブログ 一般人 無料
ブログ 収益化
ブログ 収益化 note
ブログ 収益化 おすすめ
ブログ 収益化 アメブロ
ブログ 収益化 ジャンル
ブログ 収益化 テーマ
ブログ 収益化 仕組み
ブログ 収益化 方法
ブログ 収益化 目安
ブログ 収益化 難しい
ブログおすすめ
ブログおすすめ 初心者
ブログおすすめ 収益化
ブログおすすめ 日記
ブログおすすめ 無料
ブログおすすめアプリ
ブログおすすめジャンル
ブログおすすめパソコン
ブログおすすめ本
ブログとは
ブログとは わかりやすく
ブログとは 初心者
ブログとは 意味
ブログとは 日記
ブログとは 知恵袋
ブログとはどういうことですか
ブログとはどういう意味ですか
ブログとは何でしょう
ブログとは何の略
ブログウォッチャー
ブログウォッチャー カンファレンス
ブログウォッチャー リクルート
ブログウォッチャー 人流データ
ブログウォッチャー 会社概要
ブログウォッチャー 位置情報
ブログウォッチャー 売上
ブログウォッチャー 広告
ブログウォッチャー 評判
ブログウォッチャー 酒田
ブログランキング
ブログランキング おすすめ
ブログランキング まとめ
ブログランキング ワードプレス
ブログランキング 意味ない
ブログランキング 政治
ブログランキングとは
ブログランキングライブドア
ブログランキング乙
ブログランキング競馬
ブログ一般人人気
ブログ収入
ブログ収入 アメブロ
ブログ収入 一般人
ブログ収入 公務員
ブログ収入 副業
ブログ収入 現実
ブログ収入 確定申告
ブログ収入 税金
ブログ収入 芸能人
ブログ収入ランキング 一般人
ブログ始め方
ブログ始め方 おすすめ
ブログ始め方 アメブロ
ブログ始め方 スマホ
ブログ始め方 ヒトデ
ブログ始め方 ワードプレス
ブログ始め方 本
ブログ始め方 無料 趣味
ブログ始め方 趣味
ブログ始め方無料
ブログ村
ブログ村 in out
ブログ村 マイレージ
ブログ村 ログイン
ブログ村 中学受験
ブログ村 収入
ブログ村 婚活
ブログ村 宝塚
ブログ村 登録
ブログ村とは

サジェストのキーワード見てるだけでも結構面白いものですね笑

Pythonに挑戦

続いて同じ事をPythonで書いてみます。

とはいえJava的な書き方をしても意味が無い上に、Pythonのいろはも分からない状態です。まずはネットでサンプルコードを拾って、コピペして動かしてみます。

以下の記事を参考にしました。

サンプルコードがめちゃくちゃシンプル。26行で作れちゃうのね…。

そのまま書いても勉強にならないので、main関数を用意してみたり、せめてforループを書いてみたりはしました。

import requests
import xml.etree.ElementTree as ET
import time

# UserAgentを偽装しないと正常なデータを取得できない
headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100"
}
url = "http://www.google.com/complete/search"
# キーワードは各自変更してください
query = "ブログ"

def main():
    suggestKeywords:list = execute(query)
    result:list = []
    for item in suggestKeywords:
        result.append(item)
        for item2 in execute(item):
            result.append(item2)
    result = sorted(set(result), key=result.sort(key=None, reverse=False))
    for item in result:
        print(item)
    return 0;

def execute(value) -> list:
    return parseXML(executeGet(value))
   
def executeGet(value) -> str:
    time.sleep(0.1)
    response = requests.get(
        url,
        headers=headers,
        params={"q": value, "ie": "utf_8", "oe": "utf_8", "output": "toolbar"},
    )
    return response.text;
    
# XMLデータをパースしてサジェストのキーワードリストを抽出する
def parseXML(text) -> list:
    print(text);
    keyword_nodes = ET.fromstring(text).findall("CompleteSuggestion/suggestion");
    return list(map(lambda x: x.attrib["data"], keyword_nodes))

main();

メソッドの書き方も分からなかったので、Javaのコードと同じ処理単位をメソッド化してみました。

元のコードが全く無駄のない物なので、僕が無駄な処理を追加している感が否めませんね…。

Pythonを触ってみて感じたこと

Pythonがシンプルだと言われるのが少し分かった気がしました。

使い易い標準ライブラリが豊富

今回HttpリクエストとXMLのパースを取り扱いましたがどちらも非常に扱いが簡単です。

JavaでHttpリクエストを書く際はGETを行う為にいくつものクラスを利用していますが、Pythonだとrequestsを使うだけ。例外発生時のハンドリングも書かなくても構わない。

これは楽っすわ…。

Javaでは「InputStreamからの、InputStreamReaderからの、BufferedReader!」みたいなことしなきゃいけないのに、それをしなくて良いのはとても嬉しいですし、開いたInputStreamをcloseして…とかもしなくて良いのも楽ですね。

XMLのパースもPythonでは標準で用意されているElementTreeを使えば簡単且つシンプルに書けますね。

可読性が高い

簡単なコードを触っただけなのでまだなんとも言えませんが、これだけシンプルに書けるのであれば可読性は非常に高いですね。

「誰が書いても似た様なコードになる」という事前知識は持っていましたが、その意味が理解出来ました。

書いていて違和感はある

C系言語しか使ってこなかったので、以下の書き方には違和感がありました。

  • 変数や関数で型を宣言しなくても良い
  • 行末に;(セミコロン)を書かない
  • 関数やクラス、forループなどで{}(中括弧)が出てこない

型宣言について調べてみたところ、Pythonでも変数・関数で型宣言が出来るけど、書かなくてもコンパイルエラーにはならないし、動作にも問題無いみたいです。

この辺はプロジェクトのコーディング規約によるかもしれませんね。

個人的には型宣言があった方が分かりやすいですし安心感があるのですが、Pythonに慣れていないからそう感じるのかな。

セミコロンや中括弧に関しては書き慣れれば気にならなくなるんじゃないかなぁ。

まあまだ勉強し始めたばかりですしね。

まとめ

いかがでしたでしょうか。

とりあえず今回は基本から始めてみました。

特に作りたいものがある訳でもないので、今後も気が向いた時に勉強の内容をまとめていきたいなと思います。

最後までご覧いただきありがとうございました。

ブログランキング参加しています!
励みになりますので面白かったらポチッとお願いします!
ブログランキング・にほんブログ村へにほんブログ村
人気ブログランキング
FC2ブログランキング

タイトルとURLをコピーしました