python scrapy反爬虫技术详解
2022年5月30日大约 3 分钟约 962 字
反爬虫
- 发现某时间段访问陡增,ip相同,user-agent都是Python,直接限制访问。
破解: 使用
user-agent
随机模拟不同的浏览器,获取IP代理
- 使用cookies标记用户,发现IP变化,直接要求登录才能访问
破解: 注册账号,每次请求带cookie或者token
- 健全账号体系,只能访问朋友圈内部的好友信息
破解: 注册多个账号,多个账号联合爬取
- 同一个用户请求过于频繁,进一步加剧IP访问频率限制
破解: 模拟人请求,限制请求速度
- 弹出验证码,让识别验证码继续操作
破解: 通过各种手段识别验证码
- 增加动态网站,数据通过js动态加载,增加网络分析复杂;发现大量请求只请求html,不请求img,css,js
破解: 通过selenium和phantomjs完全模拟浏览器操作
通过下载器中间件更换user-agent
安装
fake-uesragent
包:pip install -i https://pypi.douban.com/simple/ fake-useragent
>>> vim ArticleSpider/middlewares.py
from fake_useragent import UserAgent
class RandomUserAgentMiddleware(object):
# 随机更换user-agent
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): # request请求包会经过这个函数
def get_ua():
return getattr(self.ua, self.ua_type)
random_agent = get_ua() # 获得一个随机的user-agent
request.headers.setdefault('User-Agent', random_agent) # 修改Request的User-Agent
# 将自定义的类添加到下载器中间件。键的数字越小优先执行
>>> vim ArticleSpider/settings.py
DOWNLOADER_MIDDLEWARES = {
'ArticleSpider.middlewares.RandomUserAgentMiddleware': 543, # 自定义的UserAgent中间件
'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None, # 取消系统默认的UserAgent中间件
}
RANDOM_UA_TYPE = “random”
通过下载器中间件更换IP代理
>>> vim ArticleSpider/middlewares.py
class RandomProxyMiddleware(object):
# 动态设置IP代理
def process_request(self, request, spider):
request.meta["proxy"] = "http://n.n.n.n:n"
禁用Cookies
>>> vim ArticleSpider/settings.py
COOKIES_ENABLED = False # 特别是不需要登陆的网站
限速
>>> vim ArticleSpider/settings.py
DOWNLOAD_DELAY = 10 # 10秒下载一次
AUTOTHROTTLE_ENABLED = True # 启动AutoThrottle扩展
动态网页获取
将selenium集成到scrapy中
使用下载器中间件集成
>>> vim ArticleSpider/middlewares.py
from selenium import webdriver
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):
def __init__(self):
self.browser = webdriver.Chrome(executable_path="C:/chromedriver.exe")
super(JSPageMiddleware, self).__init__()
# 通过Chrome请求动态网页,这里是同步请求,如果成为异步需要自己手写downloader
def process_request(self, request, spider):
if spider.name == "jobbole": # 如果是jobbole.com网站就是用浏览器动态请求
self.browser.get(request.url)
import time
time.sleep(3)
print("访问:{0}".format(request.url))
# 返回Response,自动停止请求
return HtmlResponse(url=self.browser.current_url, body=self.browser.page_source, encoding="utf-8", request=request)
**注意:**中间件集成有几个问题:1.当爬虫关闭时无法关闭浏览器。2.不是所有的网站都需要动态请求。
可以放到Spider里面做,每一个Spider就有一个浏览器。
异步调用Chrome请参考链接:scrapy-phantomjs-downloader
在spider内集成
>>> vim ArticleSpider/spiders/jobbole.py
from scrapy.xlib.pydispatch import dispatcher # 调度器
from scrapy import signals # 信号
from selenium import webdriver
class JobboleSpider(scrapy.Spider):
name = 'jobbole'
allowed_domains = ['blog.jobbole.com']
start_urls = ['http://blog.jobbole.com/all-posts/']
def __init__(self):
self.browser = webdriver.Chrome(executable_path="C:/chromedriver.exe")
super(JobboleSpider, self).__init__()
dispatcher.connect(self.spider_closed, signals.spider_closed) # 使用信号量关闭Chrome
def spider_closed(self, spider):
# 信号量处理函数,当爬虫退出的时候关闭Chrome
print("spider closed")
self.browser.quit()
...
>>> vim ArticleSpder/middlewares.py
from scrapy.http import HtmlResponse
class JSPageMiddleware(object):
# 通过Chrome请求动态网页
def process_request(self, request, spider):
if spider.name == "jobbole": # 如果是jobbole.com网站就是用浏览器动态请求
spider.browser.get(request.url)
import time
time.sleep(3)
print("访问:{0}".format(request.url))
# 返回Response,自动停止请求
return HtmlResponse(url=spider.browser.current_url, body=spider.browser.page_source, encoding="utf-8", request=request)
scrapy-splash
scrapy的动态网站解决方案,通过HTTP请求执行js,轻量级的,比selenium效率高一点
支持分布式,稳定性没有Chrome高
具体用法参考:scrapy-splash
其他类似方案还有:selenium grid