Как я собрал 1,5 миллиона изображений и текст страниц с помощью Scrapy, Digital Ocean Spaces(S3) и Rotating Proxies
Table Of Content
- DigitalOcean - Облачная платформа для разработчиков
- Помогает миллионам разработчиков легко создавать, тестировать, управлять и масштабировать приложения любого размера - быстрее, чем когда-либо...
- -*- coding: utf-8 -*-
- Scrapy settings for image_downloader project
- # For simplicity, this file contains only settings considered important or
- commonly used. You can find more settings consulting the documentation:
- # https://docs.scrapy.org/en/latest/topics/settings.html
- https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
- https://docs.scrapy.org/en/latest/topics/spider-middleware.html
- Crawl responsibly by identifying yourself (and your website) on the user-agent
- Obey robots.txt rules
- ROBOTSTXT_OBEY = True
- Configure maximum concurrent requests performed by Scrapy (default: 16)
- Configure a delay for requests for the same website (default: 0)
- See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
- See also autothrottle settings and docs
- The download delay setting will honor only one of:
- Disable cookies (enabled by default)
- Disable Telnet Console (enabled by default)
- Override the default request headers:
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
- 'Accept-Language': 'en',
- Enable or disable spider middlewares
- See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
- 'image_downloader.middlewares.ImageDownloaderSpiderMiddleware': 543,
- Enable or disable downloader middlewares
- See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
- 'image_downloader.middlewares.ImageDownloaderDownloaderMiddleware': 543,
- Enable or disable extensions
- See https://docs.scrapy.org/en/latest/topics/extensions.html
- 'scrapy.extensions.telnet.TelnetConsole': None,
- Configure item pipelines
- See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
- 'image_downloader.pipelines.ImageDownloaderPipeline': 300,
- Enable and configure the AutoThrottle extension (disabled by default)
- See https://docs.scrapy.org/en/latest/topics/autothrottle.html
- The initial download delay
- The maximum download delay to be set in case of high latencies
- The average number of requests Scrapy should be sending in parallel to
- each remote server
- Enable showing throttling stats for every response received:
- Enable and configure HTTP caching (disabled by default)
- See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
- 'scrapy.pipelines.images.ImagesPipeline': 1,
- 'data': 'scrapy.core.downloader.handlers.datauri.DataURIDownloadHandler',
- IMAGES_STORE = 'C:/Users/amag/Downloads/scrapy-master/image_downloader/downloads'
- Obey robots.txt rules
- -*- coding: utf-8 -*-
- Define your item pipelines here
- # Don't forget to add your pipeline to the ITEM_PIPELINES setting
- See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
- -*- coding: utf-8 -*-
- Define here the models for your scraped items
- # See documentation in:
- https://docs.scrapy.org/en/latest/topics/items.html
Применение:
У меня было требование извлечь данные и изображения с веб-сайта, на котором было 100 000 страниц и примерно 500 ГБ изображений. Метод должен был быть экономичным и эффективным.
Предложенное решение:
Я решил использовать Scrapy, который является открытым фреймворком на языке Python для парсинга веб-сайтов.
Я выбрал Digital Ocean для настройки виртуальной машины и Digital Ocean Spaces в качестве хранилища данных для загрузки изображений, чтобы клиент мог их позже скачать.
Я выбрал StormProxies в качестве решения для прокси, так как оно экономично и предоставляет неограниченное количество запросов за небольшую плату.
Реализация:
Спецификация виртуальной машины Digital Ocean:
ОС: Ubuntu 18.04 LTS
Размер: Стандартный, 4vCPU, 4 ГБ ОЗУ
DigitalOcean - Облачная платформа для разработчиков
Помогает миллионам разработчиков легко создавать, тестировать, управлять и масштабировать приложения любого размера - быстрее, чем когда-либо...
cloud.digitalocean.com
Хранилище: Подписка на DigitalOcean Spaces (5 долларов за 250 ГБ в месяц)
Создайте пространство внутри вашей подписки на DigitalOcean и запишите его название. Мы будем напрямую сохранять изображения в DigitalOcean Spaces. Scrapy совместим с любым хранилищем S3, поэтому DigitalOcean Spaces подходит без необходимости переписывать код.
Подписка на Storm Proxies:
40 потоков/1 IP с неограниченными запросами (39 долларов в месяц). Действительно экономичный вариант по сравнению с другими провайдерами.
Код Scrapy:
import scrapy
from ..items import MyImage
import base64
class HubSpider(scrapy.Spider):
name = 'spider_sample'
allowed_domains = ['Allowed_Domain']
start_urls = ['URL_of_Website']
def parse(self, response):
links = response.css('#masonry .bold a::attr(href)').extract()
for link in links:
url = response.urljoin(link)
yield scrapy.Request(url=url, callback=self.parse_data)
next_page = response.css('.pagination a[title*="Next Page"]::attr(href)').extract_first()
if next_page:
next_url = response.urljoin(next_page)
yield scrapy.Request(url=next_url, callback=self.parse)
def parse_data(self, response):
image_data = MyImage()
image_data["Creator"] = response.css('.pull-left span::text').extract_first()
image_data["Name"] = ''.join(response.css('.well-inline h2::text').extract()).strip()
a_img = ["NA"]
img_urls = response.css('a[rel="abcd"]::attr(href)').extract_first()
img_urls = response.urljoin(img_urls)
a_img[0] = img_urls
titles = response.css('a[rel="abcd"]::attr(title)').extract()
# img_urls = [response.urljoin(i) for i in img_urls]
image_data["image_urls"] = a_img
# image_data["title"] = titles
image_data["Description"] = ''.join(response.xpath('//*[@id="downloadDescription"]//text()').extract()).strip()
try:
image_names = img_urls.split("/")[-1]
except IndexError:
image_names = image_data["Creator"]
# image_names = [i.split("/")[-1] for i in img_urls]
try:
image_data["category2"] = response.css('.breadcrumb li a::text').extract()[1]
image_data["category3"] = response.css('.breadcrumb li a::text').extract()[2]
except IndexError:
image_data["category2"] = "NA"
image_data["category3"] = "NA"
image_data["category4"] = response.css('.hidden-phone::text').extract_first(default="NA")
image_data["abcd"] = response.css('.abcd .well-small p img::attr(alt)').extract() + response.css('.abcd::text').extract()
try:
image_data["Downloads"] = response.css('.noborder .well-small .font-large *::text').extract()[1]
except IndexError:
image_data["Downloads"] = "NA"
tags = response.css('#threadtagsarea a::text').extract()
tags = ["#"+i for i in tags]
image_data["Tags"] = tags if tags else "NA"
data_1 = response.css('.abcd .well-small p span[class="bold"]::text').extract()
info_box = {}
for i in range(len(data_1)):
info_box[data_1[i]] = response.css('.abcd well-small em a::text').extract()[i]
image_data["ABCD"] = abcd if info_box else "NA"
# image_data["image_name"] = titles
image_data["Image_Names"] = image_names
image_data["Link"] = response.url
yield image_data
settings.py файл в Scrapy (оптимизирован для скорости и параллельности)
# -*- coding: utf-8 -*-
# Scrapy settings for image_downloader project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
# https://docs.scrapy.org/en/latest/topics/settings.html
# https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
BOT_NAME = 'image_downloader'
SPIDER_MODULES = ['image_downloader.spiders']
NEWSPIDER_MODULE = 'image_downloader.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'image_downloader (+http://www.yourdomain.com)'
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
# Obey robots.txt rules
# ROBOTSTXT_OBEY = True
# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
DOWNLOAD_DELAY = 0.25
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
# Disable cookies (enabled by default)
#COOKIES_ENABLED = False
# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False
# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# 'image_downloader.middlewares.ImageDownloaderSpiderMiddleware': 543,
#}
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
# 'image_downloader.middlewares.ImageDownloaderDownloaderMiddleware': 543,
#}
# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
# 'image_downloader.pipelines.ImageDownloaderPipeline': 300,
#}
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False
# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
ITEM_PIPELINES = {'image_downloader.pipelines.ImageDownloaderPipeline': 300}
# 'scrapy.pipelines.images.ImagesPipeline': 1,
#DOWNLOAD_HANDLERS = {
# 'data': 'scrapy.core.downloader.handlers.datauri.DataURIDownloadHandler',
#}
REACTOR_THREADPOOL_MAXSIZE = 15
DOWNLOAD_TIMEOUT = 60
ROTATING_PROXY_LIST = [
'Proxy_IP_from_stormproxy_1',
'Proxy_IP_from_stormproxy_2',
# ...
]
DOWNLOADER_MIDDLEWARES = {
# ...
'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
'rotating_proxies.middlewares.BanDetectionMiddleware': 620,
# ...
}
IMAGES_STORE = 's3://sims4amit/images/' # Это имя пространства, созданного в подписке DigitalOcean Spaces
# IMAGES_STORE = 'C:/Users/amag/Downloads/scrapy-master/image_downloader/downloads'
AWS_ACCESS_KEY_ID = "Access_key_id_digital_ocean"
AWS_SECRET_ACCESS_KEY = "secret_key_digital_ocean"
AWS_ENDPOINT_URL = 'https://nyc3.digitaloceanspaces.com'
AWS_REGION_NAME = "nyc3"
AWS_USE_SSL = False
AWS_VERIFY = False
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
Пользовательский пайплайн для переименования загруженных имен изображений.
pipelines.py файл
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import scrapy
from scrapy.pipelines.images import ImagesPipeline
import os
from urllib.parse import urlparse
class ImageDownloaderPipeline(ImagesPipeline):
def file_path(self, request, response=None, info=None):
return 'files/' + os.path.basename(urlparse(request.url).path)
items.py файл (Пользовательский пайплайн для элементов)
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class MyImage(scrapy.Item):
# title = scrapy.Field()
# createDate = scrapy.Field()
image_urls = scrapy.Field()
# images = scrapy.Field()
Creator = scrapy.Field()
Name = scrapy.Field()
Description = scrapy.Field()
category2 = scrapy.Field()
category3 = scrapy.Field()
category4 = scrapy.Field()
abcd = scrapy.Field()
Downloads = scrapy.Field()
Tags = scrapy.Field()
Image_Names = scrapy.Field()
abcd = scrapy.Field()
Link = scrapy.Field()
# image_name = scrapy.Field()
Я клонировал репозиторий GitHub на виртуальную машину DigitalOcean и добавил IP-адрес в список разрешенных на подписке Storm Proxies. Вы можете запустить код Scrapy в сеансе screen на Linux VM, чтобы процесс не прерывался.
Вот команда для запуска паука Scrapy:
scrapy crawl ImageDownloader -o output.csv — logfile logs.txt
Изображения будут сохранены в подписке DigitalOcean Spaces и будут доступны для загрузки позже.
Весь этот набор обходится менее чем за 10 долларов! (если прорейтить подписку на Storm Proxies). Мой скрэпер выполнился за 20 часов.
Вы можете найти полный код по ссылке https://github.com/amit1411/ImageDownloader