文章由本人博客首发转载请注明链接: http://qinfei.glrsmart.com/2018/11/07/scrapy-shi-zhan-3-she-zhi-dai-li/
我们已经爬取到目标网站的数据了,简单吗? 开心吗? 恭喜你已经入门scrapy 了.虽然我也是第一次拿它做项目, 看起来我更像老鸟一点有木有.好开心....
但是问题慢慢来了,那句话怎么说来着,魔高一尺道高一丈还是魔高一尺道高一丈? 人家辛辛苦苦做网站,数据为王好的吧,一定辛辛苦苦做一些东西防止你爬取.所以我们也要想办法拿到数据,毕竟我们才是吸血鬼,而他们才是正统天王.
每一个正常的网站都设置有 robots.txt 文件,用来告诉搜索引擎哪些地方你可以抓取,哪些地方禁止您抓取, 大家都是文明人,规则定起来就懂了.可我们不一样啊,我们是吸血鬼,压根不是一个种族好吗? 你这规则我可以选择忽略毕竟不是法律. settings.py
设置:
ROBOTSTXT_OBEY = False
一句话,我们的蜘蛛就不管规则了,变成了野蛮人.
有的网站通过判断发起请求的 headers 所以我们要伪装成浏览器来访问,你网站不可能拒绝正常的浏览器用户吧, settings.py
设置:
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36"
我们的蜘蛛挺聪明,伪装成了 Linux 上面的 chrome, 这样应该可以畅行无阻了吧(这里也暴露了,我上班主力系统是 ubuntu 18.04).
或者 编写中间件, 随机挑选 headers: https://www.jianshu.com/p/bbf0e73e24ab
什么?要登录才可以访问?
我爬取的网站比较菜,这个我确实没有做,不过我们现在手撸一个,毕竟这对我们来说不算什么难事:
TODO: 模拟登陆
TODO: cookie
DOWNLOAD_DELAY = 0.5
# The download delay setting will honor only one of:
CONCURRENT_REQUESTS_PER_DOMAIN = 2
一个 ip 爬取了这么多页面,有监控的网站应该会发觉,毕竟如果是小网站的话CPU啊内存啊,都会不正常的升高, 然后像我这样有经验的站长(不要脸,自己网站被爬了都不知道)会检查日志分析 nginx 访问前几名的 ip,所以要换着 ip 来访问, 多嘴问一下,不忘初心,我们做的是啥? 爬虫啊,怎么能手工搜集代理呢?再多一点工作量,每次启动自己爬代理服务器不是很好嘛?这里走点弯路,带入点知识点,毕竟这是我的笔记呢,我也要记录详细点不是?增加难度!
先找一个提供免费代理服务器的网站http://cn-proxy.com
, 别急,这个网站提供的代理数量不多,但是质量不低啊, 有个问题,国内不知道是不是我的垃圾移动线路的问题还是那该死的 GFW ,我不用代理不能访问提供代理的网站! 有没有很绕.那怎么办?我电脑上有ss啊(不知道的自行 Google ),现在我想这样做,先不用 scrapy 自己用 requests 手撸一个蜘蛛先爬代理站,而且这个蜘蛛还要通过本地 ss 代理才能爬到数据.有没有很傻?做实验嘛,多动手才掌握的多啦!
直接下手:
import requests
import re
from lxml import etree
if __name__ == '__main__':
proxies = {
"http": "socks5://127.0.0.1:1080",
}
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/70.0.3538.77 Safari/537.36"}
r = requests.get("http://cn-proxy.com", headers=headers, proxies=proxies).content.decode('utf-8')
dom_tree = etree.HTML(r)
info_lists = dom_tree.xpath('//td/text()')
with open("./proxy_servers.txt", "wb") as f:
for index, o in enumerate(info_lists):
if re.match("((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))", o):
f.write(("http://" + o + ":" + info_lists[index + 1] + "\n").encode())
说了这么多,下手也不是很多嘛.
然后就可以在 proxy_servers.txt
看到我们写文章这天的免费代理服务器啦:
http://39.137.69.10:8080
---此处省略n台---
下面可以使用这些代理服务器啦! 好开心...
在 middleware.py 中添加如下代码,其中含义注释应该很清楚,建议想跟进一步的童鞋参考官方文档,这里不多嘴啦.
class ProxyMiddleWare(object):
"""docstring for ProxyMiddleWare"""
def process_request(self, request, spider):
'''
对request对象加上proxy
:param request:
:param spider:
:return:
'''
proxy = self.get_random_proxy()
print("This is request ip:" + proxy)
request.meta['proxy'] = proxy
def process_response(self, request, response, spider):
'''
对返回的response处理
process_request() 必须返回以下之一: 返回一个 Response 对象、 返回一个 Request 对象或raise一个 IgnoreRequest 异常。
如果其返回一个 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。
如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。
如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。
:param request:
:param response:
:param spider:
:return:
'''
# 如果返回的response状态不是200,重新生成当前request对象
if response.status != 200:
proxy = self.get_random_proxy()
print("This is response ip:" + proxy)
# 对当前 request 加上代理
request.meta['proxy'] = proxy
return request
return response
@staticmethod
def get_random_proxy():
'''
随机从文件中读取proxy
:return:
'''
while True:
with open('proxy_servers.txt', 'r') as f:
proxies = f.readlines()
if proxies:
break
else:
time.sleep(1)
proxy = random.choice(proxies).strip()
return proxy
这里的作用是随机挑一个代理服务器进行访问,如果跑不通就随机再换一个再次访问,其中机制由 scrapy 调度,我们不用操心.
我们 print 的东西是为了证明我们真的使用了代理访问,不要被坑啊...哈哈
在 settings.py 里面开启代理:
DOWNLOADER_MIDDLEWARES = {
'JiYS.middlewares.ProxyMiddleWare': 543,
}
后面的数字是优先级,不管它,随便写啦...
然后我们就可以开心的抓取数据而不用担心被ban啦.