上回说到设置随机 User-Agent ,这次来一个随机代理 ip 的设置。
代理ip
在爬虫中,为了避免网站将我们的 ip 封掉,我们就要使用代理 ip 。虽然说代理 ip 没有原装的好,但是有些时候还是要使用代理ip 来获取数据的。
原理
随机代理 ip 简单来说就是爬取网上的免费代理ip ,然后存入数据库,在数据库中随机拿到一个代理ip来用。具体结合到 scrapy 中,我们就要在 Middleware.py 文件中修改,原理跟上文代理的设置一样,不懂的可以去上篇文章看一下。
实战演练
在上文的 scrapydownloadertest 项目基础上操作。
在 spiders 的同级目录下,新建一个 Python Package 。还要新建python文件,具体名称参考图片。目录结构如下:
从西刺网上获取免费的代理ip 保存到数据库中。
import requests
from scrapy.selector import Selector
import pymysql
# 连接数据库
conn = pymysql.connect(host='',user='',password='',db='proxy',charset='utf8')
cursor = conn.cursor()
# 爬取西刺网站上的代理ip
def crawler_ips():
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
}
for i in range(100):
url = 'http://www.xicidaili.com/nn/'.format(i)
r = requests.get(url,headers=headers)
selector = Selector(text=r.text)
all_ips = selector.css('#ip_list tr')
ip_list = []
for tr in all_ips[1:]: # 这里由于第一个是标题,舍弃
speed_str = tr.css('.bar::attr(title)').extract_first()[0]
if speed_str:
speed = float(speed_str.split('秒')[0])
all_texts = tr.css('td::text').extract()
ip = all_texts[0]
port = all_texts[1]
proxy_type = all_texts[5]
ip_list.append((ip,port,proxy_type,speed))
for ip_info in ip_list:
# 插入数据
cursor.execute(
"insert proxy_ip(ip,port,speed,proxy_type) VALUES('{0}','{1}',{2},'HTTP')".format(
ip_info[0],ip_info[1],ip_info[3]
)
)
conn.commit()
数据库我用的是MySQL,在 navivat for mysql 中连接 MySQL ,新建一个数据库,名称随意,这里就叫做 proxy 吧,在代码中把连接数据库的代码补充完整。
新建一个数据表 proxy_ip 并写下如下字段:
这里我已经新建好了,并且运行的代码。
新建之后,我们运行上述代码,这时候会看到数据库中已经有了数据。接下来就是从数据库中随机获取一个代理 ip ,供我们程序使用。
class GetIP(object):
def get_random_ip(self):
# 从数据库中随机获取一个可用ip
random_sql = 'SELECT ip,port FROM proxy_ip ORDER BY RAND() LIMIT 1'
result = cursor.execute(random_sql)
for ip_info in cursor.fetchall():
ip = ip_info[0]
port = ip_info[1]
judge_re = self.judge_ip(ip,port)
if judge_re:
return 'http://{0}:{1}'.format(ip,port)
else:
return self.get_random_ip()
def judge_ip(self,ip,port):
# 判断ip 是否可用
http_url = 'http://www.baidu.com'
proxy_url = 'http://{0}:{1}'.format(ip,port)
try:
proxy_dict = {
'http':proxy_url
}
r = requests.get(http_url,proxies=proxy_dict)
print('effective ip')
return True
except Exception as e:
print('invalid ip and port 无效')
self.delete_ip(ip)
return False
else:
code = r.status_code
if code >= 200 and code < 300:
print('effective ip')
return True
else:
print('invalid ip and port 无效')
self_delete_ip(ip)
return False
def delete_ip(self,ip):
# 删除无效的ip
delete_sql = "delete from proxy_ip where ip = '{0}'".format(ip)
cursor.execute(delete_sql)
conn.commit()
if __name__ == '__main__':
get_ip = GetIP()
print(get_ip.get_random_ip())
这里从数据库中随机获取一个代理ip,并测试代理ip是否可用,不可用的删掉。这里有个小技巧,如果你爬取的是其他网站,就把百度的链接换成你要爬取的网站,这样会提高代理ip的可用性。
运行此代码会随机获取一个可用的ip代理。
接下来在 Middleware.py 中把代理ip 加上:
class RandomProxyMiddleware(object):
# 动态设置ip代理
def process_request(self, request, spider):
get_ip = GetIP()
request.meta['proxy'] = get_ip.get_random_ip()
在 settings.py 文件中配置一下:
DOWNLOADER_MIDDLEWARES = {
'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,
'scrapydownloadertest.middlewares.RandomProxyMiddleware': 544,
}
运行spider文件,就可以在输出的日志上面看到代理ip在使用了。
2018-09-16 16:57:46 [urllib3.connectionpool] DEBUG: http://123.249.88.153:9000 "GET http://www.baidu.com/ HTTP/1.1" 200 None
effective ip
总结
使用这样的方法可以用到代理 ip 从而有效的隐藏了自己的真实 ip 。免费从网上获取的代理通常来说没有付费得到的 ip 稳定性好。如果有条件,还是建议自费获取一些代理 ip。
自己的代理还是最好用的,我们在爬取的时候可以放慢速度,增加延迟等方式以避免自己的 ip 被封。同时不要让网站的压力太大。这样才能共存。