常见爬虫技术
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数据库中,提供给后续的爬虫项目使用,主要代码如下:
- 创建Scrapy项目
scrapy startproject xici_proxy
- 使用Item封装数据
class XiciItem(scrapy.Item):
url = scrapy.Field()#url
cryptonym = scrapy.Field()#是否高匿名
- 创建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)
- 使用Pipeline实现数据持久化
import redis
class XiciProxyPipeline(object):
# Spider开启时,获取数据库配置信息,连接redis数据库服务器
def open_spider(self, spider):
if spider.name == "xici":
# 获取配置文件中redis配置信息
host