Published on

Scrapy Anti-Ban Guide

Most websites employ varying degrees of anti-scraping mechanisms. To collect more data, you need to adopt corresponding countermeasures.

Disable ROBOTSTXT_OBEY

In settings.py, disable this setting — otherwise most websites won't welcome your spider:

ROBOTSTXT_OBEY = False

Dynamically Set the User Agent

After running $ scrapy shell https://bloodline0902.github.io/articles/2017/12/15/Scrapy-Tutorial.html, inspect the request:

>>> request.headers
{b'Accept': [b'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'], b'Accept-Language': [b'en'], b'User-Agent': [b'Scrapy/1.4.0 (+http://scrapy.org)'], b'Accept-Encoding': [b'gzip,deflate']}

The User-Agent is Scrapy blablabla... — it's obviously giving you away.

Scrapy provides a middleware for modifying the user agent. Here is the code:

import logging
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import fake_useragent
from fake_useragent import FakeUserAgentError
# fake_useragent is a third-party library that can randomly retrieve a user agent

class RandomUserAgentMiddleware(UserAgentMiddleware):
    # Use a user agent pool to avoid being banned.
    # Note: corresponding settings must be configured in settings.py.

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

    def process_request(self, request, spider):
        try:
            ua = fake_useragent.UserAgent().random
        except FakeUserAgentError:
            logging.log(logging.ERROR, 'Get UserAgent Error')

        # Log the current user agent being used
        logging.log(logging.INFO, 'Current UserAgent: %s' % (ua))
        request.headers['User-Agent'] = ua

You also need to add the corresponding settings in settings.py:

DOWNLOADER_MIDDLEWARES = {
    'scrapydemo.middlewares.RandomUserAgentMiddleware': 543,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}

I kept running into errors when doing updates — possibly related to a proxy or the Great Firewall — and when scraping Zhihu I often encountered "browser version too old" errors. So I ultimately did not use fake_useragent.

>>> from fake_useragent import UserAgent
>>> ua = UserAgent()
>>> ua.update()
fake_useragent.errors.FakeUserAgentError: Maximum amount of retries reached

The final code:

import logging
import random
from scrapy import signals
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware

class RandomUserAgentMiddleware(UserAgentMiddleware):
    # RandomUserAgentMiddleware

    def __init__(self, settings, user_agent='Scrapy'):
        super(RandomUserAgentMiddleware, self).__init__()
        self.user_agent = user_agent
        user_agent_list_file = settings.get('USER_AGENT_LIST')
        if not user_agent_list_file:
            # If USER_AGENT_LIST_FILE settings is not set,
            # Use the default USER_AGENT or whatever was
            # passed to the middleware.
            ua = settings.get('USER_AGENT', user_agent)
            self.user_agent_list = [ua]
        else:
            with open(user_agent_list_file, 'r') as f:
                self.user_agent_list = [line.strip() for line in f.readlines()]

    @classmethod
    def from_crawler(cls, crawler):
        obj = cls(crawler.settings)
        crawler.signals.connect(obj.spider_opened,
                                signal=signals.spider_opened)
        return obj

    def process_request(self, request, spider):
        user_agent = random.choice(self.user_agent_list)
        logging.log(logging.INFO, "Current User-Agent: %s" % (user_agent))
        if user_agent:
            request.headers.setdefault('User-Agent', user_agent)

In settings.py:

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
            (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
USER_AGENT_LIST = 'useragents.txt'

...

DOWNLOADER_MIDDLEWARES = {
    'scrapydemo.middlewares.RandomUserAgentMiddleware': 543,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}

Disable Cookies

Some websites may use cookies to identify scrapers, so it is a good idea to disable this.

In settings.py:

COOKIES_ENABLED = False

Set DOWNLOAD_DELAY

Set a download delay of 2 seconds or more. See DOWNLOAD_DELAY for reference.

In settings.py:

DOWNLOAD_DELAY = 2

Use Google Cache

If possible, it is best to scrape pages via Google cache.

Use Dynamic Proxies

After your scraper's IP is blocked, you can dynamically switch to a new IP and continue scraping.

In China, you can use Xici Proxy; internationally, you can use the Tor project, or the open-source project scrapoxy.

This topic is a bit involved, so I've written a separate post about it.

Distributed Crawlers

Use a highly distributed crawling service, such as Crawlera.