Поиск по блогу

понедельник, 20 октября 2014 г.

Читаем статью "Проксирование в Scrapy" ... и знакомимся с middleware из проекта "tutorial"

Здесмь я якобы систематизирую мои представления о Scrapy middleware. Читать документацию - дело утомительное. Мне удалось найти короткие статьи на русском, в которых дается три рецепта: 1) http_proxy, 2) Spider settings 3) download middlewatr

In []:
 
In [3]:
from IPython.display import Image,HTML
Image ('http://doc.scrapy.org/en/latest/_images/scrapy_architecture.png')
Out[3]:

Далее заметки, сделаные в процессе изучения материала

In [5]:
!scrapy settings --getlist DOWNLOADER_MIDDLEWARES
{'tutorial.misc.middleware.CustomUserAgentMiddleware': 401, 'tutorial.misc.middleware.CustomHttpProxyMiddleware': 400}

In [6]:
!scrapy settings --getlist DOWNLOADER_MIDDLEWARES_BASE
{'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': 400, 'scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware': 700, 'scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware': 350, 'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware': 600, 'scrapy.contrib.downloadermiddleware.retry.RetryMiddleware': 500, 'scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware': 300, 'scrapy.contrib.downloadermiddleware.robotstxt.RobotsTxtMiddleware': 100, 'scrapy.contrib.downloadermiddleware.httpcache.HttpCacheMiddleware': 900, 'scrapy.contrib.downloadermiddleware.chunked.ChunkedTransferMiddleware': 830, 'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 750, 'scrapy.contrib.downloadermiddleware.stats.DownloaderStats': 850, 'scrapy.contrib.downloadermiddleware.httpcompression.HttpCompressionMiddleware': 590, 'scrapy.contrib.downloadermiddleware.redirect.MetaRefreshMiddleware': 580, 'scrapy.contrib.downloadermiddleware.defaultheaders.DefaultHeadersMiddleware': 550}

In []:
# Сравним со старой копией из июньского поста (она  нагляднее)
# "Пример замены scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware"

[('scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware', 400),
 ('scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware', 700),
 ('scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware', 350),
 ('scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware', 600),
 ('scrapy.contrib.downloadermiddleware.retry.RetryMiddleware', 500),
 ('scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware', 300),
 ('scrapy.contrib.downloadermiddleware.robotstxt.RobotsTxtMiddleware', 100),
 ('scrapy.contrib.downloadermiddleware.httpcache.HttpCacheMiddleware', 900),
 ('scrapy.contrib.downloadermiddleware.chunked.ChunkedTransferMiddleware', 830),
 ('scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware', 750),
 ('scrapy.contrib.downloadermiddleware.stats.DownloaderStats', 850),
 ('scrapy.contrib.downloadermiddleware.httpcompression.HttpCompressionMiddleware', 590),
 ('scrapy.contrib.downloadermiddleware.redirect.MetaRefreshMiddleware', 580),
 ('scrapy.contrib.downloadermiddleware.defaultheaders.DefaultHeadersMiddleware', 550)]
In []:
DOWNLOADER_MIDDLEWARES_BASE менять нельзя !!!
Таким образом, если мы хотим поменять дефолтные настройки, то нужно присвоить "None" заменяемой дефолтной строчке
In []:
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.CustomDownloaderMiddleware': 543,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
}
Любопытно, а что там в дефолтных настройках UserAgentMiddleware
In [7]:
%load "C:\\Users\\kiss\\Anaconda\\Lib\\site-packages\\scrapy\\contrib\\downloadermiddleware\\useragent.py"
In []:
"""Set User-Agent header per spider or use a default value from settings"""

from scrapy import signals


class UserAgentMiddleware(object):
    """This middleware allows spiders to override the user_agent"""

    def __init__(self, user_agent='Scrapy'):
        self.user_agent = user_agent

    @classmethod
    def from_crawler(cls, crawler):
        o = cls(crawler.settings['USER_AGENT'])
        crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
        return o

    def spider_opened(self, spider):
        self.user_agent = getattr(spider, 'user_agent', self.user_agent)

    def process_request(self, request, spider):
        if self.user_agent:
            request.headers.setdefault('User-Agent', self.user_agent)

Я смутно представляю себе classmethod(function), зачем они нужны? Аналог статических методов? Это еще предстоит осмыслить... Поэтому здсь эта ссылка на документацию.

Return a class method for function.

A class method receives the class as implicit first argument, just like an instance method receives the instance. To declare a class method, use this idiom @classmethod ... cls ...

The @classmethod form is a function decorator – see the description of function definitions in Function definitions for details

Я также не знаю, зачем некоторым классам наследовать class UserAgentMiddleware(object) object
object - Python class

Return a new featureless object. object is a base for all new style classes. It has the methods that are common to all instances of new style classes.

Еще один дефолтный файл downloadermiddleware\httpproxy.py

In [14]:
%load "C:\\Users\\kiss\\Anaconda\\Lib\\site-packages\\scrapy\\contrib\\downloadermiddleware\\httpproxy.py"
In []:
import base64
from urllib import getproxies, unquote, proxy_bypass
from urllib2 import _parse_proxy
from urlparse import urlunparse

from scrapy.utils.httpobj import urlparse_cached
from scrapy.exceptions import NotConfigured


class HttpProxyMiddleware(object):

    def __init__(self):
        self.proxies = {}
        for type, url in getproxies().items():
            self.proxies[type] = self._get_proxy(url, type)

        if not self.proxies:
            raise NotConfigured

    def _get_proxy(self, url, orig_type):
        proxy_type, user, password, hostport = _parse_proxy(url)
        proxy_url = urlunparse((proxy_type or orig_type, hostport, '', '', '', ''))

        if user and password:
            user_pass = '%s:%s' % (unquote(user), unquote(password))
            creds = base64.b64encode(user_pass).strip()
        else:
            creds = None

        return creds, proxy_url

    def process_request(self, request, spider):
        # ignore if proxy is already seted
        if 'proxy' in request.meta:
            return

        parsed = urlparse_cached(request)
        scheme = parsed.scheme

        # 'no_proxy' is only supported by http schemes
        if scheme in ('http', 'https') and proxy_bypass(parsed.hostname):
            return

        if scheme in self.proxies:
            self._set_proxy(request, scheme)

    def _set_proxy(self, request, scheme):
        creds, proxy = self.proxies[scheme]
        request.meta['proxy'] = proxy
        if creds:
            request.headers['Proxy-Authorization'] = 'Basic ' + creds
In []:
А находимся мы..., точнее, открыли эту страничку Notebook из папке проекта tutorial
In []:
# Полный путь к папке в w8
# C:\Users\kiss\Documents\GitHub_2\scrapy-examples-master\scrapy-examples-master\tutorial>

Проект интересен сам по себе, но пока мы сосредоточимся на middleware

In []:
!chcp 65001
!tree /f
# В notebook вместо красивых линий буквы, 
# поэтому копирую из консоли
C:.
   Books
   data_utf8.json
   ex_tutorial.ipynb
   Resources
   scrapy.cfg

├───.ipynb_checkpoints
       ex_tutorial-checkpoint.ipynb

└───tutorial
       data_utf8.json
       items.py
       pipelines.py
       settings.py
       settings.pyc
       __init__.py
       __init__.pyc
    
    ├───misc
           agents.py
           log.py
           middleware.py
           proxy.py
           __init__.py
    
    └───spiders
            naive_spider.py
            __init__.py

В примере вот такой файл для middleware, он понятнее, чем файлы по умолчанию.

In [12]:
%load "tutorial/misc/middleware.py"
In []:
from scrapy import log
from proxy import PROXIES
from agents import AGENTS

import random


class CustomHttpProxyMiddleware(object):

    def process_request(self, request, spider):
        # TODO implement complex proxy providing algorithm
        if self.use_proxy(request):
            p = random.choice(PROXIES)
            try:
                request.meta['proxy'] = "http://%s" % p['ip_port']
            except Exception, e:
                log.msg("Exception %s" % e, _level=log.CRITICAL)

    def use_proxy(self, request):
        """
        using direct download for depth <= 2
        using proxy with probability 0.3
        """
        if "depth" in request.meta and int(request.meta['depth']) <= 2:
            return False
        i = random.randint(1, 10)
        return i <= 2


class CustomUserAgentMiddleware(object):
    def process_request(self, request, spider):
        agent = random.choice(AGENTS)
        request.headers['User-Agent'] = agent
В примере "tutorial" не выполняются рекомендации об отмене дефолтных настроек.
In [13]:
%load "tutorial/settings.py"
In []:
# Scrapy settings for tutorial project
#
# For simplicity, this file contains only the most important settings by
# default. All the other settings are documented here:
#
#     http://doc.scrapy.org/en/latest/topics/settings.html
#

BOT_NAME = 'tutorial'

SPIDER_MODULES = ['tutorial.spiders']
NEWSPIDER_MODULE = 'tutorial.spiders'
ITEM_PIPELINES = {
    #'tutorial.pipelines.JsonWithEncodingPipeline': 300,
}
#Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'tutorial (+http://www.yourdomain.com)'

DOWNLOADER_MIDDLEWARES = {
    'tutorial.misc.middleware.CustomHttpProxyMiddleware': 400,
    'tutorial.misc.middleware.CustomUserAgentMiddleware': 401,
}

LOG_LEVEL = 'INFO'

В "DOWNLOADER_MIDDLEWARES" нет строчек с "None", но полагаю, что пример работает.



Посты чуть ниже также могут вас заинтересовать

Комментариев нет:

Отправить комментарий