Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
2
Help us understand the problem. What is going on with this article?
@hmarf

技術書典の本を検索できるアプリを作ってみた~サーバーサイド 編(Python, Go)~

More than 1 year has passed since last update.

はじめに

この記事は、時間ができた学生が暇つぶしに「技術書典の本を検索できるアプリを作ってみた」というものです。
技術書典に参加する前にある程度購入する本を決めると思うのですが、その際に欲しい技術について書いてある本を探したいと思うはずです。サークル一覧から本を探すのはとても大変で時間が足りません。そこでスクレーピングしてデータ収集し、検索できるアプリを開発してみました(自己満)。
この記事ではサーバーサイドについて書きます。次の記事で「Androidアプリを作ってみた」を書きたいと思います。

サーバーサイドの流れ

  1. Pythonでデータを収集する
    I.技術書典のサークルリスト からURL情報を収集する
    II. 集めたURLにアクセスしてそれぞれのサークルが出している本の情報を収集する
  2. 収集したデータをDBに保存する
  3. Goでサーバーを立てる

1. Pythonでデータ収集

I. 技術書典のサークルリスト からURL情報を収集する

技術書典のサークルリストの<a>タグをひたすら保存するコード

collect_url.py
# coding: UTF-8
from bs4 import BeautifulSoup
from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.chrome.options import Options

# ブラウザのオプションを格納する変数
options = Options()

# Headlessモードを有効にする
options.set_headless(True)

# ブラウザを起動する
driver = webdriver.Chrome(chrome_options=options)

# ブラウザでアクセスする
driver.get("https://techbookfest.org/event/tbf07/circle")

# HTMLを文字コードをUTF-8に変換してから取得
html = driver.page_source.encode('utf-8')

# BeautifulSoupで扱えるようにパース
soup = BeautifulSoup(html, "html.parser")

# ファイル出力
file_text = open("url_data.txt", "w")
elems = soup.find_all("a")
for elem in elems:
    print(elem.get('href'),file=file_text)
file_text.close()

II. 集めたURLにアクセスしてそれぞれのサークルが出している本の情報を収集する

1.で集めたURLにアクセスし、
出店しているサークル名、配置場所、ジャンル、ジャンル詳細、サークル画像、出品している本の名前、内容
のデータを収集する。(残念ながらペンネームは集められなかった。html にclass or idを貼って無いから面倒

collect_data.py
# coding: UTF-8
from bs4 import BeautifulSoup
from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.chrome.options import Options
import pickle
import sys

# データを保存する
i = 0
sys.setrecursionlimit(10000)
with open('../data/getData.txt', 'wb') as ff:

    # 保存するデータ
    save_datas = []

    # ブラウザのオプションを格納する変数
    options = Options()

    # Headlessモードを有効にする
    options.set_headless(True)

    # ブラウザを起動する
    driver = webdriver.Chrome(chrome_options=options)

    urlHeader = "https://techbookfest.org"
    pathfile = "../data/url_data.txt"

    with open(pathfile) as f:
        for _path in f:
            i += 1
            url = urlHeader + _path
            print(i,url)
            # ブラウザでアクセスする
            driver.get(url)

            # HTMLを文字コードをUTF-8に変換してから取得
            html = driver.page_source.encode('utf-8')

            # BeautifulSoupで扱えるようにパース
            soup = BeautifulSoup(html, "html.parser")

            circle = soup.find(class_="circle-name").string
            arrange = soup.find(class_="circle-space-label").string
            genre = soup.find(class_="circle-genre-label").string
            keyword = soup.find(class_="circle-genre-free-format").string

            circle_image = soup.find(class_="circle-detail-image").find(class_="ng-star-inserted")

            book_title = []
            for a in soup.find_all(class_="mat-card-title"):
                book_title.append(a.string)

            book_content = []
            for a in soup.find_all(class_="products-description"):
                book_content.append(a.string)

            for title, content in zip(book_title, book_content):
                data = [circle,circle_image['src'],arrange,genre,keyword,title,content,url]
                save_datas.append(data)

    pickle.dump(save_datas,ff)

2. 収集したデータをDBに保存する

ファイルに保存したデータを取得し、MySQLにInsertするだけのプログラムです。Insertするだけなので適当なプログラムになってしまった。

insertDB.py
# coding: UTF-8
import MySQLdb
import pickle

# データベースへの接続とカーソルの生成
connection = MySQLdb.connect(
    host='0.0.0.0',
    user='user',
    passwd='password',
    db='techBook')
cursor = connection.cursor()

# id, circle, circle_image, arr, genere, keyword, title, content
with open('../data/getData.txt','rb') as f:
    load_datas = pickle.load(f)
    for load_data in load_datas:
        data = []
        if load_data[6] == None:
            for dd in load_data:
                if dd == None:
                    continue
                dd = dd.replace('\'','')
                data.append(dd)
            sql = "INSERT INTO circle (circle, circle_image, arr, genere, keyword, title, circle_url) values ('" + data[0] + "','" + data[1] + "','" + data[2]+"','" + data[3]+"','" + data[4]+"','" + data[5]+"','" + data[6]+"')"
        else:
            for dd in load_data:
                dd = dd.replace('\'','')
                data.append(dd)
            sql = "INSERT INTO circle (circle, circle_image, arr, genere, keyword, title, content, circle_url) values ('" + data[0] + "','" + data[1] + "','" + data[2]+"','" + data[3]+"','" + data[4]+"','" + data[5]+"','" + data[6]+"','" + data[7]+"')"
            print(sql)
        cursor.execute(sql)

# 保存を実行
connection.commit()

# 接続を閉じる
connection.close()

3. Goでサーバーを立てる

Pythonで書いてもよかったのですが、気分的にGoで書きました。
レイアードアーキテクチャで書いています。(宣伝)
ファイル数が多いので全部載せることはできませんでした。Githubを参照ください。
検索はSQLの部分一致検索で行います。

SELECT * FROM circle where content like '%swift%';

取得したデータをjson形式で返して終わりです!!

[request] 
{
"keyword":"..."
}

[response]
{
    "result": [
        {
            "CircleURL": "...",
            "Circle": "...",
            "CircleImage": "...",
            "arr": "...",
            "Genere": "...",
            "Keyword": "...",
            "Title": "...",
            "Content": "..."
        },
    ]
}

終わりに

久々にPythonを書いた気がします。全体的に書いてて楽しかったです。今回書いたコードはこちら
読んでいただきありがとうございました。次回のAndroid編をお楽しみに!!

2
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
hmarf
機械学習を勉強しています。R-CNN推しです。 趣味でGolang、Kotlin書いてます。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
2
Help us understand the problem. What is going on with this article?