Получаем картиночки на python при помощи grab for Fun and Profit

от
Прочие языки    граббинг, python

Давеча понадобилось мне выкачать пак из 8к картиночек с зерочана. Делать это вручную было как-то совсем не вариантом, поэтому выбор пал на вариант с грабом картиночек.

Языком на котором я решил пилить грабер стал пайтон, почему? Огромное количество сторонних инструментов которые без особой жопной боли можно приспособить под свои нужды. Таким вот инструментом и стал фреймворк grab о котором и пойдёт сегодня речь.
Стоит сразу обмолвиться что нормальной документации у него нет и по сей день, так что я оперировал немного устаревшей инфой, однако даже так, фреймворк делал своё дело и очень мне понравился.

Для начала нужно его поставить, это вполне спокойно делается командой
  1. pip install grab

Окей, теперь, для примера получим страницу, ну скажем, с артами по Undertale, тобишь http://www.zerochan.net/Undertale

Для этого:
  1. from grab import *
  2. g = Grab(log_file='out.html')
  3. g.setup(connect_timeout=50, timeout=50)
  4. g.go('http://www.zerochan.net/Undertale')
скачать

Думаю, у многих возник логичный вопрос - а зачем такой большой таймаут? Дело в том, что в фреймворке используется библиотека pycurl, и лично у меня первый коннект может занимать до 40 секунд. От чего так, я не понял, на 7й винде и линуксе всё было норм.

Итак, будем исходить из того что мы получили нашу html страницу.
Ага, вот и оно, картинки с маленькими превьюшками!
  sss.png

Превосходно. Попробуем теперь открыть какую-либо картинку в полном размере, посмотрим какой у неё линк?
Бинго!

превью: http://s3.zerochan.net/Undertale.240.1991330.jpg
сама картинка: http://static.zerochan.net/Undertale.full.1991330.jpg

Всего-лишь везение, но это крайне упростило мне задачу, давайте соберём их всех получим все картинки с этой страницы.
Для этого определим функцию get_img которая будет принимать list тегов img из которых мы извлечём параметр src, по которому мы потом и будем их скачивать при помощи urllib предварительно обработав.

  1. # -*- coding: UTF-8 -*-
  2.  
  3. from grab import *
  4. import time
  5. import os
  6. import urllib
  7.  
  8.  
  9. def get_img(img_link_list):
  10.     for x in img_link_list:
  11.         # Получаем параметр src тега img
  12.         img_url = str(x.get('src'))
  13.         # Исправляем ссылку
  14.         img_url = img_url.replace('.240.', '.full.')
  15.         img_url = img_url.replace('s3.', 'static.')
  16.         # Скачиваем изображение
  17.         img = urllib.urlopen(img_url).read()
  18.         # Пусть его именем станет его номер и расширение
  19.         img_name = img_url.split('.')[len(img_url.split('.')) - 2] + '.' + img_url.split('.')[len(img_url.split('.')) - 1]
  20.         # Если такой файл уже существует, удаляем к чертям
  21.         if os.path.isfile(img_name): os.remove(img_name)
  22.         # Сохраняем, закрываем, ждём дабы нас не послали за попытку DDOS`a
  23.         f = open(img_name, "wb")
  24.         f.write(img)
  25.         f.close()
  26.         time.sleep(0.4)
  27.  
  28.  
  29. g = Grab()
  30. g.setup(connect_timeout=50, timeout=50)
  31. g.go('http://www.zerochan.net/Undertale')
  32. get_img(g.xpath_list('//li/a/img'))
скачать

Думаю, я достаточно подробно расписал где мы и что делаем, единственное чего я не объяснил, так это того что за магией мы получаем этот самый список с тегами. И тут мы подходим к такой возможности фрейморка как работа с DOM деревом.
Для этого и предназначены методы xpath

xpath — вернуть первый элемент удовлетворяющий запросу
xpath_list — вернуть все элементы
xpath_text — вернуть текстовое содержимое элемента (и всех вложенных элементов)
xpath_number — вернуть первое числовое вхождение из текста элемента (и всех вложенных элементов)

Итак, запускаем, и получаем аж 22 поименованные картиночки.
   ssффs.png
Однако мы ведь хотим ВСЕ картиночки, со ВСЕХ страниц.
Для этой задачи лучше вынести переход на страницу в отдельную функцию. Грабить будем циклом. Количество страниц получим автоматически парсингом, как и ссылку для перехода на следующую страницу.

А вот и нужная нам строка в html`e, значит получение ссылки для перехода будет выглядеть так:
  1. g.xpath('//p[@class="pagination"]/a[@rel="next"]').get('href')
  1. <p class="pagination" style="margin-bottom: 50px; ">
  2.     page 1 of 15    <a href="?p=2" tabindex="1" rel="next">Next &raquo;</a>
  3. </p>

Итак, готовый код

  1. # -*- coding: UTF-8 -*-
  2.  
  3. from grab import *
  4. import time
  5. import os
  6. import urllib
  7.  
  8.  
  9. def next_page(flink):
  10.     global max_page, i, sub_link, g
  11.     g.go(flink)
  12.     time.sleep(0.4)
  13.     if max_page != i: sub_link = g.xpath('//p[@class="pagination"]/a[@rel="next"]').get('href')
  14.  
  15.  
  16. def get_img(img_link_list):
  17.     for x in img_link_list:
  18.         # Получаем параметр src тега img
  19.         img_url = str(x.get('src'))
  20.         # Исправляем ссылку
  21.         img_url = img_url.replace('.240.', '.full.')
  22.         img_url = img_url.replace('s3.', 'static.')
  23.         # Скачиваем изображение
  24.         img = urllib.urlopen(img_url).read()
  25.         # Пусть его именем станет его номер и расширение
  26.         img_name = img_url.split('.')[len(img_url.split('.')) - 2] + '.' + img_url.split('.')[len(img_url.split('.')) - 1]
  27.         # Если такой файл уже существует, удаляем к чертям
  28.         if os.path.isfile(img_name): os.remove(img_name)
  29.         # Сохраняем, закрываем, ждём дабы нас не послали за попытку DDOS`a
  30.         f = open(img_name, "wb")
  31.         f.write(img)
  32.         f.close()
  33.         time.sleep(0.4)
  34.  
  35.  
  36. g = Grab()
  37. g.setup(connect_timeout=50, timeout=50)
  38. link = 'http://www.zerochan.net/Undertale'
  39. sublink = ''  # ?p=xxx и тому подобное
  40. i = 1
  41.  
  42. #  Получаем число страниц для грабежа и разбоя
  43. g.go(link)
  44. max_page = int(g.xpath_text('//p[@class="pagination"]').split(' ')[3])
  45.  
  46. while i <= max_page:
  47.     next_page(link + sub_link)
  48.     get_img(g.xpath_list('//li/a/img'))
  49.     print('Grab page number %i ended' % i)
  50.     i += 1
  51.  
  52. print('Done')
  53. exit()
скачать

Полностью готовую версию моего проекта можно будет найти тут.

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

Полезные линки:
Site:
     http://grablib.org
Github project: source code and issue tracker:
     http://github.com/lorien/grab
English mailing list:
     http://groups.google.com/group/grab-users
English documentation:
     http://docs.grablib.org/en/latetst/
Russian mailing list:
     http://groups.google.com/group/python-grab
Russian documentation:
     http://docs.grablib.org/ru/latetst/
Статья на Хабрахабре
     https://habrahabr.ru/post/127584/
  • +3
  • views 7691