はじめに

プログラマが知るべき97のこと」というエッセイ集がWeb上で公開されていたのでクローリングしてPDF化してみました。

プロジェクトの作成

今回はクローリングスクリプトではScrapyを使用し、PDF化スクリプトではreportlabを使用します。

$ mkdir ninety_seven_things
$ cd ninety_seven_things
$ pipenv install scrapy reportlab

クローリングスクリプト

# crawler.py
from scrapy import Spider, Request
from functools import reduce
from operator import add

class NinetySevenThingsSpider(Spider):
    name = 'ninety_seven_things_spider'

    # クローリングを開始するURL
    start_urls = ['https://xn--97-273ae6a4irb6e2hsoiozc2g4b8082p.com/']

    custom_settings = {
        "DOWNLOAD_DELAY": 3, # ページ遷移間隔(秒)
        "FEED_EXPORT_ENCODING": 'utf-8', # エンコード指定
    }

    def parse(self, response):
        url_obj = response.xpath('/html/body/div/div/div/div/ol//li//a/@href')
        full_urls = map(lambda href: response.urljoin(href.extract()), url_obj)
        for full_url in full_urls:
            yield Request(full_url, callback=self.output)
            
    def output(self, response):
        title = response.xpath('/html/body/div/div/article/h1/text()').extract_first()
        author = response.xpath('/html/body/div/div/article/span/a/text()').extract_first()
        body = reduce(add, map(lambda x: x.extract() + '\n', response.css('p::text')[:-3]))
        yield {
            'title': title,
            'author': author,
            'body': body.replace('。', '。\n')
        }

クローリング

$ pipenv run scrapy runspider crawler.py -o output.json

PDF作成スクリプト

# pdf.py
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.lib.styles import getSampleStyleSheet
import json
from functools import reduce

FILENAME = 'プログラマが知るべき97のこと.pdf'
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiKakuGo-W5'))
styles=getSampleStyleSheet()
styles["Normal"].fontName = 'HeiseiKakuGo-W5'

doc = SimpleDocTemplate(FILENAME,pagisize=A4)

with open('output.json', 'r') as f:
    texts = json.loads(f.read())
    story = reduce(lambda x, y: x + y, map(lambda text: [
        Paragraph('<font size=18>' + text['title'] + '</font>', styles["Normal"]),
        Spacer(1, 18),
        Paragraph('<font size=12> 著者: ' + text['author'] + '</font>', styles["Normal"]),
        Spacer(1, 12),
        Paragraph('<font size=10>' + text['body'] + '</font>', styles["Normal"]),
        PageBreak()
    ], texts))

    doc.build(story)

PDF作成

$ pipenv run python pdf.py

おわり

出典

プログラマが知るべき97のこと CC-by-3.0-USでライセンスされているようです。