• Scrapy 框架进阶笔记


    上一篇简单了解了scrapy各个模块的功能:Scrapy框架初探 -- Dapianzi卡夫卡
    在这篇通过一些实例来深入理解 scrapy 的各个对象以及它们是怎么相互协作的

    settings.py 配置文件

    #USER_AGENT = 'cats (+http://www.yourdomain.com)'
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
    
    # Obey robots.txt rules 是否乖乖听从robot.txt
    #ROBOTSTXT_OBEY = True
    
    # Configure maximum concurrent requests performed by Scrapy (default: 16)
    #CONCURRENT_REQUESTS = 32
    
    # Configure a delay for requests for the same website (default: 0)
    # See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
    # See also autothrottle settings and docs 请求间隔时间。需要看抓取时间需求,IP资源,目标网站反爬虫策略
    DOWNLOAD_DELAY = 1
    # The download delay setting will honor only one of: 二选一,按ip还是按域名设置请求上限
    #CONCURRENT_REQUESTS_PER_DOMAIN = 16
    CONCURRENT_REQUESTS_PER_IP = 16
    
    # Disable cookies (enabled by default) 自动处理cookie,需要登录会话
    #COOKIES_ENABLED = False
    COOKIES_ENABLED = True
    
    # Disable Telnet Console (enabled by default) 
    TELNETCONSOLE_ENABLED = False
    
    # Override the default request headers: 默认的http headers
    DEFAULT_REQUEST_HEADERS = {
        'Connection': 'Keep-Alive',
        'Accept': 'text/html, application/xhtml+xml,*/*',
        'Accept-Language': 'zh-CN',
        'Accept-Encoding': 'gzip, deflate',
    }
    

    其实看注释就已经很清楚各个配置项是什么作用了,根据自己的需求,目标网站的反爬虫策略来配置就好了
    另外可以任意添加自己自定义的配置项,比如MySQL配置:

    # MYSQL CONFIG
    MYSQL_HOST = '127.0.0.1'
    MYSQL_PORT = 3307
    MYSQL_USER = 'root'
    MYSQL_PASS = ''
    MYSQL_NAME = 'pets'
    

    在项目中需要使用的时候,使用 crawler 对象。在 pipelines.py 中 的例子:

    @classmethod
    def from_crawler(cls, crawler):
        '''static function'''
        host = crawler.settings.get('MYSQL_HOST')
        ...
    

    pipelines.py 管道处理

    管道处理,根据字面意思当然是可以定义不同的逻辑,按顺序对 Items 进行处理。

    管道顺序

    管道的顺序需要在 settings.py 定义

     ITEM_PIPELINES = {
        # enable image pipeline
        'cats.pipelines.FilterPipeline': 1,
        'cats.pipelines.ImgsPipeline': 100,
        'cats.pipelines.CatsPipeline': 300,
    }   
    

    这里定义了3个管道,分别是 过滤去重 -> 下载图片 -> 保存到数据库 。后面的数字代表管道权重

    @classmethod[from_crawler] 初始化管道

    这个是从官方文档看到的,为了从 settings.py 读取相关配置,调用类方法来初始化 管道处理器。在使用数据库的时候很方便:

    db = None
    cursor = None
    
    def __init__(self, conf):
        try:
            self.db = pymysql.connect(conf['host'], conf['user'], conf['pass'], conf['name'], port=conf['port'], charset='utf8')
        except pymysql.OperationalError as e:
            print ("Mysql Operation Error[%d]: %s" % tuple(e))
            exit(0)
        self.cursor = self.db.cursor()
    
    def __del__(self):
        self.db.close()
    
    @classmethod
    def from_crawler(cls, crawler):
        '''static function'''
        return cls({
            'host' : crawler.settings.get('MYSQL_HOST'),
            'port' : crawler.settings.get('MYSQL_PORT'),
            'name' : crawler.settings.get('MYSQL_NAME'),
            'user' : crawler.settings.get('MYSQL_USER'),
            'pass' : crawler.settings.get('MYSQL_PASS')
        })
    

    process_item() 管道处理方法

    这里是对 Item 进行处理的主要方法,数据库落地或者 DropItem() .处理完成记得 return item

    sql = "SELECT id FROM cat_imgs WHERE img_hash=%s"
    self.cursor.execute(sql, (item['img_hash'], ))
    if self.cursor.fetchone():
        spider.log('HASH exists.')
        raise DropItem("HASH exists.")
    else:
        return item
    

    ImagesPipeline 图片下载

    【官方文档】Downloading and processing files and images

    • 新建一个类继承 ImagesPipeline。因为不是普通的 pipeline,因此__init__(),process_item(),spider_open(),from_crawler() 等方法可能不适用

    • get_media_requests(self, item, info) 下载图片方法第三个参数info存储了spider的信息,可以直接获取spider的属性:

        def get_media_request(self, item, info):
            name = info.spider.name
            ref = info.spider.ref
            return Request(item['img_src'], headers={"Referer": ref})
      
    • item_completed(self, results, item, info) 图片下载完成后的处理。results参数始终是一个 list, 跟你定义的 Item 字段没有关系

        def item_completed(self, results, item, info):
      
            for ok,x in results:
                if not ok:
                    raise DropItem("Item contains no files")
                item['img_src'] = x['path']
                # 我的 item 结构中 只有一个图片资源
                return item
      

    Image 图片下载配置

    IMAGES_STORE = r'C:UsersadminPycharmProjectscatscatsimgs'
    # image thumb size
    IMAGES_THUMBS = {
        # 缩略图最大宽高
        'small': (90, 90),
    }
    # image filter size,宽高小于这个尺寸不下载
    IMAGES_MIN_HEIGHT = 120
    IMAGES_MIN_WIDTH = 120
    # redirect download url,有些图片链接包含重定向
    MEDIA_ALLOW_REDIRECTS = True
    

    spiders 爬虫逻辑

    爬虫执行的命令scrapy crawl xxx,这里的爬虫名字就是xxx_spider.py 里定义的 name 属性。 name 不需要要跟文件名一致

    启动爬虫并传参

    def __init__(self, page_range='1-10', *args, **kwargs):
        super(xxxSpider, self).__init__(*args, **kwargs)
        self.start, self.end = page_range.split('-')
    

    启动命令大概是 scrapy crawl xxx -a page_range=1-5
    传多个参数的方法暂时没有找到很好的解决方法,因此利用参数序列化成为一个参数的方式。不知道是不是因为window-cmd的关系。

    yieldreturn

    刚学python,因此对 yield 一开始没有概念。个人觉得在爬虫这个场景下,可以这么理解:

    • Return : 立即返回一个 request 请求(或返回一个 item)
    • Yield : 注册一个 request 请求(或一个 item),供后面的逻辑管道处理
      因此需要返回多个请求(item)的用yield,相当于一个请求列表;确定只有一个请求,可以用return(yield 应该也没问题)

    Xpathcss

    • .extract() 获取一个目标列表,.extract_first() 获取列表第一个元素

    • 获取标签内容:.css('div::text'),.xpath('//div/text()')

    • 获取标签属性:.css('a::attr(href)'),.xpath('//a/@href')

    • xpath 比 css 要更快一些,css 选择器会被解释为 xpath 选择器

    • 注释内容无法通过 text() 获取,尝试通过获取整个元素再去掉 然后重新构造选择器,比如 抓取百度贴吧的时候它的源码是这样的:
        <html>....</html>
        <code id="..." class="..."><!--
            <ul class="...">
                <li>......</li>
            </ul>
        --></code>
      

      看起来像是用了某种前端框架,真实 html 内容被放在了 标签 并注释掉了。这时直接使用 xpath 是无法获取 code 里的 html 内容的

        thread_list = res.xpath('//html/code[@id="pagelet_html_frs-list/pagelet/thread_list"]').extract_first()
        # 去掉注释
        exp = re.compile(r'<code.*><!--(.*)--></code>', re.I|re.M|re.S)
        thread_list = re.sub(exp, r'1', thread_list)
        # 重新加载内容
        topic = Selector(text=thread_list).xpath('//a[@class="j_th_tit "]')
      

    其他

    • scrapy 为我们造好了绝大部分轮子,我们需要做的就是熟悉它。
    • 需要自己开发的功能,主要是研究如何从目标站点的html结构中获取自己需要的内容,以及对应的存储逻辑
    • 其他一些开发,可能是遇到特殊的反爬虫策略,也可能是对 scrapy 的部分功能还不够熟悉
  • 相关阅读:
    第一课:js命名空间的介绍,js对象的扩展以及js数组化
    浏览器缓存机制-社招必问知识
    2013年前端校园招聘经历
    GBDT(MART) 迭代决策树简介
    coursera 公开课 文本挖掘和分析(text mining and analytics) week 1 笔记
    Predicting purchase behavior from social media-www2013
    Recommending branded products from social media -RecSys 2013-20160422
    2016年数据挖掘,机器学习可投会议
    java 中遍历hashmap 和hashset 的方法
    NLPIR分词工具的使用(java环境下)
  • 原文地址:https://www.cnblogs.com/dapianzi/p/8085445.html
Copyright © 2020-2023  润新知