欢迎关注本人简书共同交流运维/监控/Linux/Python/Django/Flask...: https://www.jianshu.com/u/788c815ee098

我们已经爬取到目标网站的数据了,简单吗? 开心吗? 恭喜你已经入门scrapy 了.虽然我也是第一次拿它做项目, 看起来我更像老鸟一点有木有.好开心....

但是问题慢慢来了,那句话怎么说来着,魔高一尺道高一丈还是魔高一尺道高一丈? 人家辛辛苦苦做网站,数据为王好的吧,一定辛辛苦苦做一些东西防止你爬取.所以我们也要想办法拿到数据,毕竟我们才是吸血鬼,而他们才是正统天王.

不遵守 robots.txt 规则

每一个正常的网站都设置有 robots.txt 文件,用来告诉搜索引擎哪些地方你可以抓取,哪些地方禁止您抓取, 大家都是文明人,规则定起来就懂了.可我们不一样啊,我们是吸血鬼,压根不是一个种族好吗? 你这规则我可以选择忽略毕竟不是法律. settings.py 设置:

ROBOTSTXT_OBEY = False

一句话,我们的蜘蛛就不管规则了,变成了野蛮人.

设置 headers

有的网站通过判断发起请求的 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).

模拟登录,伪装成用户

什么?要登录才可以访问? 我爬取的网站比较菜,这个我确实没有做,不过我们现在手撸一个,毕竟这对我们来说不算什么难事:

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

在 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啦.