スクレイピング

Webスクレイピングの基本は urllib.request またはより新しい Requests と,正規表現 re とである。より高レベルのライブラリとして Beautiful Soup がある(pip install beautifulsoup4from bs4 import BeautifulSoup)。

例えば,このサイトにどれだけリンクがあるかを調べてみよう。まず urllib.request による方法:

import urllib.request
import re

with urllib.request.urlopen('http://example.jp/') as f:
    s = f.read().decode('utf-8')
    a = re.findall('<a href="(.*?)"', s)

requests による方法:

import requests
import re

r = requests.get('http://example.jp/')
if r.status_code != 200:  # エラーチェックが必要なら
    raise ConnectionError()
r.encoding = 'utf-8'  # 自動判断では 'ISO-8859-1' に間違えられることがある
a = re.findall('<a href="(.*?)"', r.text)

これで a には ['../', '../stat/', 'install.html', ...] という配列が入る。

ファイルのダウンロードは例えば次のようにする:

import requests
import pathlib

r = requests.get('http://example.jp/hoge.png')
pathlib.Path("hoge.png").write_bytes(r.content)

ほか,例えば r.headers['Last-Modified'] で更新日時がわかるので,os.utime() でファイルのタイムスタンプを変えるといったことも可能であろう。

User Agent(ブラウザの種類)は "python-requests/2.22.0" のような感じで先方のログに残る。これが嫌なら適当に変えられる:

r = requests.get('http://example.jp/', headers={'User-Agent': 'hogehoge/001'})

最後に,いらすとやさんから「科学」ジャンルの画像のサムネール(256x256)をスクレイプする簡単なコード例を挙げておく。カレントディレクトリ下に irasutoya というサブディレクトリがあるとする。

import requests
import re
import pathlib
import time

path = pathlib.Path("irasutoya")
names = [re.sub("^.*/", "", str(n)) for n in path.iterdir()]

url = 'https://www.irasutoya.com/search/label/科学'
while True:
    print("Requesting", url)
    r = requests.get(url)
    if r.status_code != 200:
        print("Status:", r.status_code)
        break
    r.encoding = 'utf-8'
    a = re.findall('document.write\(bp_thumbnail_resize\("(.*?)"', r.text)
    for i in a:
        name = re.sub("^.*/", "", i)
        if name in names:
            continue
        names.append(name)
        png256 = re.sub('/s72-c/', '/s256-c/', i)
        print("Downloading", png256)
        time.sleep(1)  # 迷惑をかけないように必ず数秒待つ
        r1 = requests.get(png256)
        if r1.status_code != 200:
            print("Status:", r1.status_code)
            continue
        pathlib.Path("irasutoya/" + name).write_bytes(r1.content)
    m = re.search("<a class='blog-pager-older-link' href='(.*?)'", r.text)
    if not m:
        break
    url = m.group(1)

さらについでに,いらすとやさんのいろいろな顔アイコン(動物・モンスターを除く人間だけ)以下の256×256サムネールを全部取得してカレントディレクトリの irasutoya2 サブディレクトリに入れる:

path = pathlib.Path("irasutoya2")
names = [re.sub("^.*/", "", str(n)) for n in path.iterdir()]

urls = [
    'http://www.irasutoya.com/2013/10/blog-post_5077.html',
    'http://www.irasutoya.com/2013/10/blog-post_3974.html',
    'http://www.irasutoya.com/2013/10/blog-post_9098.html',
    'http://www.irasutoya.com/2013/10/blog-post_6907.html',
    'http://www.irasutoya.com/2013/10/blog-post_872.html',
    'http://www.irasutoya.com/2013/10/blog-post_8683.html',
    'http://www.irasutoya.com/2013/10/blog-post_2022.html',
    'http://www.irasutoya.com/2013/10/blog-post_1473.html',
    'http://www.irasutoya.com/2015/10/blog-post_59.html',
    'http://www.irasutoya.com/2015/10/blog-post_29.html',
    'http://www.irasutoya.com/2015/10/blog-post_405.html',
    'http://www.irasutoya.com/2015/10/blog-post_135.html' ]

for u in urls:
    print("Requesting", u)
    r = requests.get(u)
    if r.status_code != 200:
        print("Status:", r.status_code)
        break
    r.encoding = 'utf-8'
    a = re.findall('"([^"]*/s800/[^"]*)"', r.text)
    for i in a:
        name = re.sub("^.*/", "", i)
        if name in names:
            continue
        names.append(name)
        png256 = re.sub('/s800/', '/s256-c/', i)
        m = re.search('^//', png256)
        if m:
            png256 = 'http:' + png256
        print("Downloading", png256)
        time.sleep(1)
        r1 = requests.get(png256)
        if r1.status_code != 200:
            print("Status:", r1.status_code)
            continue
        pathlib.Path("irasutoya2/" + name).write_bytes(r1.content)

Last modified: