セキュリティこねこね

セキュリティ関連技術やCTFなどのアウトプット

技術書典 応援祭の無料本一覧を取得する

2月29日~3月1日に開催されるはずだった技術書典8新型コロナウイルス感染症の影響により中止となり、代わりに3月7日~4月5日までの約1ヶ月間オンラインで技術書典 応援祭が開催されています。

記事投稿点で700種類以上の技術書が掲載されており、多くは電子版と製本版が有料で販売されているのですが、中には電子版が無料で提供されているものがあります。それらはログインすれば.pdfや.epubの形式でダウンロードが可能です。

しかし残念ながらサイトには価格順でソート表示する機能が見当たりません。なんとかすべての無料本を入手したいと思い、スクレイピングで一覧を作成しましたので参考までに掲載させていただきます。※もし不都合あればコメント欄などから連絡いただければ取り下げます

技術書典 応援祭の無料本一覧

以下の51冊が見つかりました。ありがたく読ませていただきます。末尾から順に取得したので、サイトの表示とは順番が逆になっています。漏れがあったらすみません。なお以下は記事投稿時点のものであり、期間中に増える可能性があります。

Title Author
虎の穴ラボの薄い本。vol.4 虎の穴ラボ
イケてないアプリケーションで試すセキュリティホール Ruby on Rails版 Ginza.rb
ニャーQL勉強会のつくりかた Ginza.rb
お絵描きソフトをつくる本 おえかきや
DELiGHTWORKS TECHBOOK DELiGHTWORKS TECH
BRBT Magazine #1 株式会社エス・エム・エス A&I推進部有志
BRBT Magazine #prep. 株式会社エス・エム・エス A&I推進部有志
私がみんなに低レイヤーを学んでほしい理由 カレーうり坊
クマのたっきゅうびん きんとーん・らぼ
俺の自由研究 - 3日でできる obniz 連携 きんとーん・らぼ
mixi tech note #03 株式会社ミクシィ
mixi tech note #02 株式会社ミクシィ
mixi tech note #01 株式会社ミクシィ
XFLAG Tech Note vol.02 株式会社ミクシィ
XFLAG Tech Note 株式会社ミクシィ
Born 図式とその一般化 Pate Cube (冊子のみ) Math Relish
バイナリ生物学入門 へにゃぺんて
バイナリ生成環境「daisy-tools」実験報告 へにゃぺんて
フルスクラッチで作る!UEFIベアメタルプログラミング へにゃぺんて
フルスクラッチで作る!x86_64自作OS へにゃぺんて
自作OS自動化のPoCとしての遺伝的MBR へにゃぺんて
フルスクラッチで作る!x86_64自作OS パート4 ぼくらのイーサネットフレーム! へにゃぺんて
作って分かる!x86_64機械語入門 へにゃぺんて
フルスクラッチで作る!UEFIベアメタルプログラミング パート2 へにゃぺんて
フルスクラッチで作る!x86_64自作OS パート3 システムコールの薄い本 へにゃぺんて
Ohgami's Commentary on OS5 へにゃぺんて
フルスクラッチで作る!x86_64自作OS パート2 ACPIでHPET取得してスケジューラを作る本 へにゃぺんて
フルスクラッチで作る!x86_64自作OS パート5 てっとりばやくマルチコア へにゃぺんて
JavaScript で位置情報取得 Ginza.rb
モダナイズ モバシフ Ginza.rb
PokeAI #1:初代1vs1編 ヤマブキ計算所
WANTEDLY TECH BOOK 7 Wantedly執筆部
#UiPath activity Journey Vol.2 聖地で出会いし者たち
#UiPath activity Journey 厳選30アクティビティ 聖地で出会いし者たち
トークンエンジニア入門 なんでもトーク
[既刊:無償配布中]micro:bitではじめる電子工作 コピペテック
[既刊:無償配布中]Oculus Goで動くVRアプリをUnityで作ろう コピペテック
[既刊:無償配布中]Three.jsとTensorFlow.jsで作るモーションキャプチャ3Dゲーム コピペテック
kintone 魔改造入門 ポータル編 PART1 きんとーん・らぼ
エンジニアのためのダイエット術 きんとーん・らぼ
kintoneでおうちHack きんとーん・らぼ
Google Home プチハック! きんとーん・らぼ
kintoneで始めるIoT きんとーん・らぼ
kintone × Google翻訳 きんとーん・らぼ
kintone まかない飯 きんとーん・らぼ
WANTEDLY TECH BOOK 6 Wantedly執筆部
WANTEDLY TECH BOOK 5 Wantedly執筆部
WANTEDLY TECH BOOK 4 Wantedly執筆部
WANTEDLY TECH BOOK 3 Wantedly執筆部
WANTEDLY TECH BOOK 2(電子版のみ) Wantedly執筆部
WANTEDLY TECH BOOK (電子版のみ) Wantedly執筆部
※以下2020/03/31に追記。 -
OPTiM TECH BOOK #1 OPTiM
コードレビュー×信号処理の薄い本 お引越し
[無料] ねこはうす通信創刊準備号 ねこはうす
Go Starter Book Women Who Go Tokyo
【無料頒布中】最短路問題のおはなし ちんちらんど
オレオレ証明書との HTTPS 通信設定(Android・ iOS) からてか

取得に使ったスクリプト

当初GoogleスプレッドシートのIMPORTXMLでやろうとしたところ、どうもJavaScriptで動的にページ内容が作られているようでうまくいきませんでした。サイトのソースを見てもパッと見でよくわからなかったので、PythonからHeadless Chromeを使う形で実現しました。

作成したスクリプトは全書籍の電子版価格一覧を出力するものです。約5秒の間隔を開けてアクセスするようにしているので、完了まで1時間くらいかかります。Ctrl+Cで中断した場合や、新たに書籍が追された場合は、既存取得部分をスキップして必要な分だけアクセスするようになっています。

import time
import re
import traceback
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import chromedriver_binary
from bs4 import BeautifulSoup

options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--log-level=3")
driver = webdriver.Chrome(options=options)

try:
    exist_id_list = []
    try:
        with open('./output.txt', mode='r', encoding='utf-8') as f:
            exist_id_list = list(map(lambda x: re.sub(r'^(\d+).*', r'\1', x), f.read().splitlines()[1:]))
            print("既存のoutput.txtを読み込みました。")
    except Exception as e:
        with open("./output.txt", mode='w', encoding='utf-8') as f:
            f.write("id, title, author, price\n")
        print("output.txtを新規作成しました。")

    print("全productのリンクを取得中・・・")
    products = []
    driver.get("https://techbookfest.org/market/all")
    WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located)
    seconds = 0
    prev_product_count = 0
    wait_flag = True
    while wait_flag and seconds < 180:
        time.sleep(5)
        products = BeautifulSoup(driver.page_source,"lxml").find_all(class_="css-1dbjc4n r-18u37iz r-1w6e6rj r-1qhn6m8")[0].find_all("a")
        if prev_product_count < len(products):
            prev_product_count = len(products)
            seconds += 5
            print(str(seconds) + "秒 : " + str(len(products)) + "件")
            wait_flag = True
        else:
            wait_flag = False

    print("各productの電子版価格を取得中・・・")
    for product in reversed(products):
        product_id = re.sub(r'/product/(\d+)', r'\1', product['href'])
        if product_id in exist_id_list:
            print(product_id + "をスキップ")
        else:
            driver.get("https://techbookfest.org/product/" + product_id)
            WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located)
            time.sleep(5)
            title = BeautifulSoup(driver.page_source,"lxml").find("h2").text
            author = BeautifulSoup(driver.page_source,"lxml").find("h3").text
            price = re.sub(r'.*電子版¥(\d+).*', r'\1', BeautifulSoup(driver.page_source,"lxml").find(class_="css-1dbjc4n r-18u37iz r-1w6e6rj r-1v1z2uz").text)
            result_line = ", ".join([product_id, title, author, price])
            print(result_line)
            with open("./output.txt", mode='a', encoding='utf-8') as f:
                f.write(result_line + "\n")

except:
    traceback.print_exc()
finally:
    driver.quit()

動かすためには以下の準備が必要です。「<Chromeに近いバージョン数>」はChromeのヘルプを確認してからchromedriver-binary · PyPIの一番近いものを使います。

>pip install selenium
>pip install chromedriver-binary==<Chromeに近いバージョン数>
>pip install beautifulsoup4
>pip install lxml

出力結果から0円のものに絞り込んだり、ログインして購入→ダウンロードする部分はいまのところ手動でやっています。前述のスクリプトは改変含め自由に使っていただいて構いませんが、動作など一切保証しません。使い方についての問い合わせなどもご遠慮ください。技術書典のサーバに負荷を掛けないようにくれぐれもご注意ください。

更新履歴

2020/03/31

  • 追加されていた6冊を追記しました。
  • ページ構成が変わっていたのでスクリプトを修正しました。