zoukankan      html  css  js  c++  java
  • 爬虫框架之Scrapy(二)

    递归解析

    糗事百科递归解析

    在前面的例子里只是爬取了糗事百科热门的第一个页面,但是当我们需要爬取更多的页面时,需要对每个页面的url依次发起请求,然后通过解析的方法进行作者和标题的解析。

    我们可以构建一个url列表,放进去所有页面的url,但是这样是不推荐的。

    我们也可以通过requests的方法来手动解析,然后来使用递归的思想来编写程序。

    比如:

    糗事百科首页的页码

    # -*- coding: utf-8 -*-
    import scrapy
    from happy.items import HappyItem
    
    class QiushiSpider(scrapy.Spider):
        name = 'qiushi'
        allowed_domains = ['www.qiushibaike.com']
        start_urls = ['http://www.qiushibaike.com/']
        # 爬取多个页面
        pageNum = 1
        url = 'https://www.qiushibaike.com/8hr/page/%d/'
    
        def parse(self, response):
            # xpath为response中的方法
            odiv = response.xpath('//div[@class="recommend-article"]//li')
            for li in odiv:
                # 连续的li里面混有广告,屏蔽掉广告
                try:
                    title = li.xpath('.//div[@class="recmd-right"]/a/text()')[0].extract()
                    author = li.xpath('.//span[@class="recmd-name"]/text()')[0].extract()
                except:
                    continue
                # 提供item到管道文件
                item = HappyItem()
                item['title'] = title
                item['author'] = author
                yield item
            # 爬取所有页码数据
            if self.pageNum <= 13:
                self.pageNum += 1
                url = self.url%(self.pageNum)
                # 递归爬取数据 发起URL请后,将得到的数据进行使用parse解析,所以要使用回调函数callback
                yield scrapy.Request(url=url, callback=self.parse)

    得到结果:

    post请求发送

    Scrapy不用手动对起始列表中的url发送请求,是因为爬虫文件中爬虫类继承了Spider父类中start_requests(self)方法,该方法就可以对初始列表中的url发起请求。

      def start_requests(self):
            for u in self.start_urls:
               yield scrapy.Request(url=u,callback=self.parse)

    但是该方法默认实现的是get请求,如果需要发起post请求,则需要子类重写该方法

    def start_requests(self):
            #请求的url
            post_url = 'http://fanyi.baidu.com/sug'
            # post请求参数
            formdata = {
                'kw': 'wolf',
            }
            # 发送post请求
            yield scrapy.FormRequest(url=post_url, formdata=formdata, callback=self.parse)

    Scrapy的日志等级

    在使用Scrapy crawl spidername 运行程序时,在终端里打印输出的就是Scrapy的日志。

    一般的日志种类:

      ERROR:错误

      WARNING:警告

      INFO:一般的信息

      DEBUG:调试的信息

    设置日志的指定输出:

    在settings.py配置文件中加入:

      LOG_LEVEL = 日志的指定种类

      LOG_FILE = ‘log.txt’ 表示将日志写入指定文件

    请求传参

    有时候我们爬取的数据不在同一个页面,比如在这个页面的下级页面,这时候我们就需要用法请求传参。

    例子:https://www.cnblogs.com/xiaozx/p/10744604.html 爬取网易新闻的国内版 srapy

    提高scrapy爬取效率的技巧

    1 增加并发:

    默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。

    2  降低日志级别:

    在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’.

    3 禁止cookie

    如果不是真的需要cookie,则在scrapy爬取数据时可以进制cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False.

    4 禁止重试

    对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False.

    5 减少下载超时

    如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s.

    示例

    爬取校花网校花图片 www.521609.com

    # -*- coding: utf-8 -*-
    import scrapy
    from xiaohua.items import XiaohuaItem
    
    class XiahuaSpider(scrapy.Spider):
    
        name = 'xiaohua'
        allowed_domains = ['www.521609.com']
        start_urls = ['http://www.521609.com/daxuemeinv/']
    
        pageNum = 1
        url = 'http://www.521609.com/daxuemeinv/list8%d.html'
    
        def parse(self, response):
            li_list = response.xpath('//div[@class="index_img list_center"]/ul/li')
            for li in li_list:
                school = li.xpath('./a/img/@alt').extract_first()
                img_url = li.xpath('./a/img/@src').extract_first()
    
                item = XiaohuaItem()
                item['school'] = school
                item['img_url'] = 'http://www.521609.com' + img_url
    
                yield item
    
            if self.pageNum < 10:
                self.pageNum += 1
                url = format(self.url % self.pageNum)
                #print(url)
                yield scrapy.Request(url=url,callback=self.parse)

    pipelines.py

    import json
    import os
    import urllib.request
    class XiaohuaPipeline(object):
        def __init__(self):
            self.fp = None
    
        def open_spider(self,spider):
            print('开始爬虫')
            self.fp = open('./xiaohua.txt','w')
    
        def download_img(self,item):
            url = item['img_url']
            fileName = item['school']+'.jpg'
            if not os.path.exists('./xiaohualib'):
                os.mkdir('./xiaohualib')
            filepath = os.path.join('./xiaohualib',fileName)
            urllib.request.urlretrieve(url,filepath)
            print(fileName+"下载成功")
    
        def process_item(self, item, spider):
            obj = dict(item)
            json_str = json.dumps(obj,ensure_ascii=False)
            self.fp.write(json_str+'
    ')
    
            #下载图片
            self.download_img(item)
            return item
    
        def close_spider(self,spider):
            print('结束爬虫')
            self.fp.close()

    重点是settings.py 设置有利于爬虫下载的方式

    USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
    
    # Obey robots.txt rules
    ROBOTSTXT_OBEY = False
    
    # Configure maximum concurrent requests performed by Scrapy (default: 16)
    CONCURRENT_REQUESTS = 100
    COOKIES_ENABLED = False
    LOG_LEVEL = 'ERROR'
    RETRY_ENABLED = False
    DOWNLOAD_TIMEOUT = 3
    # Configure a delay for requests for the same website (default: 0)
    # See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
    # See also autothrottle settings and docs
    # The download delay setting will honor only one of:
    #CONCURRENT_REQUESTS_PER_DOMAIN = 16
    #CONCURRENT_REQUESTS_PER_IP = 16
    DOWNLOAD_DELAY = 3

     UA池和代理池

    下载中间件

    下载中间件,是位于引擎和下载器之间的一组层组件。

    作用:

    1 引擎将请求传递给下载器的过程中,下载中间件可以对请求进行一些列的处理,比如设置user-agent,设置代理等。

    2 在下载器完成将response传递给引擎中,下载中间件可以对响应进行一些列处理,比如进行gzip解压等。

    我们使用下载中间件,一般会对请求随机的设置user-agent,设置随机的代理,目的在于应对网站的反爬策略。

    ua池

    尽可能多将scrapy工程的请求伪装成不同类型的浏览器身份。

    流程:

    1 在下载中间件中拦截请求

    2 将拦截到的请求信息中的UA进行篡改

    3 在配置文件中开启下载中间件

    import random
    from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware
    
    # 单独给ua池一个下载中间件的一个类
    class RandomUserAgent(UserAgentMiddleware):
    
        def process_request(self, request, spider):
            user_agent_list = [
                "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
                "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
                "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
                "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
                "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
                "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
                "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
                "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
                "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
                "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
                "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
                "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
                "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
                "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
                "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
                "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
                "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
                "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
                "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
                "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
                "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
                "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
                "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
                "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
                "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
                "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
                "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
            ]
            # 随机挑选一个user-agent
            ua = random.choice(user_agent_list)
            # 将他写入到当前拦截到的request请求中
            request.headers.setdefault('User-Agent', ua)

    settings.py

    DOWNLOADER_MIDDLEWARES = {
       'happy2.middlewares.RandomUserAgentMiddleware': 400,
    }
    代理池

    尽可能多将scrapy工程的请求设置成不同的ip访问。

    流程:

    1 在下载中间件中拦截请求

    2 将拦截到的请求ip改成某一代理ip

    3 在配置文件中开启下载中间件

    # 还是单独写一个类
    class ProxyMiddleware(object):
    
        def process_request(self, request, spider):
            # http型的可用ip
            PROXY_http = [
                '153.180.102.104:80',
                '195.208.131.189:56055',
            ]
            # 这是https请求类型的可用IP
            PROXY_https = [
            '120.83.49.90:9000',
            '95.189.112.214:35508',
            ]
            # 取出本次请求的路径
            h = request.url.split(':')[0]
            if h == 'http':
                ip = random.choice(PROXY_http)
                request.meta['proxy'] = 'http://' + ip
            elif h == 'https':
                ip = random.choice(PROXY_https)
                request.meta['proxy'] = 'https://' + ip

    settings.py

    DOWNLOADER_MIDDLEWARES = {
       'happy2.middlewares.ProxyMiddleware':400,
    }
  • 相关阅读:
    css 笔记
    解决谷歌浏览器中的input背景色默认是黄色
    lunbo
    操作json进行分组再组
    点击返回顶部
    关于有的手机浏览器下载APK时会成TXT
    火狐浏览器jsonshow插件
    微信web开发者工具
    浏览器UA
    click多次注册事件会导致一个事件被触发多次的解决方法
  • 原文地址:https://www.cnblogs.com/xiaozx/p/10757382.html
Copyright © 2011-2022 走看看