Интро


Цель — создать велосипед скрипт, который пробежится по сайту и проверит каждую страницу сайта на валидность html.
Я слышал, что если нападает перфекционизм, то надо полежать, отдохнуть и это пройдет.
Подумаешь, в валидаторе ошибка…
Но если все же не проходит, то

Ставим докер и контейнер с валидатором


Раньше поставить локальный валидатор было делом муторным.
Не то, что бы сложным, а требующим времени.
С приходом докера он устанавливается за секунды.
Ставим докер:
yum install docker

После установки докера берем себе готовый образ с собранным валидатором:
docker pull magnetikonline/html5validator

Когда образ закачается, то запускаем его:
docker run -p 8080:80 -p 8888:8888 --name validator --restart=always  -d magnetikonline/html5validator

И стартуем:
docker start validator


После запуска можно зайти на http://localhost:8888 и увидеть:



Локальный валидатор работает! Его можно натравить на какой-нибудь сайт.
И проверяем с командной строки:

curl 'localhost:8888?doc=http://www.w3schools.com'

И вот такой результат:




Как видно, валидатор нашел 3 ошибки.

Паучок



Теперь надо написать скрипт, который бы обошел все страницы сайта.
За основу я взял вот такой web crawler/scraper на основе Mojo.
И немного изменил:

#!/usr/bin/env perl
use 5.010;
use open qw(:locale);
use strict;
use utf8;
use warnings qw(all);
use Mojo::UserAgent;
use List::MoreUtils 'true';
use Term::ANSIColor;

# Адрес сайта для проверки
my $site_to_check = 'http://habrahabr.ru';

# Адрес локального валидатора
my $local_validator = 'http://192.168.1.217:8888';

# FIFO queue
my @urls = ( Mojo::URL->new($site_to_check) );

# User agent following up to 5 redirects
my $ua = Mojo::UserAgent->new( max_redirects => 5 );

# Track accessed URLs
my $active = 0;
my %uniq;

sub parse {
    my ($tx) = @_;

    # Request URL
    my $url = $tx->req->url;

    # Extract and enqueue URLs
    for my $e ( $tx->res->dom('a[href]')->each ) {

        # Validate href attribute
        my $link = Mojo::URL->new( $e->{href} );
        next if 'Mojo::URL' ne ref $link;

        # "normalize" link
        $link = $link->to_abs( $tx->req->url )->fragment(undef);
        next unless $link->protocol =~ /^https?$/x;

        # Don't go deeper than /a/b/c
        next if @{ $link->path->parts } > 3;

        # Access every link only once
        next if ++$uniq{ $link->to_string } > 1;

        # Don't visit other hosts
        next if $link->host ne $url->host;

        push @urls, $link;
        my $get   = $ua->get( $local_validator . "?doc=$link" )->res->body;
        my @answ  = split / /, $get;
        my $count = true { /class="error"/ } @answ;
        print color("green"), $link, color("reset");
        print "  Кол-во ошибок в валидаторе: ",
          color("red"), "$count \n", color("reset");

    }

    return;
}

sub get_callback {
    my ( undef, $tx ) = @_;

    # Parse only OK HTML responses
    $tx->res->code == 200
      and $tx->res->headers->content_type =~ m{^text/html\b}ix
      and parse($tx);

    # Deactivate
    --$active;

    return;
}

Mojo::IOLoop->recurring(
    0 => sub {

        # Keep up to 4 parallel crawlers sharing the same user agent
        for ( $active .. 4 - 1 ) {

            # Dequeue or halt if there are no active crawlers anymore
            return ( $active or Mojo::IOLoop->stop )
              unless my $url = shift @urls;

            # Fetch non-blocking just by adding
            # a callback and marking as active
            ++$active;
            $ua->get( $url => \&get_callback );
        }
    }
);

# Start event loop if necessary
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

Ссылка на гитхаб

Результат работы:


Комментарии (7)


  1. kloppspb
    31.08.2015 11:13

    >Раньше поставить локальный валидатор было делом муторным.
    >Не то, что бы сложным, а требующим времени.

    Так эти ужасы по ссылке скрыты внутри одного HTML::Tidy, разве нет?


    1. pcdesign
      31.08.2015 11:34

      Немного разные оперы.


      1. kloppspb
        31.08.2015 12:24

        И то, и то ведь — надстройка над tidyp. Но первом случае оно ещё и как веб-сервис работает, а в случае HTML::Tidy только автономно (что далеко не минус), зато ставится намного проще. В чём принципиальная разница?

        P.S. А вообще, для нелокальных сайтов/страниц можно и напрямую обращаться к validator.w3.org/check?uri=… :)


        1. pcdesign
          31.08.2015 12:30
          +1

          — Это на счет HTML::Tidy

          HTML Tidy is a great utility for pretty-printing your HTML, and it may
          clean it up a bit, though I wouldn't trust it to do too much of that.
          It's not a validator, though. It's a linter. There is, orbiting
          somewhere inside the W3C solar system, some validator software written
          by one of the W3C guys.

          — А на счет обращений к validator.w3, если надо 100500 страниц проверить, то лучше к локальному валидатору обращаться.
          И быстрее и не забанят.


          1. kloppspb
            31.08.2015 13:54

            Я про валидатор по первой ссылке, который «Building the W3C Markup Validation Service from source». Чем он отличается от HTML::Tidy, если внутри себя использует ту же tidyp?

            А вообще, можно просто сравнить результаты во всех трёх случаях :)


  1. kabal375
    01.09.2015 11:05

    Это наверное полезная статья, так что минусовать рука не поднимается, но что она делает в хабе «виртуализация»? Просто из-за наличия докера в процессе подготовки? Эдак можно добавлять сюда что угодно, просто по факту того, что что угодно можно запускать в виртуальной среде.

    Я просто интересуюсь технологиями виртуализации, но не интересуюсь веб-программировании и уже не знаю как мне отфильтровать ленту, чтобы не видеть статьи о веб-разработке от людей, открывших для себя VMware Workstation.


    1. pcdesign
      01.09.2015 12:12

      Хаб «виртуализация» убрал.

      Я просто в восторге от докера, наверное поэтому воткнул в «Виртуализацию» :)