常见爬虫技术

1.降低请求频率

对于Scrapy框架来说,在配置文件settings.py中设置DOWNLOAD_DELAY即可。以下代码设置下载延迟时间为3秒,即两次请求间隔3秒。

DOWNLOAD_DELAY = 3

为了防止请求过于规律,可以使用RANDOMIZE_DOWNLOAD_DELAY设置一个介于0.5 *DOWNLOAD_DELAY和1.5 *DOWNLOAD_DELAY之间的随机延迟时间。

RANDOMIZE_DOWNLOAD_DELAY = True

2. 修改请求头

网站可能会对HTTP请求头的每个属性做检查。HTTP定义了十多个请求头类型,不过大多数都不常用,只有几个字段被大多数浏览器用来初始化所有的网络请求,如下表所示。其中最重要的参数是User-Agent,我们使用它来伪装成浏览器。如果你正在处理一个警觉性非常高的网站,就要注意那些经常用却很少检查的请求头,如Accept-Language属性。
在这里插入图片描述

3. 禁用Cookie

有些网站会通过Cookie来发现爬虫的轨迹。因此,如果不是特殊需要,可以禁用Cookie,这样网站就无法通过Cookie来侦测到爬虫了。Scrapy中禁止Cookie功能也非常简单,在配置文件settings.py中做如下设置:

COOKIES_ENABLED = False

4. 伪装成随机浏览器

我们都是通过User-Agent将爬虫伪装成固定浏览器,但是对于警觉性高的网站,会侦测到这一反常现象,即持续访问网站的是同一种浏览器。因此,每次请求时,可以随机伪装成不同类型的浏览器。Scrapy中的中间件UserAgentMiddleware就是专门用于设置User-Agent的,在爬虫运行时,会自动将User-Agent添加到HTTP请求中,并且可以设置多个浏览器,请求时可以随机添加不同的浏览器。要实现此功能,只需要完成以下3步:

1)设定浏览器列表

  MY_USER_AGENT = [
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
 "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
 "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
 "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
 "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
 "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
 "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
 "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
 "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
 "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
 "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
 "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
 "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
 "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10",
 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
 ]

将settings.py中设置固定浏览器代码删除或注释掉,添加上面内容。

2)在中间件UserAgentMiddleware中从浏览器列表中随机获取一个浏览器

在middlewares.py中定义基于UserAgentMiddleware的类,实现对User-Agent的随机设置,代码如下:

#导入UserAgentMiddleware组件模块
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random#导入随机模块
from xx.settings import MY_USER_AGENT#导入浏览器列表
#定义类xxUserAgentMiddleware,用于设置随机设置user-agent
#继承于UserAgentMiddleware
class xxUserAgentMiddleware(UserAgentMiddleware):
    #处理Request请求函数
    def process_request(self, request, spider):
        #使用random模块的choice函数从列表MY_USER_AGENT中随机获取一个浏览器类型
        agent = random.choice(list(MY_USER_AGENT))
        print("user-agent:",agent)
        #将User-Agent附加到Reqeust对象的headers中
        request.headers.setdefault('User-Agent', agent)

3)启用中间件UserAgentMiddleware。

在settings.py中,启用中间件xxtUserAgentMiddleware。

更简单的办法: 使用fake_useragent库实现浏览器的随机获取

# 导入UserAgentMiddleware组件模块
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
# 导入fake-useragent库
from fake_useragent import UserAgent

# 定义类xxUserAgentMiddleware,用于设置随机设置user-agent
class xxUserAgentMiddleware(UserAgentMiddleware):
    #处理Request请求函数
    def process_request(self, request, spider):
	    # 生成UserAgent对象
        ua = UserAgent()
        # 随机获取User-Agent
        request.headers['User-Agent'] = ua.random
        print(request.headers['User-Agent'])#打印

5.更换IP地址

有的网站会设置一个IP访问频率的阈值,一旦IP访问频率超过这个阈值,就会被认定为机器人程序,进而封杀IP,禁止访问网站的任何信息。一个很简单的方法就是设置延时,但这显然会降低爬虫的效率,而IP地址又无法伪造。这时,就只能使用HTTP代理服务器了。

HTTP代理服务器(HTTP Proxy Server)其功能就是代理网络用户去取得网络信息,它是客户端浏览器和网站服务器之间的信息中转站。

可以通过以下几种方式获取代理服务器:

  • 自行搭建代理服务器
    可以购买阿里云或者腾讯云服务器,自行搭建代理服务器。这种方式的优点是可靠、稳定;缺点是资金、时间和技术成本都比较高。

  • 使用免费代理服务器
    网络上有许多免费的代理服务器供大家使用,搜索“代理”就能找到不少代理服务平台,这些平台一般都会提供免费代理服务器信息。这种方式的优点是免费、省心、省力;缺点是代理服务器有效期短、不稳定、不可控。

  • 购买付费代理服务器
    付费代理服务平衡了上述两种方案,即在花费较少资金的情况下,提供可靠、稳定、时效较长的代理服务器。以下为部分免费和付费代理服务平台:

    • 西刺代理:提供免费代理,不提供付费代理服务

    • 快代理:提供免费代理和付费代理服务

    • 讯代理:只提供付费代理服务。

Scrapy设置代理服务器非常简单,只需在Request请求中将代理服务器的URL赋给meta参数的键proxy。

Request(url,meta={"proxy": 'http://119.101.117.163:99999', "download_timeout": 10})

通过爬虫,将西刺代理中的高匿代理服务器的URL爬取下来,经过验证后,将有效的URL持久化到Redis数据库中,提供给后续的爬虫项目使用,主要代码如下:

  1. 创建Scrapy项目
scrapy startproject xici_proxy
  1. 使用Item封装数据
class XiciItem(scrapy.Item):
    url = scrapy.Field()#url
    cryptonym = scrapy.Field()#是否高匿名
  1. 创建Spider文件及Spider类
from scrapy import Request
from scrapy.spiders import Spider
from xx.items import XiciItem
from twisted.internet.error import DNSLookupError,TimeoutError, TCPTimedOutError#导入错误模块

class XiciSpider(Spider):
    name = 'xici'
    current_page = 1 # 当前页
    def __init__(self,url):
        self.test_url = url # 从命令中获取测试网站的url

    #获取初始Request
    def start_requests(self):
	    # 西祠代理免费代理的url地址
        url = "https://www.xicidaili.com/nn"
        yield Request(url)
    # 数据解析
    def parse(self, response):
        list_selector = response.xpath("//tr[@class='odd']")
        # 依次读取每条代理的信息,从中获取ip、端口、类型
        for one_selector in list_selector:
            item = XiciItem()
            # 获取ip
            ip = one_selector.xpath("td[2]/text()").extract()[0]
            # 获取端口
            port =  one_selector.xpath("td[3]/text()").extract()[0]
            # 获取是否高匿
            cryptonym = one_selector.xpath("td[5]/text()").extract()[0]
            # 获取类型(http或https)
            http = one_selector.xpath("td[6]/text()").extract()[0]
            # 拼接成完整的代理url
            url = "{}://{}:{}".format(http,ip,port)
            item["url"] = url
            # 一定要设置dont_filter=True不过滤重复请求
            yield Request(self.test_url,#测试网站的url
                          callback=self.test_parse,#回调函数
                          errback=self.error_back,#出错回调函数
                          meta={"proxy":url,#代理服务器地址
                                "dont_retry":True,#请求不重试
                                "download_timeout":10,#超时时间
                                "item":item},
                          dont_filter=True#不过滤重复请求
                          )
        if self.current_page <= 5:#爬取5页代理信息
            #获取下一页url
            next_url = response.xpath("//a[@class='next_page']/@href").extract()[0]
            next_url = response.urljoin(next_url)
            self.current_page+=1
            yield Request(next_url)

    # 测试网站的数据解析
    def test_parse(self, response):
        yield response.meta["item"]
    #请求失败的回调函数
    def error_back(self,failure):
        #打印错误日志信息
        self.logger.error(repr(failure))
        #细化出错原因
        if failure.check(DNSLookupError):# DNS出错
            # 获取request
            request = failure.request
            #输出错误日志信息
            self.logger.error('DNSLookupError on %s', request.url)
        elif failure.check(TimeoutError, TCPTimedOutError):#超时出错
            # 获取request
            request = failure.request
            #输出错误日志信息
            self.logger.error('TimeoutError on %s', request.url)
  1. 使用Pipeline实现数据持久化
import redis  
class XiciProxyPipeline(object):
    # Spider开启时,获取数据库配置信息,连接redis数据库服务器
    def open_spider(self, spider):
        if spider.name == "xici":
            # 获取配置文件中redis配置信息
            host = spider.settings.get("REDIS_HOST")  # 主机地址
            port = spider.settings.get("REDIS_PORT",)  # 端口
            db_index = spider.settings.get("REDIS_DB_INDEX")  # 索引
            db_psd = spider.settings.get("REDIS_PASSWORD")  # 密码
            # 连接redis,得到一个连接对象
            self.db_conn = redis.StrictRedis(host=host, port=port, db=db_index,
                                             password=db_psd, decode_responses=True)
            self.db_conn.delete("ip")

    # 将数据存储于redis数据库
    def process_item(self, item, spider):
        if spider.name == "xici":
            # 将item转换为字典类型
            item_dict = dict(item)
            # 将item_dict保存于key为ip的集合中
            self.db_conn.sadd("ip", item_dict["url"])
        return item
  1. settings.py中配置项
# 1 设置robots协议:ROBOTSTXT_OBEY(False为不遵守协议
ROBOTSTXT_OBEY = False
# 2 设置用户代理:USER_AGENT
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0;Win64; x64) " \
             "AppleWebKit/537.36 (KHTML, like Gecko) " \
             "Chrome/68.0.3440.106 Safari/537.36"
# 3 启用管道:XiciProxyPipeline
ITEM_PIPELINES = {
    'xici_proxy.pipelines.XiciProxyPipeline': 300,
}

# 4 设置Redis数据库地址、端口、索引及密码信息
REDIS_HOST = "localhost"  # 主机地址
REDIS_PORT = 6379  # 端口
REDIS_DB_INDEX = 0  # 索引
REDIS_PASSWORD = ""  # 密码

实现随机代理

将免费可用的代理服务器信息保存到Redis数据库后,所有的Scrapy爬虫项目就可以使用它们了。需要注意的是,一个爬虫项目的所有请求不能委托给固定的一个代理服务器,因为目标网站依然会监测到同一IP频繁访问的异常现象。比较好的做法是每次请求时,随机指定一个代理服务器,将请求分散到多个代理服务器中。

proxy = self.db_conn.srandmember("ip")  # 随机获取一个代理url
print("随机代理URL:", proxy)
# 根据URL生成Request,使用yield返回给引擎
yield Request(next_url,#目标URL
             callback=self.qidian_parse,  # 回调函数
             errback=self.error_back,  # 异常时调用的函数
             meta={"proxy": proxy,  # 代理服务器URL
                   "download_timeout": 10  # 超时时间
                   }
             )

阅读资料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章