从零开始写Python爬虫 --- 2.5 爬虫实践:糗事百科&爬虫攻防

从零开始写Python爬虫 --- 2.5 爬虫实践:糗事百科&爬虫攻防

既然上次我们爬了大量的代理下来,我们肯定是要用的,对吧?这次我就找了个有点反爬虫能力的网站:糗事百科 ,来作为一个小例子。


目标分析:

糗事百科一直是各种段子的聚集体,既然是段子,最主要的部分就是文字,这样一想,爬点文字下载估计非常的简单。
看一下网站的模样:


简单明了,我们把作者,段子,评论和点赞数量爬下来就成。


项目创建:

还是那一套,很简单:

 # 创建项目
 scrapy startproject xiubai
 # c进入项目文件夹
 cd xiubai
 # 创建hot爬虫
 scrapy genspider hotspider qiushibaike.com

编写ITEMS:

设置我们需要的字段:

import scrapy


class XiubaiItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    
    author = scrapy.Field()
    body = scrapy.Field()
    funNum = scrapy.Field()
    comNum = scrapy.Field()
    

Spiders的编写:

这次需要爬的内容不多,爬虫本身写的也很简单:

import scrapy
from xiubai.items import XiubaiItem

class HotspiderSpider(scrapy.Spider):
    name = "hotspider"
    allowed_domains = ["qiushibaike.com"]
    start_urls = []
    # 我们爬取35页的全部热门段子
    for i in range(1,3):
        start_urls.append('http://www.qiushibaike.com/8hr/page/'+str(i)+'/')
    
    
    def parse(self, response):
        item = XiubaiItem()

        # 找到热门段子主体
        main = response.xpath('//div[@id="content-left"]/div')


        for div in main:
            #段子作者
            item['author'] =div.xpath('.//h2/text()').extract()[0]
            #段子主体: 
            item['body'] = ''.join( div.xpath('a[@class="contentHerf"]/div/span[1]/text()').extract())
            #段子footer
            item['funNum']= div.xpath('.//span[@class="stats-vote"]/i/text()').extract()[0]
            item['comNum']= div.xpath('.//span[@class="stats-comments"]/a/i/text()').extract()[0]
            yield item


编写PIPELINE:

简单的写入到本地的文本文件:

class XiubaiPipeline(object):
    def process_item(self, item, spider):

        with open("/Users/ehco/Desktop/result/qiubai.txt",'a+') as f:
            f.write('作者:{} \n{}\n点赞:{}\t评论数:{}\n\n'.format(
                item['author'], item["body"], item['funNum'], item["comNum"]))

试一下?

好了,我们来测试一下,
先在settings.py设置我们写的pipeline:

ITEM_PIPELINES = {
    'xiubai.pipelines.XiubaiPipeline': 300,
}

运行一下?

scrapy crawl hotspider


报错了!
这是当然的,?,不然我也没必要写这篇了。

可以看到 返回 503ERROR,想必是糗百的服务器拒绝我们的请求了,
如何突破呢?


爬虫攻防:

Scrapy框架在突破反爬虫技术上有十分强大的框架给我们用,
很多时候我们只需要自己改一些设定就能和网站管理员玩“猫捉老鼠”的游戏了
但是想要更多的自定义,需要我们自己写“MIddleWares:中间件”
更多的可以参考一下文档:
下载器中间件(Downloader Middleware)
下面我来介绍一些常用的方法。


突破时间间隔封锁:

很多网站对于人和爬虫的判断,基于访问的速度:爬虫可以做到1s内十次上百次的访问,人类却明显做不到。

scrapy框架只要在settings.py里设置就能改变访问的速度:

DOWNLOAD_DELAY = 1

这里代表:每1s访问一次。
如果不考虑网站管理员的反爬虫,这个值当然越小越快,
但是总是会有一个 阈值 小于这个值,你的spider就要被封锁咯~
所以具体还是得自己实际测试才能知道。


突破Cookie封锁:

虽然我们没遇到过,但是众所周知,cookies是来确定用户身份的一长串数据,如果有的网站对cookie进行简单的检验,我们简单的设置一下也能解决:

COOKIES_ENABLED = False

这里我们就禁用了cookie


突破user-agent封锁:

这种方式,本质上就是对headers进行判断,在前面的bs4爬虫攻防里我已经具体介绍过了,我就不多说了。

从这里开始就需要我们自己写中间件,才能够完成任务了。

首先,在与settings.py的同级目录建立文件夹:

middlewares


接着,在文件夹下创建:

__init__.py 


这样可以让Python认为这个文件是一个可以导入的包
然后,我们开始写中间件:

customUserAgent.py
'''
自定义scrapy框架的
user-agent头
从一个被良好维护的user-agent列表里
随机筛选合适的user-agent 
防止封锁
'''
from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware

import random



#一个不容易被封锁的user-agent列表
agents = ['Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;',
              'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv,2.0.1) Gecko/20100101 Firefox/4.0.1',
              'Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11',
              'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
              'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)']

class RandomUserAgent(UserAgentMiddleware):
    def process_request(self,request,spider):
        '''
        定义下载中间件,
        必须要写这个函数,
        这是scrapy数据流转的一个环节
        具体可以看文档:
        http://scrapy-chs.readthedocs.io/zh_CN/0.24/topics/downloader-middleware.html
        '''
        ua = random.choice(agents)
        request.headers.setdefault('User-agent',ua)

这样就能每次发起访问请求的时候,随机选择一个user-agent了。

当然,我们需要在settings.py里激活我们的下载中间件:
注意,需要scrapy自身的user-agent中间件关闭!

DOWNLOADER_MIDDLEWARES = {
    'xiubai.middlewares.coustomUserAgent.RandomUserAgent': 20,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware':None
}

突破IP封锁:

终于说道关键的部分了,不说这里,上一张我们弄出来的这么多代理都白费啦!
原理我也不多说了,自然是一个ip的频繁访问,导致被封ip了。
解决办法也很简单,写一个代理中间件,让他每次访问的时候都换一个ip就成:

首先,我们要建立一个单独文件来存储可以用的代理:proxy.py

'''
通过爬取可用的免费代理,
进行测试可用度之后
筛选获得的代理

将静态的资源文件写在功能文件之外
方便及时更新维护。
'''
# 可以自行添加更多代理
proxies = [
    '89.36.215.72:1189',
    '94.177.203.123:1189',
    '110.73.11.227:8123',
    '180.183.176.189:8080',
    '109.62.247.81:8080',
]

这里单独写一个文件,是为了方便维护,毕竟免费的代理动不动就不能用啦!user-agent就不会,我们可以硬编码在功能里

接着,创建我们的中间件:coustomProxy.py

'''
从本地文件proxy.py中
读取可以用的代理列表
并从中随机选择一个代理
供给spider使用
'''
from xiubai.middlewares.proxy import proxies
import random

class RandomProxy(object):
    def process_request(self,request,spider):
        # 从文件中随机选择一个代理
        proxy = random.choice(proxies)

        request.meta['proxy'] = 'http://{}'.format(proxy)

最后,设置settings.py,注意,这里和上面不一样,不能关闭scrapy本身的代理中间件,只需要让自己写的在官方之前执行就成。

DOWNLOADER_MIDDLEWARES = {
    'xiubai.middlewares.coustomProxy.RandomProxy':10,
    'xiubai.middlewares.coustomUserAgent.RandomUserAgent': 20,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware':None,
    'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware':100,
}

爬取结果:

折腾了这么多,应该能顺利的爬取糗事百科了吧?
结果是肯定的:



可以看到,糗事百科35页的所有段子都被我们爬下来了,
好了,快拿去逗女票吧!

到这里,基本就把scrapy框架的基本内容介绍完了,更加深入的使用和开发,
就要靠你们自己在实践中成长了!


每天的学习记录都会 同步更新到:
微信公众号: findyourownway

知乎专栏:从零开始写Python爬虫 - 知乎专栏

blog : www.ehcoblog.ml

Github: Ehco1996/Python-crawler

编辑于 2017-05-21

文章被以下专栏收录