54
48

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ページ内のリンク切れ確認を行うテストコード

Last updated at Posted at 2015-12-20

リンク切れチェックを手で確認するのは精度が不安で時間掛かる。かといって外部ツールは実行が重いし開発環境で出来ない。ということで自作しました。相対リンクと絶対リンクに対応しています。

リンク切れチェックツールの動作フロー

  1. 指定されたURLをHTTP GETしてBeautiful Soup で解析。
  2. リンクを外部サイトリンク、相対リンク、絶対リンクに分類
  3. ページ内の同一ドメインのリンクからリンク先URLを抽出
  4. 重複を排除する
  5. 4で生成したリンク先URLに対してHTTPリクエスト投げてHTTPステータスが200であることを確認

Python3.5のasync/await版

ノンブロッキングなHTTPリクエストで後述のPython2版より60%高速に動作します。Python2版はページ下部。こちらのコードだと100リンクを1-3秒で確認完了します。

tests_url.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

import random
from urllib.parse import urlparse

from bs4 import BeautifulSoup
import requests
import asyncio
import aiohttp
from module.site.site import Site


def tests_urls():
    urls = [
        "http://www.disney.co.jp/home.html",
        "http://starwars.disney.co.jp/home.html"
    ]

    for test_url in urls:
        parse_and_request(test_url)


def parse_and_request(url):
    """
    urlをダウンロードして、bs4を解析して
    全リンクのステータスチェックする
    """
    # urlをパース
    o = urlparse(url)
    host = o.netloc

    # 指定されたURLをGETして解析
    response = requests.get(url, timeout=2)
    assert response.status_code == 200
    soup = BeautifulSoup(response.text, "lxml")
    test_urls = []
    for a in soup.find_all("a"):
        href = a.get("href")
        if href[0] == '#':
            pass
        elif href[0] == '/':
            # 相対リンク
            test_url = 'http://{}{}'.format(host, href)
            test_urls.append(test_url)
        elif host in href:
            # 絶対リンクかつ、同一ドメイン
            test_urls.append(href)
        else:
            # 外部サイトリンクはテストしない
            print('IGNORE:{}'.format(href))

    # 重複排除
    test_urls = list(set(test_urls))
    for test_url in test_urls:
        print(test_url)

    # リンクが生きているか非同期実行してチェック
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait([check_url(url) for url in test_urls]))


async def check_url(url):
    """
    非同期でURLをチェックして、HTTP STATUSが200を応答することをチェック
    :param url: str
    """
    response = await aiohttp.request('GET', url)
    status_code = response.status
    assert status_code == 200, '{}:{}'.format(str(status_code), url)
    response.close()

実行方法
>>>py.test tests_url.py

Python2系版

tests_url.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

from urllib.parse import urlparse

from bs4 import BeautifulSoup
import requests


def tests_urls():
    urls = [
        "http://www.disney.co.jp/home.html",
        "http://starwars.disney.co.jp/home.html"
    ]

    for test_url in urls:
        parse_and_request(test_url)


def parse_and_request(url):
    """
    urlをダウンロードして、bs4を解析して
    全リンクのステータスチェックする
    """
    # urlをパース
    o = urlparse(url)
    host = o.netloc

    # 指定されたURLをGETして解析
    response = requests.get(url, timeout=2)
    assert response.status_code == 200
    soup = BeautifulSoup(response.text, "lxml")
    test_urls = []
    for a in soup.find_all("a"):
        href = a.get("href")
        if href[0] == '#':
            pass
        elif href[0] == '/':
            # 相対リンク
            test_url = 'http://{}{}'.format(host, href)
            test_urls.append(test_url)
        elif host in href:
            # 絶対リンクかつ、同一ドメイン
            test_urls.append(href)
        else:
            # 外部サイトリンクはテストしない
            print('IGNORE:{}'.format(href))

    # 重複排除
    test_urls = list(set(test_urls))
    for test_url in test_urls:
        print(test_url)

        # リンクが生きているかチェック
        response = requests.get(test_url, timeout=2)
        assert response.status_code == 200

54
48
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
48

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?