Особенности скачивания web-страниц

Немного рекламы

Идея для этой статьи родилась во время создания плагина WebPage для OutWiker. Этот плагин предназначен для скачивания страниц из Интернета и добавления ее в дерево заметок. Во время его создания ставились две цели. Во-первых, скачанная страница должна выглядеть так же, как она отображается в браузерах, и, во-вторых, страница должна нормально отображаться без подключения к Интернету, то есть по возможности все картинки, файлы CSS и скрипты должны быть скачаны и сохранены в специально отведенную для них папку (__download), а на самой HTML-странице все ссылки на внешние файлы должны быть исправлены таким образом, чтобы они загружались из этой папки.

Язык HTML является достаточно гибким (в данном случае даже слишком), и для корректного отображения страниц пришлось учитывать множество особенностей, связанных с тем, как могут загружаться картинки и CSS файлы на HTML-странице. Вот об этих особенностях и пойдет речь. Если кто-то захочет реализовать подобное скачивание страниц в своей программе, эта статья может служить что-то вроде чеклистом со списком того, что нужно учесть. Все эти пункты разделены на разделы.


1. Изображения

1.1 Изображения могут быть вставлены не только с помощью тега <img>, но и с помощью CSS с использованием свойства background-image.

<img src="image_01.png"/>
.image {
        background-image: url(back_img_02.png);
}

Таким образом, при скачивании страниц может понадобиться менять ссылки на изображения не только в HTML-документе, но и в файлах CSS.

1.2. Изображения могут быть вставлены с помощью CSS, описанных в теге атрибутах style.

Например:

<ul style="list-style: url(image.png)">

1.3. Ссылки на изображения могут быть не только в теге <img>, но и в других тегах

Например, в атрибуте background некоторых тегов, таких как body, table, td, th; или в атрибуте src тега input

<body background="background.jpg">
        <input type="image" src="input_image.png">
        <table background="table_image.png">...</table>
<body>

1.4. Ссылки на изображения могут быть как относительные, так и абсолютные

<img src="fname.png">
<img src="../dir/fname.png">
<img src="./dir/fname.png">
<img src="/dir/fname.png">
<img src="//example.com/dir/fname.png">
<img src="http://example.com/dir/fname.png>

1.5. В заголовке HTML может быть указан тег <base>

При определении полного пути для скачивании изображений или CSS нельзя забывать о том, что в коде HTML может быть указан тег <base>, чтобы переопределить корневой адрес, начиная с которого будут рассчитываться относительные пути.

<head>
<base href="http://www.example.com/images/">
</head>

<body>
<!-- Полный путь до изображения будет http://www.example.com/images/myimage.gif -->
<img src="myimage.gif">
</body>

1.6. Кавычки в тегах могут быть как одинарные, так и двойные

<img src="fname.png">
<img src='fname.png'>

1.7. Изображения на странице могут быть вставлены не только из внешнего файла, но также могут быть внедрены в текст HTML или CSS с помощью base64-кодирования

Чтобы внедрить изображение на страницу HTML, вместо ссылки надо написать текст вроде:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA..." />

или в CSS:

div.image {
  width:100px;
  height:100px;
  background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA...);
}

2. CSS

2.1. Стили CSS могут загружаться не только из внешних файлов .css, но и быть прописаны непосредственно в коде HTML.

<ul style='list-style: url(image.png)'>

2.2. В CSS ссылки могут иметь вид url("path_to_img.png"), url('path_to_img.png') или url(path_to_img.png)

.image1 {
    background-image: url("img_01.png");
}

.image2 {
        background-image: url('img_02.png');
}

.image3 {
        background-image: url(img_03.png);
}

Формально не является корректной запись с пробелом между url и открывающейся скобкой:

.image1 {
    background-image: url ("img_01.png");
}

2.3 Внутри одного CSS-файла могут загружаться другие CSS-файлы с помощью @import

Формат использования ключевого слова @import также может быть различным

@import url('import1.css');
@import url("import2.css");
@import 'import3.css';
@import '../css/import4.css';
@import '//css/import5.css';
@import 'http://example.com/css/import6.css';

2.4 Ссылки на CSS могут быть указаны с параметрами

Например:

<link rel="stylesheet" type="text/css" href="styles.css?id=123">

2.5 На сайтах, заточенных под Internet Explorer старых версий, может встречаться встраивание изображений с использованием MHTML.

.image {
  background-image: url(mhtml:http://example.com/images.html!imgid);
}

3. Favicon (значок сайта)

3.1. Ссылка на favicon может быть не указана в HTML явно

В этом случае нужно пытаться загрузить favicon.ico из корня сервера.

3.2. Favicon может быть не только в форматах ICO и PNG

Хотя рекомендуется использовать именно форматы ICO и PNG, но некоторые браузеры умеют работать с favicons в форматах GIF (в том числе и анимированные), JPEG, SVG и APNG.

3.3. Для указания ссылки на favicon внутри тега link внутри атрибута rel могут использоваться как значение "icon", так и значение "shortcut icon"

<link rel="icon" type="image/png" href="/someimage.png" />
<link rel="shortcut icon" type="image/png" href="/someimage.png" />

3.4. Favicon может быть размером не только 16x16 пикселей

Внутри favicon.ico может содержаться несколько изображений разного формата (рекомендуются размеры 16 x 16, 32 x 32, 48 x 48 пикселей, а также на сайте Microsoft есть рекомендации использовать размеры 16 x 16, 24 x 24, 32 x 32, 64 x 64 пикселей).

Также встречался сайт, где favicon был в формате png, но при этом размером 32 x 32 пикселя.


4. Разное

4.1. Некоторые элементы могут загружаться через JavaScript

Это, наверное, самая сложная ситуация для корректного скачивания изображений.

4.2. При скачивании страниц обязательно нужно указывать User-agent

Есть сайты, которые выдают ошибку 403, если такой заголовок не установлен. Лучше делать вид, что к странице обращается известный браузер. Например, сервера https://vk.com для неизвестных по User-agent браузеров отдают мобильную версию, а не полноценную.

Пример на Python:

import urllib2

opener = urllib2.build_opener()
# Маскируемся под известный браузер, но в конце добавляем и свою сигнатуру
opener.addheaders = [('User-agent',
                                         '''Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/41.0.2228.0 Safari/537.36 OutWiker/1'
''),
                                         ('Accept-encoding', 'gzip')]
response = opener.open(url, timeout)

4.3. Многие серверы отдают страницу сжатой с помощью gzip

Могут возникнуть проблемы, если ваш клиент не умеет работать со сжатыми страницами. При скачивании страницы надо не забывать добавлять заголовок HTTP Accept-encoding со значением gzip. При получении ответа надо проверять заголовок Content-Encoding, и если он равен gzip, то распаковывать полученные данные.

Вот, например, код класса, который я использую в плагине WebPage, чтобы скачать страницу:

class Downloader (object):
    def __init__ (self, timeout):
        """
        Принимаем длительность таймаута в секундах.
        """

        self._timeout = timeout

    def download (self, url):
        opener = urllib2.build_opener()
        # Маскируемся под известный браузер, но в конце добавляем и свою сигнатуру
        opener.addheaders = [('User-agent',
                             '''Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/41.0.2228.0 Safari/537.36 OutWiker/1'
''),
                             ('Accept-encoding', 'gzip')]
        response = opener.open(url, timeout = self._timeout)

        # Проверяем, сжата ли страница
        if response.info().get('Content-Encoding') == 'gzip':
            buf = StringIO (response.read())
            zipfile = gzip.GzipFile (fileobj=buf)
            return zipfile
        return response

Заключение

Наверняка этот список не полон, что-то я мог забыть, а на что-то еще не натыкался при тестировании плагина WebPage, поэтому я буду рад вашим замечаниям и дополнениям, и этот список особенностей буду пополнять по мере обнаружения новых особенностей, которые надо учитывать при скачивании страниц из интернета.

Спасибо за внимание.

Вы можете подписаться на новости сайта через RSS, Группу Вконтакте или Канал в Telegram.
5 stars

Рейтинг 5.0/5. Всего 5 голос(а, ов)


Немного рекламы



Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 404