25
2025
04
01:07:59

Python爬虫之Scrapy如何应对网站反爬虫策略

1.爬虫的基本概念

爬虫:自动获取网站数据的程序,关键是批量的获取

反爬虫:使用技术手段防止爬虫程序的方法

误伤:反爬技术将普通用户识别为爬虫,如果误伤过高,效果再好也不能用

成本:反爬虫需要的人力和机器成本

拦截:成功拦截爬虫,一般拦截率越高,误伤率越高

2.反爬虫的目的

初级爬虫:简单粗暴,不管服务器压力,容易弄挂网站

数据保护:具有知识产权的数据

失控的爬虫:由于某些情况下,忘记或者无法关闭的爬虫

商业竞争对手:防止被对手爬走了数据

3.爬虫和反爬虫的经典应对场景(重点)


4.随机更换User-Agent

通过之前的执行流程图可以看到,Requests和Response之间有很多的Middleware,这个UserAgentMiddleware就是默认用来处理User-Agent的。

从源码可以看到,如果不在settings文件中定义USER_AGENT,那么就会使用默认的"Scrapy"


4.1 全局User-Agent

如果要定义全局默认的User-Agent,可以在settings.py文件中去定义

# 定义全局的User-Agent
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36"

4.2 局部User-Agent

局部user-agent是在我们自己写的spider代码里面定义的,可以通过custom_settings来定义

custom_settings = {
        # 是否开启自定义cookie,如果为False则使用settings中的cookie
        "COOKIES_ENABLED": True,
        # 下载延迟时间,值越大请求越慢
        "DOWNLOAD_DELAY": 10,
        # 是否开启随机延迟时间
        "RANDOMIZE_DOWNLOAD_DELAY": True,
        # 并发请求数
        "CONCURRENT_REQUESTS": 1,
        # 浏览器的user-agent(注意这里的名字是USER_AGENT)
        "USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.4951.54 Safari/537.36",
    }

4.3 fake-useragent(重点)

官方地址:https://github.com/hellysmile/fake-useragent

默认维护的User-Agent的链接:https://fake-useragent.herokuapp.com/browsers/0.1.11(后面是最新版本号)

4.3.1 安装模块

pip install fake-useragent

4.3.2 Middleware编写
from fake_useragent import UserAgent

# 随机更换User-Agent
class RandomUserAgentMiddleware(object):
    def __init__(self, crawler):
        super(RandomUserAgentMiddleware, self).__init__()
        self.ua = UserAgent()

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_request(self, request, spider):
        request.headers.setdefault(b'User-Agent', self.ua.random)
4.3.3 settings配置
DOWNLOADER_MIDDLEWARES = {
    # 'article_spider.middlewares.ArticleSpiderDownloaderMiddleware': 543,
    # 注释掉默认的useragent的中间件
    # 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
    # 自定义useragent的中间件
    'article_spider.middlewares.RandomUserAgentMiddleware': 1
}
4.3.4 如何自定义生成的策略?

fake-useragent默认支持很多种的user-agent的生成方式,有如下类型:ua.ie、ua.opera、ua.chrome等

我们有的时候就想只生产chrome的user-agent,那如何动态切换配置了?


动态切换user-agent的生成策略

通过self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random"),可以读取配置文件的数据

通过get_ua()函数可以动态调用对应的方法,相当于:self.ua.self.ua_type,但是python语法是不支持这样调用的。所以我们通过get_ua()这个内部函数来实现

from fake_useragent import UserAgent

# 随机更换User-Agent
class RandomUserAgentMiddleware(object):
    def __init__(self, crawler):
        super(RandomUserAgentMiddleware, self).__init__()
        self.ua = UserAgent()
        self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_request(self, request, spider):
        # 动态获得配置的策略
        def get_ua():
            return getattr(self.ua, self.ua_type)

        request.headers.setdefault(b'User-Agent', get_ua())

RANDOM_UA_TYPE这个是自定义配置,默认为random

以后如果想改成chrome或其他的,只需要更改settings中的这个配置即可

DOWNLOADER_MIDDLEWARES = {
    # 'article_spider.middlewares.ArticleSpiderDownloaderMiddleware': 543,
    # 注释掉默认的useragent的中间件
    # 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
    # 自定义useragent的中间件
    'article_spider.middlewares.RandomUserAgentMiddleware': 1
}
# 随机user-agent的策略
RANDOM_UA_TYPE = "random"

5.IP代理池

有些网站会限制同一个IP频繁爬取网站,这个时候就需要用到IP代理池了。

IP代理推荐:https://zhuanlan.zhihu.com/p/33576641

5.1 爬取代理IP


5.2 实现随机取代理IP

5.2.1 数据库设计
CREATE TABLE `proxy_ip` (
  `ip` varchar(255) NOT NULL,
  `port` varchar(255) NOT NULL,
  `speed` float DEFAULT NULL,
  `proxy_type` varchar(5) DEFAULT NULL,
  PRIMARY KEY (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5.2.2 代码
# mysql高效的取随机数据
SELECT * 
FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `table`)-(SELECT MIN(id) FROM `table`))+(SELECT MIN(id) FROM `table`)) AS id) AS t2 
WHERE t1.id >= t2.id 
ORDER BY t1.id LIMIT 1;
class ProxyIP(object):
    # 随机提取IP
    def get_random_ip(self):
        random_ip_sql = """
            select ip, port from proxy_ip
            order by RAND()
            limit 1
        """
        cursor.execute(random_ip_sql)
        for ip_info in cursor.fetchall():
            ip = ip_info[0]
            port = ip_info[1]

            # 判断ip是否可用
            if self.judge_ip(ip, port):
                return "http://{0}:{1}".format(ip, port)
            else:
                # 如果不可用,继续查找可用IP
                self.get_random_ip()

    # 判断ip是否可用
    def judge_ip(self, ip, port):
        http_url = "http://www.baidu.com"
        proxy_url = "http://{0}:{1}".format(ip, port)
        proxy_dict = {
            "http": proxy_url,
            # "https": proxy_url,
        }
        try:
            resp = requests.get(http_url, proxies=proxy_dict)
            code = resp.status_code
            if 200 <= code < 300:
                print("effective ip")
                return True
            else:
                # 如果ip不可用,删除ip
                print("invalid ip and port")
                self.delete_ip(ip)
                return False
        except Exception as e:
            print("invalid ip and port")
            print(e)
            self.delete_ip(ip)
            return False

    # 删除IP
    def delete_ip(self, ip):
        delete_ip_sql = """
            delete from proxy_ip where ip = '{0}'
        """.format(ip)
        cursor.execute(delete_ip_sql)
        conn.commit()
        return True


if __name__ == '__main__':
    proxy_ip = ProxyIP()
    print(proxy_ip.get_random_ip())
5.2.3 Middleware中间件
from article_spider.tools.proxy_ip import ProxyIP

# 随机获取代理IP
# github上有scrapy-proxies,提供了很多配置功能,可以自己改造一下
# github上有scrapy-crawlera,这个是收费的
# python使用Tor(洋葱浏览器)爬取网站
class RandomIPMiddleware(object):
    def process_request(self, request, spider):
        proxy_ip = ProxyIP()
        request.meta["proxy"] = proxy_ip.get_random_ip()
5.2.4 settings配置
DOWNLOADER_MIDDLEWARES = {
    # 自定义useragent的中间件
    'article_spider.middlewares.RandomUserAgentMiddleware': 1,
    # 随机IP代理
    'article_spider.middlewares.RandomIPMiddleware': 2
}

5.3 总结

我们在爬取网站的时候,总体还是要采用后面讲的限速的策略,不要爬的太快了,一定要珍惜自己的IP!

如果你想爬的更快的话,可以使用IP代理池进行爬取

5.4 其他好用的IP代理的实现

上面的代理IP都是爬取的免费的,免费的一般都不稳定,可以考虑一些付费的IP代理

5.4.1 scrapy-proxies

这个都是在settings文件中进行配置的,不支持数据库操作,所以可以自己对源码改一下。

官方地址:https://github.com/aivarsk/scrapy-proxies

5.4.2 scrapy-crawlera(收费)

这个是代理IP商提供的开源代码,但是使用他们的代理IP是收费的

官方地址:https://github.com/scrapy-plugins/scrapy-zyte-smartproxy

官方文档:https://scrapy-zyte-smartproxy.readthedocs.io/en/latest/

5.4.2 Tor(洋葱浏览器)爬取网站

洋葱浏览器可以使用代理隐藏自己的真实IP地址,常用于黑客使用,我们的python也可以操作Tor来爬取网站

文章:https://copyfuture.com/blogs-details/2020051818370565011ksvjjtfy8inp9

6.验证码识别

6.1 编码实现(tesseract-ocr)

这个识别准确度比较低,不太推荐使用

还可以使用之前我们写的opencv识别工具类:https://www.yuque.com/suifengbiji/kzz2hu/lufd1y#MKjYY

6.2 在线打码(推荐)

图鉴:图片识别-广告识别-目标检测-准快信息技术有限公司

超级鹰:超级鹰验证码识别-专业的验证码云端识别服务,让验证码识别更快速、更准确、更强大

打码兔:打码兔-手机打码平台 - 打码app,正规打码网站

6.2.1 超级鹰打码平台代码

1)工具类

class Chaojiying_Client(object):

    def __init__(self, username, password, soft_id):
        self.username = username
        password = password.encode('utf8')
        self.password = md5(password).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }

    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
                          headers=self.headers)
        return r.json()

    def ReportError(self, im_id):
        """
        im_id:报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()


if __name__ == '__main__':
    # 用户中心>>软件ID 生成一个替换 96001
    chaojiying = Chaojiying_Client('13279385584', 'ky6856', '930856')
    # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    im = open('a.jpg', 'rb').read()
    # 1902 验证码类型  官方网站>>价格体系 3.4+版 print 后要加()
    print(chaojiying.PostPic(im, 1902))

2)识别超级鹰自己的登录验证码

from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import time
from chaojiying import Chaojiying_Client

web = Chrome()
web.get("http://www.chaojiying.com/user/login/")

# 截取验证码图片,自动获得图片的二进制
img = web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/div/img").screenshot_as_png
# 识别验证码
chaojiying = Chaojiying_Client('13279385584', 'ky6856', '930856')
dic = chaojiying.PostPic(img, 1902)
verify_code = dic.get("pic_str")

# 填写用户名
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[1]/input").send_keys("13279385584")
# 填写密码
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[2]/input").send_keys("ky6856")
# 填写验证码
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[3]/input").send_keys(verify_code)
time.sleep(5)
# 点击登陆
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[4]/input").click()

6.3 人工代码

后面是真人在给你识别,价格最贵,但是识别率肯定是最高的


7.禁用Cookie

将Cookie禁掉,就不会在Requests中携带cookie了,那网站就无法跟踪,适用于不需要登录的网站。

我们可以在scrapy中的settings.py文件中做全局配置

# Disable cookies (enabled by default)
COOKIES_ENABLED = False

也可以在我们具体的spider代码中做自定义配置custom_settings

class ZhihuSpider(scrapy.Spider):
    name = 'zhihu'
    allowed_domains = ['www.zhihu.com']
    start_urls = ['http://www.zhihu.com/']
    
    # 禁用Cookie
    custom_settings = {
        "COOKIES_ENABLED": False
    }
}

8.限制爬取速度

中文文档:AutoThrottle 扩展 — Scrapy 2.5.0 文档

官方文档:AutoThrottle extension — Scrapy 2.11.2 documentation

主要是通过这个AutoThrottle扩展来实现限制速度,可以在settings文件中配置

也可以在spider代码中自定义配置custom_settings

8.1 介绍

# 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
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# 下载延迟时间,每隔3秒下载一个网页
DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
CONCURRENT_REQUESTS_PER_DOMAIN = 16
CONCURRENT_REQUESTS_PER_IP = 16

8.2 代码

cookie_list = "111111"
custom_settings = {
    # 是否开启自定义cookie,如果为False则使用settings中的cookie
    "COOKIES_ENABLED": True,
    # 下载延迟时间,值越大请求越慢
    "DOWNLOAD_DELAY": 10,
    # 是否开启随机延迟时间
    "RANDOMIZE_DOWNLOAD_DELAY": True,
    # 并发请求数
    "CONCURRENT_REQUESTS": 1,
    # 浏览器的user-agent(注意这里的名字是USER_AGENT)
    "USER_AGENT": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.4951.54 Safari/537.36",
    # 默认的请求头
    'DEFAULT_REQUEST_HEADERS': {
        'Accept': 'application/json, text/javascript, */*; q=0.01',




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://hqyman.cn/post/10878.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: